summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.dir-locals.el2
-rw-r--r--.gitignore45
-rw-r--r--INSTALL6
-rw-r--r--Makefile.in62
-rw-r--r--README5
-rwxr-xr-xadmin/merge-gnulib11
-rwxr-xr-xautogen.sh6
-rw-r--r--build-aux/ndk-build-helper-1.mk112
-rw-r--r--build-aux/ndk-build-helper-2.mk105
-rw-r--r--build-aux/ndk-build-helper-3.mk28
-rw-r--r--build-aux/ndk-build-helper-4.mk39
-rw-r--r--build-aux/ndk-build-helper.mk81
-rw-r--r--build-aux/ndk-module-extract.awk88
-rw-r--r--configure.ac1484
-rw-r--r--cross/Makefile.in190
-rw-r--r--cross/README5
-rw-r--r--cross/langinfo.h20
-rw-r--r--cross/ndk-build/Makefile.in144
-rw-r--r--cross/ndk-build/README353
-rw-r--r--cross/ndk-build/ndk-build-executable.mk22
-rw-r--r--cross/ndk-build/ndk-build-shared-library.mk171
-rw-r--r--cross/ndk-build/ndk-build-static-library.mk142
-rw-r--r--cross/ndk-build/ndk-build.mk.in68
-rw-r--r--cross/ndk-build/ndk-clear-vars.mk57
-rw-r--r--cross/ndk-build/ndk-prebuilt-shared-library.mk24
-rw-r--r--cross/ndk-build/ndk-prebuilt-static-library.mk24
-rw-r--r--cross/ndk-build/ndk-resolve.mk162
-rw-r--r--cross/verbose.mk.android55
-rw-r--r--doc/emacs/Makefile.in2
-rw-r--r--doc/emacs/android.texi647
-rw-r--r--doc/emacs/emacs.texi19
-rw-r--r--doc/emacs/input.texi146
-rw-r--r--doc/lispref/commands.texi135
-rw-r--r--doc/lispref/display.texi7
-rw-r--r--doc/lispref/elisp.texi1
-rw-r--r--doc/lispref/frames.texi571
-rw-r--r--doc/lispref/os.texi3
-rw-r--r--doc/lispref/processes.texi18
-rw-r--r--etc/DEBUG33
-rw-r--r--etc/MACHINES12
-rw-r--r--etc/NEWS36
-rw-r--r--etc/PROBLEMS50
-rw-r--r--exec/Makefile.in140
-rw-r--r--exec/README3
-rw-r--r--exec/config-mips.m4.in42
-rwxr-xr-xexec/config.guess1768
-rw-r--r--exec/config.h.in358
-rwxr-xr-xexec/config.sub1890
-rw-r--r--exec/configure.ac537
-rw-r--r--exec/deps.mk21
-rw-r--r--exec/exec.c1202
-rw-r--r--exec/exec.h201
-rw-r--r--exec/exec1.c94
-rwxr-xr-xexec/install-sh541
-rw-r--r--exec/loader-aarch64.s187
-rw-r--r--exec/loader-armeabi.s204
-rw-r--r--exec/loader-mips64el.s234
-rw-r--r--exec/loader-mipsel.s236
-rw-r--r--exec/loader-x86.s203
-rw-r--r--exec/loader-x86_64.s195
-rw-r--r--exec/mipsel-user.h43
-rw-r--r--exec/mipsfpu.c289
-rw-r--r--exec/mipsfpu.h82
-rw-r--r--exec/test.c105
-rw-r--r--exec/trace.c1420
-rw-r--r--java/AndroidManifest.xml.in201
-rw-r--r--java/INSTALL968
-rw-r--r--java/Makefile.in331
-rw-r--r--java/README908
-rwxr-xr-xjava/debug.sh364
-rw-r--r--java/emacs.keystorebin0 -> 2776 bytes
-rw-r--r--java/org/gnu/emacs/EmacsActivity.java434
-rw-r--r--java/org/gnu/emacs/EmacsApplication.java81
-rw-r--r--java/org/gnu/emacs/EmacsClipboard.java47
-rw-r--r--java/org/gnu/emacs/EmacsContextMenu.java374
-rw-r--r--java/org/gnu/emacs/EmacsCopyArea.java206
-rw-r--r--java/org/gnu/emacs/EmacsCursor.java47
-rw-r--r--java/org/gnu/emacs/EmacsDialog.java372
-rw-r--r--java/org/gnu/emacs/EmacsDocumentsProvider.java575
-rw-r--r--java/org/gnu/emacs/EmacsDrawLine.java66
-rw-r--r--java/org/gnu/emacs/EmacsDrawPoint.java31
-rw-r--r--java/org/gnu/emacs/EmacsDrawRectangle.java123
-rw-r--r--java/org/gnu/emacs/EmacsDrawable.java32
-rw-r--r--java/org/gnu/emacs/EmacsFillPolygon.java82
-rw-r--r--java/org/gnu/emacs/EmacsFillRectangle.java116
-rw-r--r--java/org/gnu/emacs/EmacsFontDriver.java173
-rw-r--r--java/org/gnu/emacs/EmacsGC.java121
-rw-r--r--java/org/gnu/emacs/EmacsHandleObject.java59
-rw-r--r--java/org/gnu/emacs/EmacsInputConnection.java348
-rw-r--r--java/org/gnu/emacs/EmacsMultitaskActivity.java29
-rw-r--r--java/org/gnu/emacs/EmacsNative.java254
-rw-r--r--java/org/gnu/emacs/EmacsNoninteractive.java203
-rw-r--r--java/org/gnu/emacs/EmacsOpenActivity.java472
-rw-r--r--java/org/gnu/emacs/EmacsPixmap.java192
-rw-r--r--java/org/gnu/emacs/EmacsPreferencesActivity.java141
-rw-r--r--java/org/gnu/emacs/EmacsSdk11Clipboard.java290
-rw-r--r--java/org/gnu/emacs/EmacsSdk23FontDriver.java114
-rw-r--r--java/org/gnu/emacs/EmacsSdk7FontDriver.java538
-rw-r--r--java/org/gnu/emacs/EmacsSdk8Clipboard.java147
-rw-r--r--java/org/gnu/emacs/EmacsService.java914
-rw-r--r--java/org/gnu/emacs/EmacsSurfaceView.java141
-rw-r--r--java/org/gnu/emacs/EmacsThread.java81
-rw-r--r--java/org/gnu/emacs/EmacsView.java708
-rw-r--r--java/org/gnu/emacs/EmacsWindow.java1300
-rw-r--r--java/org/gnu/emacs/EmacsWindowAttachmentManager.java225
-rw-r--r--java/res/drawable/emacs.pngbin0 -> 13462 bytes
-rw-r--r--java/res/values-v11/style.xml24
-rw-r--r--java/res/values-v14/style.xml25
-rw-r--r--java/res/values-v19/bool.xml22
-rw-r--r--java/res/values-v29/style.xml32
-rw-r--r--java/res/values/bool.xml22
-rw-r--r--java/res/values/style.xml26
-rw-r--r--java/res/xml/preferences.xml28
-rw-r--r--lib-src/Makefile.in21
-rw-r--r--lib-src/asset-directory-tool.c284
-rw-r--r--lib-src/emacsclient.c2
-rw-r--r--lib/Makefile.in28
-rw-r--r--lib/asnprintf.c34
-rw-r--r--lib/asprintf.c39
-rw-r--r--lib/float+.h147
-rw-r--r--lib/float.c33
-rw-r--r--lib/float.in.h194
-rw-r--r--lib/fpucw.h108
-rw-r--r--lib/frexp.c168
-rw-r--r--lib/frexpl.c35
-rw-r--r--lib/fseterr.c84
-rw-r--r--lib/fseterr.h45
-rw-r--r--lib/getdelim.c147
-rw-r--r--lib/getline.c27
-rw-r--r--lib/gnulib.mk.in902
-rw-r--r--lib/isnan.c190
-rw-r--r--lib/isnand-nolibm.h33
-rw-r--r--lib/isnand.c19
-rw-r--r--lib/isnanf-nolibm.h41
-rw-r--r--lib/isnanf.c20
-rw-r--r--lib/isnanl-nolibm.h34
-rw-r--r--lib/isnanl.c20
-rw-r--r--lib/itold.c28
-rw-r--r--lib/math.c22
-rw-r--r--lib/math.in.h2735
-rw-r--r--lib/printf-args.c183
-rw-r--r--lib/printf-args.h150
-rw-r--r--lib/printf-frexp.c190
-rw-r--r--lib/printf-frexp.h23
-rw-r--r--lib/printf-frexpl.c37
-rw-r--r--lib/printf-frexpl.h23
-rw-r--r--lib/printf-parse.c623
-rw-r--r--lib/printf-parse.h193
-rw-r--r--lib/printf.c40
-rw-r--r--lib/signbitd.c64
-rw-r--r--lib/signbitf.c64
-rw-r--r--lib/signbitl.c64
-rw-r--r--lib/size_max.h30
-rw-r--r--lib/stdalign.in.h49
-rw-r--r--lib/stpncpy.c92
-rw-r--r--lib/vasnprintf.c5741
-rw-r--r--lib/vasnprintf.h72
-rw-r--r--lib/vasprintf.c50
-rw-r--r--lib/vfprintf.c70
-rw-r--r--lib/xsize.c21
-rw-r--r--lib/xsize.h108
-rw-r--r--lisp/battery.el80
-rw-r--r--lisp/bindings.el3
-rw-r--r--lisp/button.el30
-rw-r--r--lisp/cedet/semantic/db-ebrowse.el3
-rw-r--r--lisp/comint.el3
-rw-r--r--lisp/cus-edit.el6
-rw-r--r--lisp/elec-pair.el14
-rw-r--r--lisp/electric.el1
-rw-r--r--lisp/faces.el2
-rw-r--r--lisp/files.el9
-rw-r--r--lisp/frame.el88
-rw-r--r--lisp/gnus/mail-source.el2
-rw-r--r--lisp/hexl.el2
-rw-r--r--lisp/htmlfontify.el5
-rw-r--r--lisp/ielm.el2
-rw-r--r--lisp/image/wallpaper.el2
-rw-r--r--lisp/international/fontset.el17
-rw-r--r--lisp/isearch.el27
-rw-r--r--lisp/loadup.el174
-rw-r--r--lisp/ls-lisp.el2
-rw-r--r--lisp/mail/emacsbug.el6
-rw-r--r--lisp/mail/rmail.el6
-rw-r--r--lisp/menu-bar.el28
-rw-r--r--lisp/minibuffer.el58
-rw-r--r--lisp/mwheel.el12
-rw-r--r--lisp/net/browse-url.el22
-rw-r--r--lisp/net/eww.el8
-rw-r--r--lisp/org/org-ctags.el4
-rw-r--r--lisp/pixel-scroll.el2
-rw-r--r--lisp/play/doctor.el5
-rw-r--r--lisp/play/dunnet.el5
-rw-r--r--lisp/play/gamegrid.el4
-rw-r--r--lisp/progmodes/cperl-mode.el2
-rw-r--r--lisp/progmodes/prog-mode.el2
-rw-r--r--lisp/shell.el7
-rw-r--r--lisp/simple.el85
-rw-r--r--lisp/speedbar.el2
-rw-r--r--lisp/startup.el13
-rw-r--r--lisp/subr.el64
-rw-r--r--lisp/term.el3
-rw-r--r--lisp/term/android-win.el237
-rw-r--r--lisp/textmodes/reftex-global.el6
-rw-r--r--lisp/textmodes/text-mode.el6
-rw-r--r--lisp/touch-screen.el667
-rw-r--r--lisp/version.el72
-rw-r--r--lisp/wid-edit.el122
-rw-r--r--m4/asm-underscore.m483
-rw-r--r--m4/exponentd.m4116
-rw-r--r--m4/exponentf.m492
-rw-r--r--m4/exponentl.m4112
-rw-r--r--m4/float_h.m4106
-rw-r--r--m4/frexp.m4181
-rw-r--r--m4/frexpl.m4233
-rw-r--r--m4/fseterr.m413
-rw-r--r--m4/getdelim.m4114
-rw-r--r--m4/getline.m4111
-rw-r--r--m4/gnulib-comp.m4253
-rw-r--r--m4/intmax_t.m459
-rw-r--r--m4/inttypes_h.m429
-rw-r--r--m4/isnand.m496
-rw-r--r--m4/isnanf.m4197
-rw-r--r--m4/isnanl.m4248
-rw-r--r--m4/ldexpl.m4135
-rw-r--r--m4/math_h.m4391
-rw-r--r--m4/ndk-build.m4480
-rw-r--r--m4/printf-frexp.m438
-rw-r--r--m4/printf-frexpl.m448
-rw-r--r--m4/printf-posix-rpl.m426
-rw-r--r--m4/printf.m41728
-rw-r--r--m4/signbit.m4393
-rw-r--r--m4/size_max.m475
-rw-r--r--m4/stdint_h.m427
-rw-r--r--m4/stpncpy.m4108
-rw-r--r--m4/vasnprintf.m4298
-rw-r--r--m4/vasprintf-posix.m4101
-rw-r--r--m4/vasprintf.m446
-rw-r--r--m4/vfprintf-posix.m4110
-rw-r--r--m4/xsize.m412
-rw-r--r--msdos/sed1v2.inp12
-rw-r--r--msdos/sed3v2.inp1
-rw-r--r--msdos/sedlibcf.inp1
-rw-r--r--msdos/sedlibmk.inp20
-rw-r--r--nt/gnulib-cfg.mk35
-rw-r--r--nt/mingw-cfg.site15
-rw-r--r--src/Makefile.in72
-rw-r--r--src/alloc.c84
-rw-r--r--src/android-asset.h422
-rw-r--r--src/android-emacs.c169
-rw-r--r--src/android.c6726
-rw-r--r--src/android.h231
-rw-r--r--src/androidfns.c3205
-rw-r--r--src/androidfont.c1108
-rw-r--r--src/androidgui.h724
-rw-r--r--src/androidmenu.c829
-rw-r--r--src/androidselect.c499
-rw-r--r--src/androidterm.c6073
-rw-r--r--src/androidterm.h476
-rw-r--r--src/buffer.c24
-rw-r--r--src/buffer.h11
-rw-r--r--src/callproc.c108
-rw-r--r--src/charset.c5
-rw-r--r--src/coding.c27
-rw-r--r--src/coding.h4
-rw-r--r--src/conf_post.h10
-rw-r--r--src/dired.c65
-rw-r--r--src/dispextern.h62
-rw-r--r--src/dispnew.c51
-rw-r--r--src/editfns.c8
-rw-r--r--src/emacs-module.c162
-rw-r--r--src/emacs.c138
-rw-r--r--src/epaths.in22
-rw-r--r--src/fileio.c343
-rw-r--r--src/filelock.c33
-rw-r--r--src/fns.c4
-rw-r--r--src/font.c26
-rw-r--r--src/font.h14
-rw-r--r--src/fontset.c27
-rw-r--r--src/frame.c58
-rw-r--r--src/frame.h103
-rw-r--r--src/fringe.c23
-rw-r--r--src/image.c731
-rw-r--r--src/inotify.c19
-rw-r--r--src/keyboard.c252
-rw-r--r--src/keyboard.h13
-rw-r--r--src/lisp.h24
-rw-r--r--src/lread.c326
-rw-r--r--src/marker.c26
-rw-r--r--src/menu.c20
-rw-r--r--src/pdumper.c24
-rw-r--r--src/print.c17
-rw-r--r--src/process.c25
-rw-r--r--src/scroll.c7
-rw-r--r--src/sfnt.c19684
-rw-r--r--src/sfnt.h1994
-rw-r--r--src/sfntfont-android.c793
-rw-r--r--src/sfntfont.c3874
-rw-r--r--src/sfntfont.h79
-rw-r--r--src/sound.c2
-rw-r--r--src/sysdep.c168
-rw-r--r--src/term.c147
-rw-r--r--src/termhooks.h22
-rw-r--r--src/terminal.c2
-rw-r--r--src/textconv.c1554
-rw-r--r--src/textconv.h42
-rw-r--r--src/verbose.mk.in11
-rw-r--r--src/w32.c3
-rw-r--r--src/w32proc.c2
-rw-r--r--src/window.c5
-rw-r--r--src/window.h19
-rw-r--r--src/xdisp.c125
-rw-r--r--src/xfaces.c61
-rw-r--r--src/xfns.c7
-rw-r--r--src/xterm.c86
-rw-r--r--src/xterm.h7
315 files changed, 99745 insertions, 935 deletions
diff --git a/.dir-locals.el b/.dir-locals.el
index 0bcded4b5d1..f0f12db6717 100644
--- a/.dir-locals.el
+++ b/.dir-locals.el
@@ -12,7 +12,7 @@
(c-mode . ((c-file-style . "GNU")
(c-noise-macro-names . ("INLINE" "NO_INLINE" "ATTRIBUTE_NO_SANITIZE_UNDEFINED"
"UNINIT" "CALLBACK" "ALIGN_STACK" "ATTRIBUTE_MALLOC"
- "ATTRIBUTE_DEALLOC_FREE"))
+ "ATTRIBUTE_DEALLOC_FREE" "ANDROID_EXPORT"))
(electric-quote-comment . nil)
(electric-quote-string . nil)
(indent-tabs-mode . t)
diff --git a/.gitignore b/.gitignore
index b09a0c030b3..c95bccccfbf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -52,6 +52,23 @@ src/config.h
src/epaths.h
src/emacs-module.h
+# Built by recursive call to `configure'.
+*.android
+!INSTALL.android
+!verbose.mk.android
+
+# Built by `javac'.
+java/install_temp/*
+java/*.apk*
+java/*.dex
+java/org/gnu/emacs/*.class
+
+# Built by `aapt'.
+java/org/gnu/emacs/R.java
+
+# Built by `config.status'.
+java/AndroidManifest.xml
+
# C-level sources built by 'make'.
lib/alloca.h
lib/assert.h
@@ -70,8 +87,10 @@ lib/limits.h
lib/malloc/*.gl.h
lib/signal.h
lib/std*.h
+lib/math.h
!lib/std*.in.h
!lib/stdio-impl.h
+!lib/_Noreturn.h
lib/string.h
lib/sys/
lib/time.h
@@ -81,6 +100,18 @@ src/globals.h
src/lisp.mk
src/verbose.mk
+# Stuff built during cross compilation
+cross/lib/*
+cross/src/*
+cross/lib-src/*
+cross/sys/*
+cross/config.status
+cross/*.bak
+
+cross/ndk-build/Makefile
+cross/ndk-build/ndk-build.mk
+cross/ndk-build/*.o
+
# Lisp-level sources built by 'make'.
*cus-load.el
*loaddefs.el
@@ -186,6 +217,7 @@ ID
# Executables.
*.exe
a.out
+lib-src/asset-directory-tool
lib-src/be-resources
lib-src/blessmail
lib-src/ctags
@@ -208,6 +240,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
@@ -338,3 +371,15 @@ lib-src/seccomp-filter-exec.pfc
# GDB history
.gdb_history
_gdb_history
+
+# Files ignored in exec/.
+exec/config.status
+exec/loader
+exec/test
+exec/exec1
+exec/deps/*
+exec/autom4te.cache
+exec/config.h
+exec/config-mips.m4
+exec/configure
+exec/*.s.s
diff --git a/INSTALL b/INSTALL
index 344ae39f464..a69e1f2aec7 100644
--- a/INSTALL
+++ b/INSTALL
@@ -5,9 +5,9 @@ See the end of the file for license conditions.
This file contains general information on building GNU Emacs. For
-more information specific to the MS-Windows, GNUstep/macOS, and MS-DOS
-ports, also read the files nt/INSTALL, nextstep/INSTALL, and
-msdos/INSTALL.
+more information specific to the MS-Windows, GNUstep/macOS, MS-DOS,
+and Android ports, also read the files nt/INSTALL, nextstep/INSTALL,
+msdos/INSTALL, and java/INSTALL.
For information about building from a Git checkout (rather than an
Emacs release), read the INSTALL.REPO file first.
diff --git a/Makefile.in b/Makefile.in
index 729cd4140e5..b47e88f6970 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -106,15 +106,15 @@ top_builddir = @top_builddir@
FIND_DELETE = @FIND_DELETE@
-HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
-
USE_STARTUP_NOTIFICATION = @USE_STARTUP_NOTIFICATION@
+HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
HAVE_BE_APP = @HAVE_BE_APP@
-
HAVE_PGTK = @HAVE_PGTK@
HAVE_GSETTINGS = @HAVE_GSETTINGS@
+ANDROID = @ANDROID@
+
# ==================== Where To Install Things ====================
# Location to install Emacs.app under GNUstep / macOS.
@@ -339,6 +339,10 @@ EMACS_PDMP = `./src/emacs${EXEEXT} --fingerprint`.pdmp
# Subdirectories to make recursively.
SUBDIR = $(NTDIR) lib lib-src src lisp
+ifeq ($(ANDROID),yes)
+SUBDIR := $(SUBDIR) java
+endif
+
# The subdir makefiles created by config.status.
SUBDIR_MAKEFILES_IN = @SUBDIR_MAKEFILES_IN@
SUBDIR_MAKEFILES = $(patsubst ${srcdir}/%,%,${SUBDIR_MAKEFILES_IN:.in=})
@@ -467,20 +471,20 @@ epaths-force:
esac; \
done
@(gamedir='${gamedir}'; \
- sed < ${srcdir}/src/epaths.in > epaths.h.$$$$ \
- -e 's;\(#.*PATH_LOADSEARCH\).*$$;\1 "${standardlisppath}";' \
- -e 's;\(#.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";' \
- -e 's;\(#.*PATH_SITELOADSEARCH\).*$$;\1 "${locallisppath}";' \
- -e 's;\(#.*PATH_DUMPLOADSEARCH\).*$$;\1 "${buildlisppath}";' \
- -e '/^#define PATH_[^ ]*SEARCH /s/\([":]\):*/\1/g' \
- -e '/^#define PATH_[^ ]*SEARCH /s/:"/"/' \
- -e 's;\(#.*PATH_EXEC\).*$$;\1 "${archlibdir}";' \
- -e 's;\(#.*PATH_INFO\).*$$;\1 "${infodir}";' \
- -e 's;\(#.*PATH_DATA\).*$$;\1 "${etcdir}";' \
- -e 's;\(#.*PATH_BITMAPS\).*$$;\1 "${bitmapdir}";' \
- -e 's;\(#.*PATH_X_DEFAULTS\).*$$;\1 "${x_default_search_path}";' \
- -e 's;\(#.*PATH_GAME\).*$$;\1 $(PATH_GAME);' \
- -e 's;\(#.*PATH_DOC\).*$$;\1 "${etcdocdir}";') && \
+ sed < ${srcdir}/src/epaths.in > epaths.h.$$$$ \
+ -e 's;\(#define.*PATH_LOADSEARCH\).*$$;\1 "${standardlisppath}";' \
+ -e 's;\(#define.*PATH_REL_LOADSEARCH\).*$$;\1 "${lispdirrel}";' \
+ -e 's;\(#define.*PATH_SITELOADSEARCH\).*$$;\1 "${locallisppath}";' \
+ -e 's;\(#define.*PATH_DUMPLOADSEARCH\).*$$;\1 "${buildlisppath}";' \
+ -e '/^#define PATH_[^ ]*SEARCH /s/\([":]\):*/\1/g' \
+ -e '/^#define PATH_[^ ]*SEARCH /s/:"/"/' \
+ -e 's;\(#define.*PATH_EXEC\).*$$;\1 "${archlibdir}";' \
+ -e 's;\(#define.*PATH_INFO\).*$$;\1 "${infodir}";' \
+ -e 's;\(#define.*PATH_DATA\).*$$;\1 "${etcdir}";' \
+ -e 's;\(#define.*PATH_BITMAPS\).*$$;\1 "${bitmapdir}";' \
+ -e 's;\(#define.*PATH_X_DEFAULTS\).*$$;\1 "${x_default_search_path}";' \
+ -e 's;\(#define.*PATH_GAME\).*$$;\1 $(PATH_GAME);' \
+ -e 's;\(#define.*PATH_DOC\).*$$;\1 "${etcdocdir}";') && \
${srcdir}/build-aux/move-if-change epaths.h.$$$$ src/epaths.h
# The w32 build needs a slightly different editing, and it uses
@@ -532,6 +536,12 @@ lisp: src
lib lib-src lisp nt: Makefile
$(MAKE) -C $@ all
+java: lisp info
+ $(MAKE) -C $@ all
+
+cross: src
+ $(MAKE) -C $@ all
+
trampolines: src lisp
ifeq ($(HAVE_NATIVE_COMP),yes)
$(MAKE) -C lisp trampolines
@@ -569,10 +579,13 @@ $(MAKEFILE_NAME): config.status $(srcdir)/configure \
# Don't erase these files if make is interrupted while refreshing them.
.PRECIOUS: Makefile config.status
+# Note that calling config.status --recheck is insufficient on Android
+# due to the recursive calls to configure.
+
config.status: ${srcdir}/configure
- if [ -x ./config.status ]; then \
+ if [ -x ./config.status ]; then \
$(CFG) ./config.status --recheck; \
- else \
+ else \
$(CFG) $(srcdir)/configure $(CONFIGURE_FLAGS); \
fi
@@ -987,6 +1000,12 @@ endef
mostlyclean_dirs = src oldXMenu lwlib lib lib-src nt doc/emacs doc/misc \
doc/lispref doc/lispintro test
+### Add the libexec directory to mostlyclean_dirs if its Makefile has
+### been created.
+ifneq ($(wildcard exec/Makefile),)
+mostlyclean_dirs := $(mostlyclean_dirs) exec
+endif
+
$(foreach dir,$(mostlyclean_dirs),$(eval $(call submake_template,$(dir),mostlyclean)))
mostlyclean: $(mostlyclean_dirs:=_mostlyclean)
@@ -999,7 +1018,8 @@ mostlyclean: $(mostlyclean_dirs:=_mostlyclean)
### with them.
###
### Delete '.dvi' files here if they are not part of the distribution.
-clean_dirs = $(mostlyclean_dirs) nextstep admin/charsets admin/unidata
+clean_dirs = $(mostlyclean_dirs) java cross nextstep admin/charsets \
+ admin/unidata
$(foreach dir,$(clean_dirs),$(eval $(call submake_template,$(dir),clean)))
@@ -1081,6 +1101,8 @@ extraclean: maintainer-clean
-[ "${srcdir}" = "." ] || \
find ${srcdir} '(' -name '*~' -o -name '#*' ')' ${FIND_DELETE}
-find . '(' -name '*~' -o -name '#*' ')' ${FIND_DELETE}
+ -rm -f ${srcdir}/exec/config-tmp-* ${srcdir}/exec/aclocal.m4 \
+ ${srcdir}/src/config.in ${srcdir}/exec/configure
# The src subdir knows how to do the right thing
# even when the build directory and source dir are different.
diff --git a/README b/README
index 19d5c96e348..694b3aa4c94 100644
--- a/README
+++ b/README
@@ -95,6 +95,11 @@ There are several subdirectories:
'admin' holds files used by Emacs developers, and Unicode data files.
'build-aux' holds auxiliary files used during the build.
'm4' holds Autoconf macros used for generating the configure script.
+'java' holds the Java code for the Emacs port to Android.
+'cross' holds Makefiles and an additional copy of gnulib used to build
+ Emacs for Android devices.
+'exec' holds the source code to several helper executables used to run
+ user-installed programs on Android.
Building Emacs on non-Posix platforms requires tools that aren't part
of the standard distribution of the OS. The platform-specific README
diff --git a/admin/merge-gnulib b/admin/merge-gnulib
index 97487381886..76e2a02558c 100755
--- a/admin/merge-gnulib
+++ b/admin/merge-gnulib
@@ -37,15 +37,15 @@ 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
nanosleep nproc nstrftime
- pathmax pipe2 pselect pthread_sigmask
+ pathmax pipe2 printf-posix vasprintf-posix pselect pthread_sigmask
qcopy-acl readlink readlinkat regex
sig2str sigdescr_np socklen stat-time std-gnu11 stdbool stddef stdio
- stpcpy strnlen strtoimax symlink sys_stat sys_time
+ stpcpy stpncpy strnlen strtoimax symlink sys_stat sys_time
tempname time-h time_r time_rz timegm timer-time timespec-add timespec-sub
update-copyright unlocked-io utimensat
vla warnings year2038
@@ -114,6 +114,11 @@ for module in $AVOIDED_MODULES; do
avoided_flags="$avoided_flags --avoid=$module"
done
+# Clean the lib directory as well.
+if [ -e "$src"/lib/Makefile ]; then
+ make -C "$src"/lib maintainer-clean
+fi
+
"$gnulib_srcdir"/gnulib-tool --dir="$src" $GNULIB_TOOL_FLAGS \
$avoided_flags $GNULIB_MODULES &&
rm -- "$src"lib/gl_openssl.h \
diff --git a/autogen.sh b/autogen.sh
index 6127e7b24f4..3cb77afedb3 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -256,6 +256,12 @@ Please report any problems with this script to bug-gnu-emacs@gnu.org .'
## Let autoreconf figure out what, if anything, needs doing.
## Use autoreconf's -f option in case autoreconf itself has changed.
autoreconf -fi -I m4 || exit
+
+ echo "Running 'autoreconf -fi' in exec ..."
+
+ # Now, run autoreconf inside the exec directory to generate its
+ # configure script.
+ autoreconf -fi exec || exit
fi
diff --git a/build-aux/ndk-build-helper-1.mk b/build-aux/ndk-build-helper-1.mk
new file mode 100644
index 00000000000..2cde5146301
--- /dev/null
+++ b/build-aux/ndk-build-helper-1.mk
@@ -0,0 +1,112 @@
+# ndk-build-helper-1.mk -- Helper for ndk-build.m4.
+# 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/>.
+
+# Print out information now defined. Important details include:
+# - list of source files to compile.
+# - module export include directories.
+# - module export CFLAGS.
+# - module export LDFLAGS.
+# - module name.
+
+build_kind = shared
+NDK_SO_NAMES =
+NDK_A_NAMES =
+
+# Record this module's dependencies. This information is used later
+# on to recurse over libraries.
+NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES := $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)
+NDK_$(LOCAL_MODULE)_SHARED_LIBRARIES := $(LOCAL_SHARED_LIBRARIES)
+NDK_$(LOCAL_MODULE)_EXPORT_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
+NDK_CXX_FLAG_$(LOCAL_MODULE) :=
+
+$(info Building $(build_kind))
+$(info $(LOCAL_MODULE))
+$(info $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES$(EMACS_ABI))))
+
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+NDK_SO_NAMES = $(LOCAL_MODULE)_emacs.so
+else
+NDK_SO_NAMES = lib$(LOCAL_MODULE)_emacs.so
+endif
+
+define add-so-name-1
+# Now recurse over this module's dependencies.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-so-name-1,$$(module))))
+endef
+
+define add-so-name
+ifeq ($(findstring lib,$(1)),lib)
+NDK_SO_NAME = $(1)_emacs.so
+else
+NDK_SO_NAME = lib$(1)_emacs.so
+endif
+
+ifeq ($$(findstring $$(NDK_SO_NAME),$$(NDK_SO_NAMES)),)
+NDK_SO_NAMES := $$(NDK_SO_NAMES) $$(NDK_SO_NAME)
+
+# Now recurse over this module's dependencies.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+
+# Recurse over static library dependencies of this shared library.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES) $$(NDK_$(1)_WHOLE_LIBRARIES)),$$(eval $$(call add-so-name-1,$$(module))))
+endif
+
+ifneq ($$(findstring stdc++,$$(NDK_$(1)_SHARED_LIBRARIES)),)
+NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
+endif
+endef
+
+# Figure out includes from dependencies as well.
+NDK_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
+
+define add-includes
+ifeq ($$(findstring $$(NDK_$(1)_EXPORT_INCLUDES),$$(NDK_INCLUDES)),)
+NDK_INCLUDES += $$(NDK_$(1)_EXPORT_INCLUDES)
+
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)) $$(NDK_$(1)_STATIC_LIBRARIES),$$(eval $$(call add-includes,$$(module))))
+
+# Recurse over shared library dependencies of this static library.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+
+# Recurse over static or shared library dependencies of this static
+# library.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-so-name-1,$$(module))))
+endif
+endef
+
+# Resolve additional dependencies and their export includes based on
+# LOCAL_STATIC_LIBRARIES and LOCAL_SHARED_LIBRARIES. Static library
+# dependencies can be ignored while building a shared library, as they
+# will be linked in to the resulting shared object file later.
+
+SYSTEM_LIBRARIES = z libz libc c libdl dl stdc++ libstdc++ log liblog android libandroid
+
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES)),$(eval $(call add-so-name,$(module))))
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES) $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)),$(eval $(call add-includes,$(module))))
+
+ifneq ($(findstring stdc++,$(LOCAL_SHARED_LIBRARIES)),)
+NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
+endif
+
+$(info $(foreach dir,$(NDK_INCLUDES),-I$(dir)))
+$(info $(LOCAL_EXPORT_CFLAGS))
+
+$(info $(LOCAL_EXPORT_LDFLAGS) $(abspath $(addprefix $(NDK_BUILD_DIR)/,$(NDK_A_NAMES))) -L$(abspath $(NDK_BUILD_DIR)) $(foreach soname,$(NDK_SO_NAMES),-l:$(soname)))
+$(info $(NDK_SO_NAMES))
+$(info $(NDK_CXX_FLAG_$(LOCAL_MODULE)))
+$(info End)
diff --git a/build-aux/ndk-build-helper-2.mk b/build-aux/ndk-build-helper-2.mk
new file mode 100644
index 00000000000..186f3aec333
--- /dev/null
+++ b/build-aux/ndk-build-helper-2.mk
@@ -0,0 +1,105 @@
+# ndk-build-helper-2.mk -- Helper for ndk-build.m4.
+# 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/>.
+
+# Say a static library is being built
+build_kind = static
+NDK_SO_NAMES =
+NDK_A_NAMES =
+
+# Record this module's dependencies. This information is used later
+# on to recurse over libraries.
+NDK_$(LOCAL_MODULE)_STATIC_LIBRARIES := $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)
+NDK_$(LOCAL_MODULE)_SHARED_LIBRARIES := $(LOCAL_SHARED_LIBRARIES)
+NDK_$(LOCAL_MODULE)_EXPORT_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
+NDK_CXX_FLAG_$(LOCAL_MODULE) :=
+
+$(info Building $(build_kind))
+$(info $(LOCAL_MODULE))
+$(info $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES$(EMACS_ABI))))
+
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+NDK_A_NAMES = $(LOCAL_MODULE).a
+else
+NDK_A_NAMES = lib$(LOCAL_MODULE).a
+endif
+
+define add-a-name
+ifeq ($(findstring lib,$(1)),lib)
+NDK_A_NAME = $(1).a
+else
+NDK_A_NAME = lib$(1).a
+endif
+
+ifeq ($$(findstring $$(NDK_A_NAME),$$(NDK_A_NAMES)),)
+NDK_A_NAMES := $$(NDK_A_NAMES) $$(NDK_A_NAME)
+
+# Now recurse over this module's dependencies.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-a-name,$$(module))))
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+endif
+
+ifneq ($$(findstring stdc++,$$(NDK_$(1)_SHARED_LIBRARIES)),)
+NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
+endif
+endef
+
+define add-so-name
+ifeq ($(findstring lib,$(1)),lib)
+NDK_SO_NAME = $(1)_emacs.so
+else
+NDK_SO_NAME = lib$(1)_emacs.so
+endif
+
+ifeq ($$(NDK_SO_NAMES:$$(NDK_SO_NAME)=),$$(NDK_SO_NAMES))
+NDK_SO_NAMES := $$(NDK_SO_NAMES) $$(NDK_SO_NAME)
+
+# Now recurse over this module's dependencies.
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_STATIC_LIBRARIES)),$$(eval $$(call add-a-name,$$(module))))
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)),$$(eval $$(call add-so-name,$$(module))))
+endif
+endef
+
+# Figure out includes from dependencies as well.
+NDK_INCLUDES := $(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES)
+
+define add-includes
+ifeq ($$(findstring $$(NDK_$(1)_EXPORT_INCLUDES),$$(NDK_INCLUDES)),)
+NDK_INCLUDES += $$(NDK_$(1)_EXPORT_INCLUDES)
+
+$$(foreach module,$$(filter-out $$(SYSTEM_LIBRARIES), $$(NDK_$(1)_SHARED_LIBRARIES)) $$(NDK_$(1)_STATIC_LIBRARIES),$$(eval $$(call add-includes,$$(module))))
+endif
+endef
+
+# Resolve additional dependencies based on LOCAL_STATIC_LIBRARIES and
+# LOCAL_SHARED_LIBRARIES.
+
+SYSTEM_LIBRARIES = z libz libc c libdl dl libstdc++ stdc++ log liblog android libandroid
+
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)),$(eval $(call add-a-name,$(module))))
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES)),$(eval $(call add-so-name,$(module))))
+$(foreach module,$(filter-out $(SYSTEM_LIBRARIES), $(LOCAL_SHARED_LIBRARIES) $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_LIBRARIES)),$(eval $(call add-includes,$(module))))
+
+ifneq ($(findstring stdc++,$(LOCAL_SHARED_LIBRARIES)),)
+NDK_CXX_FLAG_$(LOCAL_MODULE) := yes
+endif
+
+$(info $(foreach dir,$(NDK_INCLUDES),-I$(dir)))
+$(info $(LOCAL_EXPORT_CFLAGS))
+$(info $(LOCAL_EXPORT_LDFLAGS) $(abspath $(addprefix $(NDK_BUILD_DIR)/,$(NDK_A_NAMES))) $(and $(NDK_SO_NAMES), -L$(abspath $(NDK_BUILD_DIR)) $(foreach soname,$(NDK_SO_NAMES),-l:$(soname))))
+$(info $(NDK_A_NAMES) $(NDK_SO_NAMES))
+$(info $(NDK_CXX_FLAG_$(LOCAL_MODULE)))
+$(info End)
diff --git a/build-aux/ndk-build-helper-3.mk b/build-aux/ndk-build-helper-3.mk
new file mode 100644
index 00000000000..4d0358d4f77
--- /dev/null
+++ b/build-aux/ndk-build-helper-3.mk
@@ -0,0 +1,28 @@
+# ndk-build-helper-3.mk -- Helper for ndk-build.m4.
+# 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/>.
+
+# Say a static library is being built
+build_kind = executable
+
+$(info Building $(build_kind))
+$(info $(LOCAL_MODULE))
+$(info $(addprefix $(ANDROID_MODULE_DIRECTORY),$(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES$(EMACS_ABI))))
+
+$(info $(foreach dir,$(LOCAL_EXPORT_C_INCLUDE_DIRS) $(LOCAL_EXPORT_C_INCLUDES),-I$(dir)))
+$(info $(LOCAL_EXPORT_CFLAGS))
+$(info $(LOCAL_EXPORT_LDFLAGS))
+$(info End)
diff --git a/build-aux/ndk-build-helper-4.mk b/build-aux/ndk-build-helper-4.mk
new file mode 100644
index 00000000000..a41679c53af
--- /dev/null
+++ b/build-aux/ndk-build-helper-4.mk
@@ -0,0 +1,39 @@
+# 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/>.
+
+undefine LOCAL_MODULE
+undefine LOCAL_MODULE_FILENAME
+undefine LOCAL_SRC_FILES
+undefine LOCAL_CPP_EXTENSION
+undefine LOCAL_CPP_FEATURES
+undefine LOCAL_C_INCLUDES
+undefine LOCAL_CFLAGS
+undefine LOCAL_CPPFLAGS
+undefine LOCAL_STATIC_LIBRARIES
+undefine LOCAL_SHARED_LIBRARIES
+undefine LOCAL_WHOLE_STATIC_LIBRARIES
+undefine LOCAL_LDLIBS
+undefine LOCAL_LDFLAGS
+undefine LOCAL_ALLOW_UNDEFINED_SYMBOLS
+undefine LOCAL_ARM_MODE
+undefine LOCAL_ARM_NEON
+undefine LOCAL_DISABLE_FORMAT_STRING_CHECKS
+undefine LOCAL_EXPORT_CFLAGS
+undefine LOCAL_EXPORT_CPPFLAGS
+undefine LOCAL_EXPORT_C_INCLUDES
+undefine LOCAL_EXPORT_C_INCLUDE_DIRS
+undefine LOCAL_EXPORT_LDFLAGS
+undefine LOCAL_EXPORT_LDLIBS
diff --git a/build-aux/ndk-build-helper.mk b/build-aux/ndk-build-helper.mk
new file mode 100644
index 00000000000..05f0af76411
--- /dev/null
+++ b/build-aux/ndk-build-helper.mk
@@ -0,0 +1,81 @@
+# ndk-build-helper.mk -- Helper for ndk-build.m4.
+# 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/>.
+
+# This Makefile sets up enough to parse an Android-style Android.mk
+# file and return useful information about its contents.
+
+# See the text under ``NDK BUILD SYSTEM IMPLEMENTATION'' in
+# cross/ndk-build/README for more details.
+
+# TARGET_ARCH_ABI is the ABI that is being built for.
+TARGET_ARCH_ABI := $(EMACS_ABI)
+
+# TARGET_ARCH is the architecture that is being built for.
+TARGET_ARCH := $(NDK_BUILD_ARCH)
+
+# NDK_LAST_MAKEFILE is the last Makefile that was included.
+NDK_LAST_MAKEFILE = $(lastword $(filter %Android.mk,$(MAKEFILE_LIST)))
+
+# local-makefile is the current Makefile being loaded.
+local-makefile = $(NDK_LAST_MAKEFILE)
+
+# Make NDK_BUILD_DIR absolute.
+NDK_BUILD_DIR := $(absname $(NDK_BUILD_DIR))
+
+# Make EMACS_SRCDIR absolute. This must be absolute, or nested
+# Android.mk files will not be able to find CLEAR_VARS.
+EMACS_SRCDIR := $(absname $(EMACS_SRCDIR))
+
+# my-dir is a function that returns the Android module directory. If
+# no Android.mk has been loaded, use ANDROID_MODULE_DIRECTORY.
+my-dir = $(or $(and $(local-makefile),$(dir $(local-makefile))),$(ANDROID_MODULE_DIRECTORY))
+
+# Return all Android.mk files under the first arg.
+all-makefiles-under = $(wildcard $(1)/*/Android.mk)
+
+# Return all Android.mk files in subdirectories of this Makefile's
+# location.
+all-subdir-makefiles = $(call all-makefiles-under,$(call my-dir))
+
+# These functions are not implemented.
+parent-makefile =
+grand-parent-makefile =
+
+NDK_IMPORTS :=
+
+# Add the specified module (arg 1) to NDK_IMPORTS.
+import-module = $(eval NDK_IMPORTS += $(1))
+
+# Print out module information every time BUILD_SHARED_LIBRARY is
+# called.
+
+BUILD_SHARED_LIBRARY=$(BUILD_AUXDIR)ndk-build-helper-1.mk
+BUILD_STATIC_LIBRARY=$(BUILD_AUXDIR)ndk-build-helper-2.mk
+BUILD_EXECUTABLE=$(BUILD_AUXDIR)ndk-build-helper-3.mk
+CLEAR_VARS=$(BUILD_AUXDIR)ndk-build-helper-4.mk
+
+# Now include Android.mk.
+
+include $(ANDROID_MAKEFILE)
+
+# Finally, print out the imports.
+$(info Start Imports)
+$(info $(NDK_IMPORTS))
+$(info End Imports)
+
+# Dummy target.
+all:
diff --git a/build-aux/ndk-module-extract.awk b/build-aux/ndk-module-extract.awk
new file mode 100644
index 00000000000..6ff30973d67
--- /dev/null
+++ b/build-aux/ndk-module-extract.awk
@@ -0,0 +1,88 @@
+/^Building.+$/ {
+ kind = $2
+}
+
+/^Start Imports$/ {
+ imports = 1
+}
+
+// {
+ if (imports && ++imports > 2)
+ {
+ if (!match ($0, /^End Imports$/))
+ makefile_imports = makefile_imports " " $0
+ }
+ else if (!match ($0, /^End$/) && !match ($0, /^Building.+$/))
+ {
+ if (kind)
+ {
+ if (target_found)
+ cxx_deps = $0
+ else if (ldflags_found)
+ {
+ target = $0
+ target_found = 1
+ }
+ else if (cflags_found)
+ {
+ ldflags = $0
+ ldflags_found = 1
+ }
+ else if (includes_found)
+ {
+ cflags = $0
+ cflags_found = 1
+ }
+ else if (src_found)
+ {
+ includes = $0
+ includes_found = 1
+ }
+ else if (name_found)
+ {
+ src = $0
+ src_found = 1;
+ }
+ else
+ {
+ name = $0
+ name_found = 1
+ }
+ }
+ }
+}
+
+/^End$/ {
+ if (name == MODULE && (kind == "shared" || kind == "static"))
+ {
+ printf "module_name=%s\n", name
+ printf "module_kind=%s\n", kind
+ printf "module_src=\"%s\"\n", src
+ printf "module_includes=\"%s\"\n", includes
+ printf "module_cflags=\"%s\"\n", cflags
+ printf "module_ldflags=\"%s\"\n", ldflags
+ printf "module_target=\"%s\"\n", target
+ printf "module_cxx_deps=\"%s\"\n", cxx_deps
+ }
+
+ src = ""
+ name = ""
+ kind = ""
+ includes = ""
+ cflags = ""
+ ldflags = ""
+ name_found = ""
+ src_found = ""
+ includes_found = ""
+ cflags_found = ""
+ ldflags_found = ""
+ target_found = ""
+}
+
+/^End Imports$/ {
+ imports = ""
+ # Strip off leading whitespace.
+ gsub (/^[ \t]+/, "", makefile_imports)
+ printf "module_imports=\"%s\"\n", makefile_imports
+ makefile_imports = ""
+}
diff --git a/configure.ac b/configure.ac
index 95167329c28..65609ec8021 100644
--- a/configure.ac
+++ b/configure.ac
@@ -26,6 +26,25 @@ dnl Note this is parsed by (at least) make-dist and lisp/cedet/ede/emacs.el.
AC_INIT([GNU Emacs], [30.0.50], [bug-gnu-emacs@gnu.org], [],
[https://www.gnu.org/software/emacs/])
+if test "$XCONFIGURE" = "android"; then
+ # configure is being called recursively to configure Emacs for
+ # Android!
+ AC_MSG_NOTICE([called to recursively configure Emacs for Android.])
+ # 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,
dnl and then quoted again for a C string. Separate options with spaces.
dnl Add some environment variables, if they were passed via the environment
@@ -126,7 +145,64 @@ MAKE=$ac_cv_path_MAKE
export MAKE
dnl Canonicalize the configuration name.
+if test "$XCONFIGURE" = "android"; then
+ dnl Set host to whatever Android system Emacs is being configured
+ dnl for. Determine this by looking at the output of ANDROID_CC.
+
+ AC_MSG_CHECKING([the cross-compiler's target])
+ cc_target=`${CC} -v 2>&1 | sed -n 's/Target: //p'`
+ case "$cc_target" in
+ *android*) host_alias=$cc_target
+ ;;
+ *) AC_MSG_ERROR([The cross compiler does not compile for Android.
+Please verify that you specified the correct compiler in the ANDROID_CC
+variable when you ran configure.])
+ ;;
+ esac
+ AC_MSG_RESULT([$host_alias])
+fi
+
AC_CANONICAL_HOST
+AC_CANONICAL_BUILD
+
+AS_IF([test "$XCONFIGURE" = "android"],[
+ # Initialize the Android NDK build system. Make sure to use the
+ # passed through NDK path.
+ # Make sure to pass through the CFLAGS, as older versions of the
+ # NDK require them to be able to find system includes.
+ with_ndk_path="$android_ndk_path"
+ with_ndk_cxx_shared="$android_ndk_cxx_shared"
+ with_ndk_cxx="$android_ndk_cxx"
+ ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build],
+ [$ANDROID_CFLAGS])
+
+ # At the same time, configure libexec with the build directory
+ # set to `exec'.
+ AS_MKDIR_P([exec])
+
+ # Enter exec and configure it, using the C compiler as both the
+ # assembler and the linker. Determine the absolute name of the
+ # source directory.
+ # N.B. that the linker is actually cc, so pass -nostdlib, lest
+ # the crt be linked in. Likewise for as.
+
+ AS_CASE([$srcdir], [.], [emacs_srcdir=`pwd`],
+ [[[\\/]* | ?:[\\/]*]], [emacs_srcdir=$srcdir],
+ [*], [emacs_srcdir=`pwd`/$srcdir])
+
+ AC_MSG_NOTICE([configuring in `exec'])
+
+ OLDCWD=`pwd`
+ cd exec
+ $CONFIG_SHELL $emacs_srcdir/exec/configure \
+ --host=$host "CC=$CC" "LD=$CC" "AS=$CC" \
+ "AR=$AR" "CFLAGS=$CFLAGS"
+ emacs_val=$?
+ cd $OLDCWD
+
+ AS_IF([test "$emacs_val" != "0"],
+ [AC_MSG_ERROR([failed to configure in `exec'])])
+])
case $host in
*-mingw*)
@@ -207,6 +283,13 @@ AC_ARG_WITH([all],
[with_features=$withval],
[with_features=yes])
+dnl ARCH_INDEPENDENT_CONFIG_FILES(FILE...)
+dnl Like AC_CONFIG_FILES(FILE). However, do not generate this
+dnl if configure is being called recursively in preparation
+dnl for cross-compilation.
+AC_DEFUN([ARCH_INDEPENDENT_CONFIG_FILES], [
+ AS_IF([test "$XCONFIGURE" != "android"], [AC_CONFIG_FILES([$1])])])
+
dnl OPTION_DEFAULT_OFF(NAME, HELP-STRING)
dnl Create a new --with option that defaults to being disabled.
dnl NAME is the base name of the option. The shell variable with_NAME
@@ -260,24 +343,33 @@ AC_ARG_WITH([mailutils],
installed])],
[],
[with_mailutils=$with_features
- if test "$with_mailutils" = yes; then
- (movemail --version) >/dev/null 2>&1 || with_mailutils=no
- fi])
-if test "$with_mailutils" = no; then
- with_mailutils=
-fi
+ AS_IF([test "$with_mailutils" = yes],
+ [AS_IF([test "x$XCONFIGURE" != "xandroid" \
+ && test "$with_android" = "no"],
+ [(movemail --version) >/dev/null 2>&1 || with_mailutils=no],
+ [dnl Don't check for movemail if cross-compiling.
+ dnl instead, default to false.
+ with_mailutils=no])])])
+AS_IF([test "$with_mailutils" = no],
+ [with_mailutils=])
+
+AS_IF([test x"$with_mailutils" = xyes],
+ [AC_DEFINE([HAVE_MAILUTILS], [1],
+ [Define to 1 if Emacs was configured with mailutils])])
+
AC_SUBST([with_mailutils])
AC_ARG_WITH([pop],
[AS_HELP_STRING([--with-pop],
[Support POP mail retrieval if Emacs movemail is used (not recommended,
as Emacs movemail POP is insecure). This is the default only on
- native MS-Windows.])],
+ native MS-Windows and Android.])],
[],
- [case $host in
- *-mingw*) with_pop=yes;;
- *) with_pop=no-by-default;;
- esac])
+ dnl Enable movemail POP support on Android as GNU Mailutils is
+ dnl normally unavailable on that platform.
+ [AS_CASE([$host],
+ [*-mingw*|*android*], [with_pop=yes],
+ [with_pop=no-by-default])])
if test "$with_pop" = yes; then
AC_DEFINE([MAIL_USE_POP])
fi
@@ -498,6 +590,12 @@ OPTION_DEFAULT_ON([threads],[don't compile with elisp threading support])
OPTION_DEFAULT_OFF([cygwin32-native-compilation],[use native compilation on 32-bit Cygwin])
OPTION_DEFAULT_ON([xinput2],[don't use version 2 of the X Input Extension for input])
OPTION_DEFAULT_OFF([small-ja-dic],[generate a smaller-size Japanese dictionary])
+OPTION_DEFAULT_OFF([android],[cross-compile Android application package])
+OPTION_DEFAULT_ON([android-debug],[don't build Emacs as a debug package on Android])
+
+AC_ARG_WITH([shared-user-id],
+ [AS_HELP_STRING([--with-shared-user-id=ID],
+ [use the given shared user ID in Android builds])])
AC_ARG_WITH([file-notification],[AS_HELP_STRING([--with-file-notification=LIB],
[use a file notification library (LIB one of: yes, inotify, kqueue, gfile, w32, no)])],
@@ -614,37 +712,51 @@ do
done
IFS="$ac_save_IFS"
-if test x$ac_enable_checking != x ; then
- AC_DEFINE([ENABLE_CHECKING], [1],
-[Define to 1 if expensive run-time data type and consistency checks are enabled.])
-fi
-if $CHECK_STRUCTS; then
- AC_DEFINE([CHECK_STRUCTS], [1],
- [Define this to check whether someone updated the portable dumper
- code after changing the layout of a structure that it uses.
- If you change one of these structures, check that the pdumper.c
- code is still valid, and update the pertinent hash in pdumper.c
- by manually copying the hash from the newly-generated dmpstruct.h.])
-fi
-AC_SUBST([CHECK_STRUCTS])
-if test x$ac_gc_check_stringbytes != x ; then
- AC_DEFINE([GC_CHECK_STRING_BYTES], [1],
-[Define this temporarily to hunt a bug. If defined, the size of
- strings is redundantly recorded in sdata structures so that it can
- be compared to the sizes recorded in Lisp strings.])
-fi
-if test x$ac_gc_check_string_overrun != x ; then
- AC_DEFINE([GC_CHECK_STRING_OVERRUN], [1],
-[Define this to check for short string overrun.])
-fi
-if test x$ac_gc_check_string_free_list != x ; then
- AC_DEFINE([GC_CHECK_STRING_FREE_LIST], [1],
-[Define this to check the string free list.])
-fi
-if test x$ac_glyphs_debug != x ; then
- AC_DEFINE([GLYPH_DEBUG], [1],
-[Define this to enable glyphs debugging code.])
-fi
+# This environment variable is used to signal that checking should be
+# enabled on Android. When that happens, simply enable checking for
+# the cross-compiled Android binary.
+
+AS_IF([test "x$XCONFIGURE" = "xandroid" \
+ && test "x$android_enable_checking" = "xyes"],
+ [ac_enable_checking=yes])
+
+# There is little point in enabling checking in the build machine if
+# cross-compiling for Android.
+AS_IF([test -z "$with_android" || test -n "$XCONFIGURE"],[
+ if test x$ac_enable_checking != x ; then
+ AC_DEFINE([ENABLE_CHECKING], [1],
+ [Define to 1 if expensive run-time data type and consistency checks are enabled.])
+ fi
+ if $CHECK_STRUCTS; then
+ AC_DEFINE([CHECK_STRUCTS], [1],
+ [Define this to check whether someone updated the portable dumper
+ code after changing the layout of a structure that it uses.
+ If you change one of these structures, check that the pdumper.c
+ code is still valid, and update the pertinent hash in pdumper.c
+ by manually copying the hash from the newly-generated dmpstruct.h.])
+ fi
+ AC_SUBST([CHECK_STRUCTS])
+ if test x$ac_gc_check_stringbytes != x ; then
+ AC_DEFINE([GC_CHECK_STRING_BYTES], [1],
+ [Define this temporarily to hunt a bug. If defined, the size of
+ strings is redundantly recorded in sdata structures so that it can
+ be compared to the sizes recorded in Lisp strings.])
+ fi
+ if test x$ac_gc_check_string_overrun != x ; then
+ AC_DEFINE([GC_CHECK_STRING_OVERRUN], [1],
+ [Define this to check for short string overrun.])
+ fi
+ if test x$ac_gc_check_string_free_list != x ; then
+ AC_DEFINE([GC_CHECK_STRING_FREE_LIST], [1],
+ [Define this to check the string free list.])
+ fi
+ if test x$ac_glyphs_debug != x ; then
+ AC_DEFINE([GLYPH_DEBUG], [1],
+ [Define this to enable glyphs debugging code.])
+ fi
+],[AS_IF([test "x$ac_enable_checking" != x],
+ [android_enable_checking=yes
+ export android_enable_checking])])
dnl The name of this option is unfortunate. It predates, and has no
dnl relation to, the "sampling-based elisp profiler" added in 24.3.
@@ -682,6 +794,527 @@ AC_ARG_ENABLE([build-details],
[test "$enableval" = no && BUILD_DETAILS=--no-build-details])
AC_SUBST([BUILD_DETAILS])
+# JAVA_PUSH_LINT(OPT)
+# -------------------
+# Check if javac supports the diagnostic flag -Xlint:OPT.
+# If it does, add it to WARN_JAVAFLAGS.
+
+AC_DEFUN([JAVA_PUSH_LINT],
+[
+ AC_CACHE_CHECK([whether Java compiler accepts -Xlint:$1],
+ [emacs_cv_javac_knows_lint_$1],
+ AS_IF([rm -f conftest.class
+cat << EOF > conftest.java
+
+class conftest
+{
+
+}
+
+EOF
+("$JAVAC" -Xlint:$1 conftest.java 2>&AS_MESSAGE_LOG_FD) \
+ && rm -f conftest.class], [emacs_cv_javac_knows_lint_$1=yes],
+ [emacs_cv_javac_knows_lint_$1=no]))
+
+ AS_IF([test "$emacs_cv_javac_knows_lint_$1" = "yes"],
+ [WARN_JAVAFLAGS="$WARN_JAVAFLAGS -Xlint:$1"])
+])
+
+# Start Android configuration. This is done in three steps:
+
+# First, the SDK tools needed to build the Android package on the host
+# are found.
+
+# Then, configure is called inside itself with the NDK C and C++
+# compilers, and the Makefiles generated, along with config.h, are
+# renamed to end with .android.
+
+# Finally, configure continues to configure the Emacs binary that will
+# run on the host.
+
+ANDROID=
+JAVAC=
+AAPT=
+JARSIGNER=
+APKSIGNER=
+ZIPALIGN=
+DX=
+ANDROID_JAR=
+ANDROID_ABI=
+WARN_JAVAFLAGS=
+ANDROID_SHARED_USER_ID=
+
+# This is a list of Makefiles that have alternative versions for
+# Android.
+android_makefiles="lib/Makefile lib/gnulib.mk lib-src/Makefile src/Makefile"
+
+# This is whether or not to package mailutils into the executable.
+emacs_use_mailutils=
+
+AC_ARG_VAR([ANDROID_CC], [The Android C cross-compiler.])
+AC_ARG_VAR([SDK_BUILD_TOOLS], [Name of directory holding Android SDK build-tools.])
+AC_ARG_VAR([ANDROID_CFLAGS], [Flags given to the Android C cross-compiler.])
+AC_ARG_VAR([JAVAC], [Java compiler path. Used for Android.])
+AC_ARG_VAR([JARSIGNER], [Java package signer path. Used for Android.])
+AC_ARG_VAR([APKSIGNER], [Android package signer path. Used for Android.])
+AC_ARG_VAR([SDK_BUILD_TOOLS], [Path to the Android SDK build tools.])
+
+if test "$with_android" = "yes"; then
+ AC_MSG_ERROR([Please specify the path to the Android.jar file, like so:
+
+ ./configure --with-android=/path/to/android.jar
+
+along with the path to the SDK build-tools (this is the directory with
+tools such as aapt, dx, and aidl):
+
+ SDK_BUILD_TOOLS=/path/to/sdk-build-tools
+
+The cross-compiler should then be specified:
+
+ 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
+ AC_CHECK_PROGS([JAVAC], [javac])
+ if test "$JAVAC" = ""; then
+ AC_MSG_ERROR([The Java compiler required to build Emacs was not found.
+Please make sure `javac' can be found on your path, or alternatively specify
+the path to your Java compiler before configuring Emacs, like so:
+
+ JAVAC=/opt/jdk/bin/javac ./configure --with-android])
+ fi
+
+ AC_CHECK_PROGS([JARSIGNER], [jarsigner])
+ if test "$JARSIGNER" = ""; then
+ AC_MSG_ERROR([The Java package signing utility was not found.
+Please make sure `jarsigner' can be found on your path, or alternatively
+specify its location before configuring Emacs, like so:
+
+ JARSIGNER=/opt/jdk/bin/jarsigner ./configure --with-android])
+ fi
+
+ AC_CACHE_CHECK([whether the Java compiler works],
+ [emacs_cv_working_javac],
+ AS_IF([rm -f conftest.class
+cat << EOF > conftest.java
+
+import android.app.Activity;
+import android.os.Bundle;
+
+class conftest extends Activity
+{
+ @Override
+ public void
+ onCreate (Bundle savedInstanceState)
+ {
+ super.onCreate (savedInstanceState);
+ }
+}
+
+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_working_javac=yes],
+ [emacs_cv_working_javac=no]))
+
+ if test "$emacs_cv_working_javac" = "no"; then
+ AC_MSG_ERROR([The Java compiler does not work, or you did not specify
+a valid path to android.jar. See config.log for more details.])
+ fi
+
+ AC_CACHE_CHECK([whether 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.TIRAMISU;
+}
+
+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 (Tiramisu) or later.])
+ fi
+
+ dnl See if the Java compiler supports the `--release' option which
+ dnl makes it check for and prevent using features introduced after
+ dnl Java 1.7.
+
+ AC_CACHE_CHECK([whether javac accepts --release 7],
+ [emacs_cv_javac_release_7], AS_IF([rm -f conftest.class
+cat << EOF > conftest.java
+
+class conftest
+{
+
+}
+
+EOF
+("$JAVAC" --release 7 conftest.java 2>&AS_MESSAGE_LOG_FD) \
+ && rm -f conftest.class],
+ [emacs_cv_javac_release_7=yes],
+ [emacs_cv_javac_release_7=no]))
+
+ if test "$emacs_cv_javac_release_7" = "yes"; then
+ WARN_JAVAFLAGS="$WARN_JAVAFLAGS --release 7"
+ else
+ dnl If not, just make sure the generated bytecode is correct.
+ WARN_JAVAFLAGS="$WARN_JAVAFLAGS -target 1.7 -source 1.7"
+ fi
+
+ dnl Enable some useful Java linting options.
+ JAVA_PUSH_LINT([deprecation])
+ JAVA_PUSH_LINT([cast])
+ JAVA_PUSH_LINT([divzero])
+ JAVA_PUSH_LINT([nonempty])
+ JAVA_PUSH_LINT([empty])
+ JAVA_PUSH_LINT([finally])
+ JAVA_PUSH_LINT([overrides])
+ JAVA_PUSH_LINT([path])
+ JAVA_PUSH_LINT([serial])
+ JAVA_PUSH_LINT([unchecked])
+
+ # Get the name of the android.jar file.
+ ANDROID_JAR="$with_android"
+
+ # Substitute this into java/Makefile.
+ AC_SUBST([WARN_JAVAFLAGS])
+
+ AC_PATH_PROGS([AAPT], [aapt], [], "${SDK_BUILD_TOOLS}:$PATH")
+ if test "$AAPT" = ""; then
+ AC_MSG_ERROR([The Android asset packaging tool was not found.
+Please verify that the path to the SDK build tools you specified is correct])
+ fi
+
+ AC_PATH_PROGS([APKSIGNER], [apksigner], [], "${SDK_BUILD_TOOLS}:$PATH")
+ if test "$APKSIGNER" = ""; then
+ AC_MSG_ERROR([The Android package signing tool was not found.
+Please verify that the path to the SDK build tools you specified is correct])
+ fi
+
+ AC_PATH_PROGS([D8], [d8], [], "${SDK_BUILD_TOOLS}:$PATH")
+ if test "D8" = ""; then
+ AC_MSG_ERROR([The Android dexer was not found.
+Please verify that the path to the SDK build tools you specified is correct])
+ fi
+
+ AC_PATH_PROGS([ZIPALIGN], [zipalign], [], "${SDK_BUILD_TOOLS}:$PATH")
+ if test "ZIPALIGN" = ""; then
+ AC_MSG_ERROR([The Android ZIP archive alignment utility was not found.
+Please verify that the path to the SDK build tools you specified is correct]);
+ fi
+
+ dnl Now configure Emacs to generate binaries for Android. After the
+ dnl configuration completes, move the generated Makefiles.
+
+ if test "$ANDROID_CC" = ""; then
+ AC_MSG_ERROR([Please specify the path to the Android cross-compiler
+for your machine. For example:
+
+ ANDROID_CC=/path/to/armv7a-linux-androideabi19-clang \\
+ ./configure --with-android])
+ fi
+
+ dnl Obtain the cross compiler's target to find out where binaries go
+ dnl in the resulting package.
+
+ 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
+ ;;
+ *aarch64*) android_abi=arm64-v8a
+ ;;
+ *arm*v7a*) android_abi=armeabi-v7a
+ ;;
+ *mips64*) android_abi=mips64
+ ;;
+ *mips*) android_abi=mips
+ ;;
+ *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.
+
+The compiler target is: $cc_target])
+ ;;
+ esac
+ AC_MSG_RESULT([$android_abi])
+
+ ANDROID_ABI=$android_abi
+
+ dnl Obtain the minimum SDK version of the resulting Emacs binary
+ dnl built with this NDK.
+
+ ANDROID_MIN_SDK=8
+ AC_MSG_CHECKING([for the lowest Android version Emacs can run on])
+ [android_sdk=`echo "$cc_target" | grep -oE 'android([0-9][0-9]?)'`]
+
+ if test -n "$android_sdk"; then
+ android_sdk=`echo "$android_sdk" | sed -n 's/android//p'`
+ AC_MSG_RESULT([$android_sdk])
+ ANDROID_MIN_SDK=$android_sdk
+ else
+ # 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" -le "18"; then
+ ANDROID_SDK_18_OR_EARLIER=yes
+ fi
+ AC_SUBST([ANDROID_SDK_18_OR_EARLIER])
+
+ # Likewise for Android 2.2.
+ ANDROID_SDK_8_OR_EARLIER=
+ if test "$android_sdk" -le "8"; then
+ ANDROID_SDK_8_OR_EARLIER=yes
+ fi
+ AC_SUBST([ANDROID_SDK_8_OR_EARLIER])
+
+ # Save confdefs.h and config.log for now.
+ mv -f confdefs.h _confdefs.h
+ mv -f config.log _config.log
+
+ # Make sure these files are removed upon exit.
+ trap "rm -rf _confdefs.h _config.log" 0
+
+ # Figure out what --with-FOO options to pass through.
+ passthrough="$passthrough --with-png=$with_png"
+ passthrough="$passthrough --with-webp=$with_webp"
+ passthrough="$passthrough --with-gif=$with_gif"
+ passthrough="$passthrough --with-json=$with_json"
+ passthrough="$passthrough --with-jpeg=$with_jpeg"
+ passthrough="$passthrough --with-xml2=$with_xml2"
+ passthrough="$passthrough --with-sqlite3=$with_sqlite3"
+ passthrough="$passthrough --with-gnutls=$with_gnutls"
+ passthrough="$passthrough --with-tiff=$with_tiff"
+ passthrough="$passthrough --with-selinux=$with_selinux"
+ passthrough="$passthrough --with-modules=$with_modules"
+ passthrough="$passthrough --with-tree-sitter=$with_tree_sitter"
+ passthrough="$passthrough --with-imagemagick=$with_imagemagick"
+ passthrough="$passthrough --with-lcms2=$with_lcms2"
+ passthrough="$passthrough --with-mailutils=$with_mailutils"
+ passthrough="$passthrough --with-pop=$with_pop"
+ passthrough="$passthrough --with-harfbuzz=$with_harfbuzz"
+
+ AS_IF([test "x$with_mailutils" = "xyes"], [emacs_use_mailutils=yes])
+ AC_SUBST([emacs_use_mailutils])
+
+ AS_IF([XCONFIGURE=android ANDROID_CC="$ANDROID_CC" \
+ ANDROID_SDK="$android_sdk" android_abi=$android_abi \
+ android_ndk_path="$with_ndk_path" \
+ android_ndk_cxx_shared="$with_ndk_cxx_shared" \
+ android_ndk_cxx="$android_ndk_cxx" \
+ $CONFIG_SHELL $0 $passthrough], [],
+ [AC_MSG_ERROR([Failed to cross-configure Emacs for android.])])
+
+ # Now set ANDROID to yes.
+ ANDROID=yes
+
+ for makefile in $android_makefiles; do
+ AC_MSG_NOTICE([Generating $makefile.android])
+ mv -f "$makefile" "$makefile.android"
+ done
+
+ AC_MSG_NOTICE([Generating src/config.h.android])
+ mv -f src/config.h src/config.h.android
+
+ # Tell AndroidManifest.xml whether or not Emacs should be built
+ # debug.
+ ANDROID_DEBUGGABLE=false
+ if test "$with_android_debug" = "yes"; then
+ ANDROID_DEBUGGABLE=true
+ fi
+ AC_SUBST([ANDROID_DEBUGGABLE])
+
+ # Move confdefs.h back now that the recursive call to configure is
+ # complete.
+ mv -f _confdefs.h confdefs.h
+
+ # Move the Android config.log to config.log.android. */
+ mv -f config.log config.log.android
+
+ # And _config.log back.
+ mv -f _config.log config.log
+fi
+
+AC_SUBST([ANDROID])
+AC_SUBST([JAVAC])
+AC_SUBST([AAPT])
+AC_SUBST([D8])
+AC_SUBST([ZIPALIGN])
+AC_SUBST([ANDROID_JAR])
+AC_SUBST([ANDROID_ABI])
+
+if test "$XCONFIGURE" = "android"; then
+ ANDROID=yes
+
+ # Enable cross compiling.
+ cross_compiling=yes
+fi
+
+AC_SUBST([XCONFIGURE])
+
+if test "$ANDROID" = "yes"; then
+ # When --with-android is specified, almost all build options must be
+ # disabled, both within the recursive invocation of configure and
+ # outside.
+ with_xpm=no
+
+ # Some of these dependencies are now supported within Android, so
+ # they can be enabled.
+ if test "$XCONFIGURE" != "android"; then
+ with_png=no
+ with_webp=no
+ with_gif=no
+ with_json=no
+ with_jpeg=no
+ with_xml2=no
+ with_sqlite3=no
+ with_gnutls=no
+ with_tiff=no
+ with_selinux=no
+ with_modules=no
+ with_tree_sitter=no
+ with_imagemagick=no
+ with_lcms2=no
+ with_mailutils=no
+ with_pop=no
+ with_harfbuzz=no
+ fi
+
+ with_rsvg=no
+ with_libsystemd=no
+ with_cairo=no
+ with_xft=no
+ with_libotf=no
+ with_gpm=no
+ with_dbus=no
+ with_gsettings=no
+ with_threads=no
+ with_ns=no
+
+ # zlib is available in android.
+fi
+
dnl This used to use changequote, but, apart from 'changequote is evil'
dnl per the autoconf manual, we can speed up autoconf somewhat by quoting
dnl the great gob of text. Thus it's not processed for possible expansion.
@@ -705,6 +1338,11 @@ dnl quotation begins
opsys='' unported=no
case "${canonical}" in
+ ## Android
+ *linux-android* )
+ opsys=android
+ ;;
+
## GNU/Linux and similar ports
*-*-linux* )
opsys=gnu-linux
@@ -871,6 +1509,7 @@ AC_DEFUN([_AC_PROG_CC_C89], [$2])
dnl Sets GCC=yes if using gcc.
AC_PROG_CC([gcc cc cl clang "$XCRUN gcc" "$XCRUN clang"])
+
if test -n "$XCRUN"; then
AC_CHECK_PROGS([AR], [ar "$XCRUN ar"])
test -n "$AR" && export AR
@@ -904,6 +1543,7 @@ AC_DEFUN([gt_TYPE_WINT_T],
# Initialize gnulib right after choosing the compiler.
dnl Amongst other things, this sets AR and ARFLAGS.
gl_EARLY
+ndk_LATE
if test "$ac_test_CFLAGS" != set; then
# It's helpful to have C macros available to GDB, so prefer -g3 to -g
@@ -1123,6 +1763,13 @@ AS_IF([test $gl_gcc_warnings = no],
nw="$nw -Wsuggest-attribute=format"
fi
+ # If Emacs is being built for Android and many functions are
+ # currently stubbed out for operation on the build machine, disable
+ # -Wsuggest-attribute=noreturn.
+
+ AS_IF([test "$ANDROID" = "yes"],
+ [nw="$nw -Wsuggest-attribute=noreturn"])
+
gl_MANYWARN_ALL_GCC([ws])
gl_MANYWARN_COMPLEMENT([ws], [$ws], [$nw])
for w in $ws; do
@@ -1146,6 +1793,7 @@ AS_IF([test $gl_gcc_warnings = no],
gl_WARN_ADD([-Wno-null-pointer-arithmetic])
gl_WARN_ADD([-Wno-implicit-const-int-float-conversion])
gl_WARN_ADD([-Wno-int-in-bool-context])
+ gl_WARN_ADD([-Wno-shift-overflow])
fi
# This causes too much noise in the MinGW build
@@ -1268,7 +1916,7 @@ else
AM_DEFAULT_VERBOSITY=0
fi
AC_SUBST([AM_DEFAULT_VERBOSITY])
-AC_CONFIG_FILES([src/verbose.mk])
+ARCH_INDEPENDENT_CONFIG_FILES([src/verbose.mk])
dnl Some other nice autoconf tests.
AC_PROG_INSTALL
@@ -1693,7 +2341,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);
@@ -1762,11 +2409,18 @@ AC_DEFINE_UNQUOTED([SYSTEM_TYPE], ["$SYSTEM_TYPE"],
[The type of system you are compiling for; sets 'system-type'.])
AC_SUBST([SYSTEM_TYPE])
+# Check for pw_gecos in struct passwd; this is known to be missing on
+# Android.
+
+AC_CHECK_MEMBERS([struct passwd.pw_gecos], [], [], [#include <pwd.h>])
pre_PKG_CONFIG_CFLAGS=$CFLAGS
pre_PKG_CONFIG_LIBS=$LIBS
-PKG_PROG_PKG_CONFIG([0.9.0])
+dnl pkg-config does not work when cross-compiling for Android.
+if test "${ANDROID}" != "yes"; then
+ PKG_PROG_PKG_CONFIG([0.9.0])
+fi
dnl EMACS_CHECK_MODULES([GSTUFF], [gtk+-2.0 >= 1.3 glib = 1.3.4])
dnl acts like PKG_CHECK_MODULES([GSTUFF], [gtk+-2.0 >= 1.3 glib = 1.3.4],
@@ -1776,10 +2430,25 @@ dnl EMACS_CHECK_MODULES accepts optional 3rd and 4th arguments that
dnl can take the place of the default HAVE_GSTUFF=yes and HAVE_GSTUFF=no
dnl actions.
AC_DEFUN([EMACS_CHECK_MODULES],
- [PKG_CHECK_MODULES([$1], [$2],
- [$1_CFLAGS=`AS_ECHO(["$$1_CFLAGS"]) | sed -e "$edit_cflags"`
- m4_default([$3], [HAVE_$1=yes])],
- [m4_default([$4], [HAVE_$1=no])])])
+ [AS_IF([test -n "$ndk_INITIALIZED"],
+ [ndk_CHECK_MODULES([$1], [$2], m4_default([$3], [HAVE_$1=yes]),
+ m4_default([$4],[HAVE_$1=no]))],
+ [PKG_CHECK_MODULES([$1], [$2],
+ [$1_CFLAGS=`AS_ECHO(["$$1_CFLAGS"]) | sed -e "$edit_cflags"`
+ m4_default([$3], [HAVE_$1=yes])],
+ [m4_default([$4], [HAVE_$1=no])])])])
+
+dnl EMACS_CHECK_LIB(NAME, FUNCTION, ACTION-IF-FOUND, ACTION-IF-NOT-FOUND,
+dnl OTHER-LIBRARIES, INCLUDES)
+dnl ---------------------------------------------------------------------
+dnl This is like AC_CHECK_LIB; however, there is no default action, and
+dnl when cross-configuring for Android, AC_CHECK_DECLS is called with NAME
+dnl and INCLUDES instead, as the library being checked against will likely
+dnl be built together with Emacs.
+AC_DEFUN([EMACS_CHECK_LIB],
+ [AS_IF([test -n "$ndk_INITIALIZED"],
+ [AC_CHECK_DECL([$2], [$3], [$4], [$6])],
+ [AC_CHECK_LIB([$1], [$2], [$3], [$4], [$5])])])
HAVE_SOUND=no
if test "${with_sound}" != "no"; then
@@ -1949,14 +2618,87 @@ AC_SUBST([AUTO_DEPEND])
window_system=none
+ANDROID_OBJ=
+ANDROID_LIBS=
+# ANDROID_CFLAGS is a precious variable used to pass information to
+# the cross-compiler.
+ANDROID_BUILD_CFLAGS=
+REALLY_ANDROID=
+CM_OBJ="cm.o"
+
+AS_IF([test "$ANDROID" = "yes"],[
+ window_system=android
+ no_x=yes
+ ANDROID_OBJ="androidterm.o androidfns.o androidfont.o androidmenu.o"
+ ANDROID_OBJ="$ANDROID_OBJ android.o"
+ ANDROID_LIBS=
+ CM_OBJ=
+
+ AC_DEFINE([HAVE_ANDROID], [1], [Define to 1 if Emacs is being built
+with Android support])
+
+ AS_IF([test "$XCONFIGURE" != "android"], [
+ AC_DEFINE([ANDROID_STUBIFY], [1], [Define to 1 if Emacs is being built
+for Android, but all API calls need to be stubbed out])
+
+ # Now set any shared user ID that was specified.
+ AS_IF([test -n "$with_shared_user_id"],
+ [emacs_val=$with_shared_user_id
+ emacs_val=`AS_ECHO(["$with_shared_user_id"]) \
+ | sed -e 's/"/\\"/'`
+ emacs_val="\"$emacs_val\""
+ ANDROID_SHARED_USER_ID="android:sharedUserId=$emacs_val"])],[
+ # Emacs will be built as a shared library, and a wrapper around it
+ # will also be built for the benefit of applications. This
+ # requires Emacs be built as a position independent executable.
+ ANDROID_BUILD_CFLAGS="-fPIC -fvisibility=hidden"
+
+ # Graphics code in sfntfont-android.c benefits heavily from
+ # vectorization.
+ ANDROID_BUILD_CFLAGS="$ANDROID_BUILD_CFLAGS -ftree-vectorize"
+
+ # Link with libraries required for Android support.
+ # API 9 and later require `-landroid' for the asset manager.
+ # API 8 uses an emulation via the JNI.
+ AS_IF([test "$ANDROID_SDK" -lt "9"],
+ [ANDROID_LIBS="-llog -ljnigraphics"],
+ [ANDROID_LIBS="-landroid -llog -ljnigraphics"])
+
+ # This is required to make the system load emacs.apk's libpng
+ # (among others) instead of the system's own. But it doesn't work
+ # on all Android versions yet, so for now just suffix shared
+ # libraries with _emacs.
+ # ANDROID_LDFLAGS="-Wl,-rpath,'\$\$ORIGIN'"
+
+ # 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"
+
+ # Build androidselect.o.
+ ANDROID_OBJ="$ANDROID_OBJ androidselect.o"
+
+ # Check for some functions not always present in the NDK.
+ AC_CHECK_DECLS([android_get_device_api_level])
+
+ # Say this build is really for Android.
+ REALLY_ANDROID=yes])])
+
+AC_SUBST([ANDROID])
+AC_SUBST([ANDROID_OBJ])
+AC_SUBST([ANDROID_LIBS])
+AC_SUBST([ANDROID_LDFLAGS])
+AC_SUBST([ANDROID_BUILD_CFLAGS])
+AC_SUBST([ANDROID_SHARED_USER_ID])
+
if test "${with_pgtk}" = "yes"; then
window_system=pgtk
fi
-
-AC_PATH_X
-if test "$no_x" != yes && test "${with_pgtk}" != "yes"; then
- window_system=x11
+if test "${ANDROID}" != "yes"; then
+ AC_PATH_X
+ if test "$no_x" != yes && test "${with_pgtk}" != "yes"; then
+ window_system=x11
+ fi
fi
LD_SWITCH_X_SITE_RPATH=
@@ -2272,7 +3014,6 @@ NTDIR=
LIBS_ECLIENT=
LIB_WSOCK32=
NTLIB=
-CM_OBJ="cm.o"
XARGS_LIMIT=
if test "${HAVE_W32}" = "yes"; then
AC_DEFINE([HAVE_NTGUI], [1], [Define to use native MS Windows GUI.])
@@ -2435,7 +3176,11 @@ dnl use the toolkit if we have gtk, or X11R5 or newer.
haiku )
term_header=haikuterm.h
;;
+ android )
+ term_header=androidterm.h
+ ;;
esac
+
AC_SUBST([HAVE_PGTK])
if test "$window_system" = none && test "X$with_x" != "Xno"; then
@@ -2736,7 +3481,6 @@ fail;
fi
fi
-
### Use -lrsvg-2 if available, unless '--with-rsvg=no' is specified.
HAVE_RSVG=no
if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" \
@@ -2766,7 +3510,8 @@ HAVE_WEBP=no
if test "${with_webp}" != "no"; then
if test "${HAVE_X11}" = "yes" || test "${opsys}" = "mingw32" \
|| test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes" \
- || test "${HAVE_BE_APP}" = "yes" || test "${HAVE_PGTK}" = "yes"; then
+ || test "${HAVE_BE_APP}" = "yes" || test "${HAVE_PGTK}" = "yes" \
+ || test "${REALLY_ANDROID}" = "yes"; then
WEBP_REQUIRED=0.6.0
WEBP_MODULE="libwebpdemux >= $WEBP_REQUIRED"
@@ -2780,12 +3525,13 @@ if test "${with_webp}" != "no"; then
CFLAGS="$CFLAGS $WEBP_CFLAGS"
LIBS="$LIBS $WEBP_LIBS"
- AC_CHECK_FUNC([WebPGetInfo], [],
- [WEBP_MODULE="$WEBP_MODULE libwebpdecoder >= $WEBP_REQUIRED"
- HAVE_WEBP=no
- AS_UNSET([WEBP_LIBS])
- AS_UNSET([WEBP_CFLAGS])
- EMACS_CHECK_MODULES([WEBP], [$WEBP_MODULE])])
+ AS_IF([test "$REALLY_ANDROID" != "yes"], [
+ AC_CHECK_FUNC([WebPGetInfo], [],
+ [WEBP_MODULE="$WEBP_MODULE libwebpdecoder >= $WEBP_REQUIRED"
+ HAVE_WEBP=no
+ AS_UNSET([WEBP_LIBS])
+ AS_UNSET([WEBP_CFLAGS])
+ EMACS_CHECK_MODULES([WEBP], [$WEBP_MODULE])])])
CFLAGS=$OLD_CFLAGS
LIBS=$OLD_LIBS
@@ -2795,7 +3541,6 @@ if test "${with_webp}" != "no"; then
fi
if test $HAVE_WEBP = yes; then
AC_DEFINE([HAVE_WEBP], [1], [Define to 1 if using libwebp.])
- CFLAGS="$CFLAGS $WEBP_CFLAGS"
# Windows loads libwebp dynamically
if test "${opsys}" = "mingw32"; then
WEBP_LIBS=
@@ -2805,33 +3550,53 @@ fi
### Use -lsqlite3 if available, unless '--with-sqlite3=no'
HAVE_SQLITE3=no
+SQLITE3_LIBS=
+SQLITE3_CFLAGS=
if test "${with_sqlite3}" != "no"; then
- AC_CHECK_LIB([sqlite3], [sqlite3_open_v2],
- [HAVE_SQLITE3=yes],
- [HAVE_SQLITE3=no])
- if test "$HAVE_SQLITE3" = "yes"; then
- SQLITE3_LIBS=-lsqlite3
- AC_SUBST([SQLITE3_LIBS])
- LIBS="$SQLITE3_LIBS $LIBS"
- AC_DEFINE([HAVE_SQLITE3], [1],
- [Define to 1 if you have the libsqlite3 library (-lsqlite).])
- # Windows loads libsqlite dynamically
- if test "${opsys}" = "mingw32"; then
- SQLITE3_LIBS=
+ if test "${REALLY_ANDROID}" = "yes"; then
+ ndk_SEARCH_MODULE([sqlite3], [SQLITE3], [HAVE_SQLITE3=yes])
+
+ if test "$HAVE_SQLITE3" = "yes"; then
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $SQLITE3_CFLAGS"
+ AC_CHECK_DECL([sqlite3_open_v2], [HAVE_SQLITE=yes],
+ [HAVE_SQLITE3=no], [#include <sqlite3.h>])
+ CFLAGS="$SAVE_CFLAGS"
fi
- AC_CHECK_LIB([sqlite3], [sqlite3_load_extension],
- [HAVE_SQLITE3_LOAD_EXTENSION=yes],
- [HAVE_SQLITE3_LOAD_EXTENSION=no])
- if test "$HAVE_SQLITE3_LOAD_EXTENSION" = "yes"; then
- AC_DEFINE([HAVE_SQLITE3_LOAD_EXTENSION], [1],
- [Define to 1 if sqlite3 supports loading extensions.])
+ else
+ AC_CHECK_LIB([sqlite3], [sqlite3_open_v2],
+ [HAVE_SQLITE3=yes],
+ [HAVE_SQLITE3=no])
+ if test "$HAVE_SQLITE3" = "yes"; then
+ SQLITE3_LIBS=-lsqlite3
+ LIBS="$SQLITE3_LIBS $LIBS"
+ # Windows loads libsqlite dynamically
+ if test "${opsys}" = "mingw32"; then
+ SQLITE3_LIBS=
+ fi
+ AC_CHECK_LIB([sqlite3], [sqlite3_load_extension],
+ [HAVE_SQLITE3_LOAD_EXTENSION=yes],
+ [HAVE_SQLITE3_LOAD_EXTENSION=no])
+ if test "$HAVE_SQLITE3_LOAD_EXTENSION" = "yes"; then
+ AC_DEFINE([HAVE_SQLITE3_LOAD_EXTENSION], [1],
+ [Define to 1 if sqlite3 supports loading extensions.])
+ fi
fi
- fi
+ fi
+
+ if test "$HAVE_SQLITE3" = "yes"; then
+ AC_DEFINE([HAVE_SQLITE3], [1],
+ [Define to 1 if you have the libsqlite3 library (-lsqlite).])
+ fi
fi
+AC_SUBST([SQLITE3_LIBS])
+AC_SUBST([SQLITE3_CFLAGS])
+
HAVE_IMAGEMAGICK=no
if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test "${HAVE_W32}" = "yes" || \
- test "${HAVE_BE_APP}" = "yes" || test "${window_system}" = "pgtk"; then
+ test "${HAVE_BE_APP}" = "yes" || test "${window_system}" = "pgtk" || \
+ test "${REALLY_ANDROID}" = "yes"; then
if test "${with_imagemagick}" != "no"; then
if test -n "$BREW"; then
# Homebrew doesn't link ImageMagick 6 by default, so make sure
@@ -2854,14 +3619,23 @@ if test "${HAVE_X11}" = "yes" || test "${HAVE_NS}" = "yes" || test "${HAVE_W32}"
OLD_LIBS=$LIBS
CFLAGS="$CFLAGS $IMAGEMAGICK_CFLAGS"
LIBS="$IMAGEMAGICK_LIBS $LIBS"
- AC_CHECK_FUNCS([MagickRelinquishMemory MagickExportImagePixels \
- MagickMergeImageLayers MagickAutoOrientImage])
+ if test "$REALLY_ANDROID" != "yes"; then
+ AC_CHECK_FUNCS([MagickRelinquishMemory MagickExportImagePixels \
+ MagickMergeImageLayers MagickAutoOrientImage])
+ else
+ # AC_CHECK_FUNCS doesn't work for Android dependencies because
+ # they are built alongside Emacs.
+ AC_CHECK_DECLS([MagickRelinquishMemory,MagickExportImagePixels,
+MagickMergeImageLayers,MagickAutoOrientImage],
+ [], [], [#include <MagickWand/MagickWand.h>])
+ fi
CFLAGS=$OLD_CFLAGS
LIBS=$OLD_LIBS
# Check that ImageMagick links. It does not link on Fedora 25
# with './configure CC=clang', as pkg-config outputs flags like
# -lomp that work for GCC but not Clang.
- if test "$ac_cv_func_MagickRelinquishMemory" != yes; then
+ if test "$ac_cv_func_MagickRelinquishMemory" != yes \
+ && test "$REALLY_ANDROID" != "yes"; then
HAVE_IMAGEMAGICK=no
fi
fi
@@ -3135,19 +3909,29 @@ if test "${HAVE_GTK}" = "yes"; then
fi
AC_SUBST([USE_STARTUP_NOTIFICATION])
-dnl SELinux is available for GNU/Linux only.
+dnl SELinux is available for Linux kernel based systems only.
+dnl These include GNU/Linux and Android.
HAVE_LIBSELINUX=no
LIBSELINUX_LIBS=
+LIBSELINUX_CFLAGS=
if test "${with_selinux}" = "yes"; then
- AC_CHECK_LIB([selinux], [lgetfilecon],
- [HAVE_LIBSELINUX=yes],
- [HAVE_LIBSELINUX=no])
+ if test "$REALLY_ANDROID" = "yes"; then
+ ndk_SEARCH_MODULE([libselinux], [LIBSELINUX],
+ [HAVE_LIBSELINUX=yes])
+ else
+ AC_CHECK_LIB([selinux], [lgetfilecon],
+ [HAVE_LIBSELINUX=yes],
+ [HAVE_LIBSELINUX=no])
+ fi
if test "$HAVE_LIBSELINUX" = yes; then
AC_DEFINE([HAVE_LIBSELINUX], [1], [Define to 1 if using SELinux.])
- LIBSELINUX_LIBS=-lselinux
+ if test "$REALLY_ANDROID" != "yes"; then
+ LIBSELINUX_LIBS=-lselinux
+ fi
fi
fi
AC_SUBST([LIBSELINUX_LIBS])
+AC_SUBST([LIBSELINUX_CFLAGS])
HAVE_GNUTLS=no
if test "${with_gnutls}" != "no" ; then
@@ -3835,17 +4619,23 @@ else
fi
if test "${HAVE_X11}" = "yes" && test "${HAVE_FREETYPE}" = "yes" \
|| test "$window_system" = "pgtk" \
- || test "${HAVE_W32}" = "yes"; then
+ || test "${HAVE_W32}" = "yes" \
+ || test "$REALLY_ANDROID" = "yes"; then
if test "${with_harfbuzz}" != "no"; then
EMACS_CHECK_MODULES([HARFBUZZ], [harfbuzz >= $harfbuzz_required_ver])
- if test "$HAVE_HARFBUZZ" = "yes"; then
+ AS_IF([test "$HAVE_HARFBUZZ" = "yes"],[
AC_DEFINE([HAVE_HARFBUZZ], [1], [Define to 1 if using HarfBuzz.])
### mingw32 and Cygwin-w32 don't use -lharfbuzz, since they load
### the library dynamically.
- if test "${HAVE_W32}" = "yes"; then
- HARFBUZZ_LIBS=
- fi
- fi
+ AS_IF([test "${HAVE_W32}" = "yes"], [HARFBUZZ_LIBS=])
+ ## Now check for `hb_font_set_var_named_instance'.
+ OLD_CFLAGS=$CFLAGS
+ CFLAGS="$HARFBUZZ_CFLAGS $CFLAGS"
+ EMACS_CHECK_LIB([harfbuzz], [hb_font_set_var_named_instance],
+ [AC_DEFINE([HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE], [1],
+ [Define to 1 if `hb_font_set_var_named_instance' is present.])],
+ [], [$HARFBUZZ_LIBS], [#include <hb.h>])
+ CFLAGS=$OLD_CFLAGS])
fi
fi
@@ -4028,51 +4818,66 @@ AC_SUBST([LIBXPM])
### Use -ljpeg if available, unless '--with-jpeg=no'.
HAVE_JPEG=no
LIBJPEG=
+JPEG_CFLAGS=
if test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
|| test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes" \
- || test "$window_system" = "pgtk"; then
+ || test "$window_system" = "pgtk" \
+ || test "${REALLY_ANDROID}" = "yes"; then
if test "${with_jpeg}" != "no"; then
- AC_CACHE_CHECK([for jpeglib 6b or later],
- [emacs_cv_jpeglib],
- [OLD_LIBS=$LIBS
- for emacs_cv_jpeglib in yes -ljpeg no; do
- case $emacs_cv_jpeglib in
- yes) ;;
- no) break;;
- *) LIBS="$LIBS $emacs_cv_jpeglib";;
- esac
- AC_LINK_IFELSE(
- [AC_LANG_PROGRAM(
- [[#undef HAVE_STDLIB_H /* Avoid config.h/jpeglib.h collision. */
- #include <stdio.h> /* jpeglib.h needs FILE and size_t. */
- #include <jpeglib.h>
- #include <jerror.h>
- char verify[JPEG_LIB_VERSION < 62 ? -1 : 1];
- struct jpeg_decompress_struct cinfo;
- ]],
- [[
- jpeg_create_decompress (&cinfo);
- WARNMS (&cinfo, JWRN_JPEG_EOF);
- jpeg_destroy_decompress (&cinfo);
- ]])],
- [emacs_link_ok=yes],
- [emacs_link_ok=no])
- LIBS=$OLD_LIBS
- test $emacs_link_ok = yes && break
- done])
- if test "$emacs_cv_jpeglib" != no; then
- HAVE_JPEG=yes
- AC_DEFINE([HAVE_JPEG], [1],
- [Define to 1 if you have the jpeg library (typically -ljpeg).])
- ### mingw32 doesn't use -ljpeg, since it loads the library
- ### dynamically when needed, and doesn't want a run-time
- ### dependency on the jpeglib DLL.
- test "$emacs_cv_jpeglib" != yes && test "${opsys}" != "mingw32" \
- && LIBJPEG=$emacs_cv_jpeglib
+ if test "${REALLY_ANDROID}" = "yes"; then
+ # Look for libjpeg using the NDK.
+ ndk_SEARCH_MODULE([libjpeg], [JPEG], [HAVE_JPEG=yes])
+
+ if test "$HAVE_JPEG" = "yes"; then
+ LIBJPEG="$JPEG_LIBS"
+
+ AC_DEFINE([HAVE_JPEG], [1],
+ [Define to 1 if you have the jpeg library (typically -ljpeg).])
+ fi
+ else
+ AC_CACHE_CHECK([for jpeglib 6b or later],
+ [emacs_cv_jpeglib],
+ [OLD_LIBS=$LIBS
+ for emacs_cv_jpeglib in yes -ljpeg no; do
+ case $emacs_cv_jpeglib in
+ yes) ;;
+ no) break;;
+ *) LIBS="$LIBS $emacs_cv_jpeglib";;
+ esac
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#undef HAVE_STDLIB_H /* Avoid config.h/jpeglib.h collision. */
+ #include <stdio.h> /* jpeglib.h needs FILE and size_t. */
+ #include <jpeglib.h>
+ #include <jerror.h>
+ char verify[JPEG_LIB_VERSION < 62 ? -1 : 1];
+ struct jpeg_decompress_struct cinfo;
+ ]],
+ [[
+ jpeg_create_decompress (&cinfo);
+ WARNMS (&cinfo, JWRN_JPEG_EOF);
+ jpeg_destroy_decompress (&cinfo);
+ ]])],
+ [emacs_link_ok=yes],
+ [emacs_link_ok=no])
+ LIBS=$OLD_LIBS
+ test $emacs_link_ok = yes && break
+ done])
+ if test "$emacs_cv_jpeglib" != no; then
+ HAVE_JPEG=yes
+ AC_DEFINE([HAVE_JPEG], [1],
+ [Define to 1 if you have the jpeg library (typically -ljpeg).])
+ ### mingw32 doesn't use -ljpeg, since it loads the library
+ ### dynamically when needed, and doesn't want a run-time
+ ### dependency on the jpeglib DLL.
+ test "$emacs_cv_jpeglib" != yes && test "${opsys}" != "mingw32" \
+ && LIBJPEG=$emacs_cv_jpeglib
+ fi
fi
fi
fi
AC_SUBST([LIBJPEG])
+AC_SUBST([JPEG_CFLAGS])
HAVE_LCMS2=no
LCMS2_CFLAGS=
@@ -4145,50 +4950,43 @@ if test $window_system = pgtk; then
esac
fi
-if test "${with_modules}" != "no"; then
- case $opsys in
- gnu|gnu-linux)
- LIBMODULES="-ldl"
- HAVE_MODULES=yes
- ;;
- cygwin|mingw32|darwin)
- HAVE_MODULES=yes
- ;;
- *)
- # BSD systems have dlopen in libc.
- AC_CHECK_FUNC([dlopen], [HAVE_MODULES=yes])
- ;;
- esac
-
- if test "${HAVE_MODULES}" = no; then
- AC_MSG_ERROR([Dynamic modules are not supported on your system])
- else
- SAVE_LIBS=$LIBS
- LIBS="$LIBS $LIBMODULES"
- AC_CHECK_FUNCS([dladdr dlfunc])
- LIBS=$SAVE_LIBS
- fi
-fi
+AS_IF([test "x$with_modules" != "xno"],
+ [AS_CASE(["$opsys"],
+ [gnu|gnu-linux],
+ [LIBMODULES="-ldl"
+ HAVE_MODULES=yes],
+ [cygwin|mingw32|darwin],
+ [HAVE_MODULES=yes],
+ # BSD systems have dlopen in libc.
+ [AC_CHECK_FUNC([dlopen], [HAVE_MODULES=yes])])
+
+ AS_IF([test "x$HAVE_MODULES" = "xno"],
+ [AS_IF([test "$with_modules" = "ifavailable"],
+ [AC_MSG_WARN([Dynamic modules are not supported on your system])],
+ [AC_MSG_ERROR([Dynamic modules are not supported on your system])])],
+ [SAVE_LIBS=$LIBS
+ LIBS="$LIBS $LIBMODULES"
+ AC_CHECK_FUNCS([dladdr dlfunc])
+ LIBS=$SAVE_LIBS])])
+
+AS_IF([test "x$HAVE_MODULES" = xyes],
+ [MODULES_OBJ="emacs-module.o"
+ NEED_DYNLIB=yes
+ AC_DEFINE([HAVE_MODULES], [1], [Define to 1 if dynamic modules are enabled])
+ AC_DEFINE_UNQUOTED([MODULES_SUFFIX], ["$MODULES_SUFFIX"],
+ [System extension for dynamic libraries])
+ AS_IF([test -n "$MODULES_SECONDARY_SUFFIX"],
+ [AC_DEFINE_UNQUOTED([MODULES_SECONDARY_SUFFIX],
+ ["$MODULES_SECONDARY_SUFFIX"],
+ [Alternative system extension for dynamic libraries.])])])
-if test "${HAVE_MODULES}" = yes; then
- MODULES_OBJ="emacs-module.o"
- NEED_DYNLIB=yes
- AC_DEFINE([HAVE_MODULES], [1], [Define to 1 if dynamic modules are enabled])
- AC_DEFINE_UNQUOTED([MODULES_SUFFIX], ["$MODULES_SUFFIX"],
- [System extension for dynamic libraries])
- if test -n "${MODULES_SECONDARY_SUFFIX}"; then
- AC_DEFINE_UNQUOTED([MODULES_SECONDARY_SUFFIX],
- ["$MODULES_SECONDARY_SUFFIX"],
- [Alternative system extension for dynamic libraries.])
- fi
-fi
AC_SUBST([MODULES_OBJ])
AC_SUBST([LIBMODULES])
AC_SUBST([HAVE_MODULES])
AC_SUBST([MODULES_SUFFIX])
AC_SUBST([MODULES_SECONDARY_SUFFIX])
-AC_CONFIG_FILES([src/emacs-module.h])
+ARCH_INDEPENDENT_CONFIG_FILES([src/emacs-module.h])
AC_SUBST_FILE([module_env_snippet_25])
AC_SUBST_FILE([module_env_snippet_26])
AC_SUBST_FILE([module_env_snippet_27])
@@ -4378,7 +5176,8 @@ if test "${with_png}" != no; then
AC_CHECK_HEADER([png.h], [HAVE_PNG=yes])
elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
|| test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes" \
- || test "$window_system" = "pgtk"; then
+ || test "$window_system" = "pgtk" \
+ || test "${REALLY_ANDROID}" = "yes"; then
EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
if test $HAVE_PNG = yes; then
LIBPNG=$PNG_LIBS
@@ -4445,6 +5244,7 @@ AC_SUBST([PNG_CFLAGS])
### mingw32 doesn't use -ltiff, since it loads the library dynamically.
HAVE_TIFF=no
LIBTIFF=
+TIFF_CFLAGS=
if test "${opsys}" = "mingw32"; then
if test "${with_tiff}" != "no"; then
AC_CHECK_HEADER([tiffio.h], [HAVE_TIFF=yes], [HAVE_TIFF=no])
@@ -4455,28 +5255,42 @@ if test "${opsys}" = "mingw32"; then
fi
elif test "${HAVE_X11}" = "yes" || test "${HAVE_W32}" = "yes" \
|| test "${HAVE_NS}" = "yes" || test "${HAVE_BE_APP}" = "yes" \
- || test "$window_system" = "pgtk"; then
+ || test "$window_system" = "pgtk" \
+ || test "${REALLY_ANDROID}" = "yes"; then
if test "${with_tiff}" != "no"; then
- AC_CHECK_HEADER([tiffio.h],
- [tifflibs="-lz -lm"
- # At least one tiff package requires the jpeg library.
- if test "${HAVE_JPEG}" = yes; then tifflibs="-ljpeg $tifflibs"; fi
- AC_CHECK_LIB([tiff], [TIFFGetVersion], [HAVE_TIFF=yes], [],
- [$tifflibs])])
+ if test "${REALLY_ANDROID}" != "yes"; then
+ AC_CHECK_HEADER([tiffio.h],
+ [tifflibs="-lz -lm"
+ # At least one tiff package requires the jpeg library.
+ if test "${HAVE_JPEG}" = yes; then tifflibs="-ljpeg $tifflibs"; fi
+ AC_CHECK_LIB([tiff], [TIFFGetVersion], [HAVE_TIFF=yes], [],
+ [$tifflibs])])
+ else
+ ndk_SEARCH_MODULE([libtiff], [TIFF], [HAVE_TIFF=yes])
+
+ if test "$HAVE_TIFF" = "yes"; then
+ LIBTIFF="$TIFF_LIBS"
+ fi
+ fi
fi
if test "${HAVE_TIFF}" = "yes"; then
AC_DEFINE([HAVE_TIFF], [1],
[Define to 1 if you have the tiff library (-ltiff).])
- dnl FIXME -lz -lm, as per libpng?
- LIBTIFF=-ltiff
+
+ if test "$REALLY_ANDROID" != "yes"; then
+ dnl FIXME -lz -lm, as per libpng?
+ LIBTIFF=-ltiff
+ fi
fi
fi
AC_SUBST([LIBTIFF])
+AC_SUBST([TIFF_CFLAGS])
### Use -lgif or -lungif if available, unless '--with-gif=no'.
### mingw32 doesn't use -lgif/-lungif, since it loads the library dynamically.
HAVE_GIF=no
+GIF_CFLAGS=
LIBGIF=
if test "${opsys}" = "mingw32"; then
if test "${with_gif}" != "no"; then
@@ -4489,6 +5303,7 @@ if test "${opsys}" = "mingw32"; then
elif test "${HAVE_X11}" = "yes" && test "${with_gif}" != "no" \
|| test "${HAVE_W32}" = "yes" || test "${HAVE_NS}" = "yes" \
|| test "${HAVE_BE_APP}" = "yes" || test "$window_system" = "pgtk" \
+ || test "${REALLY_ANDROID}" = "yes" \
&& test "${with_gif}" != "no"; then
AC_CHECK_HEADER([gif_lib.h],
# EGifPutExtensionLast only exists from version libungif-4.1.0b1.
@@ -4508,12 +5323,20 @@ elif test "${HAVE_X11}" = "yes" && test "${with_gif}" != "no" \
test "$HAVE_GIF" = yes && LIBGIF=-lungif
fi
+# Finally, try ndk-build on Android.
+ if test "$REALLY_ANDROID" = "yes"; then
+ ndk_SEARCH_MODULE([libgif], [GIF], [HAVE_GIF=yes],
+ [HAVE_GIF=no])
+ test "$HAVE_GIF" = yes && LIBGIF="$GIF_LIBS"
+ fi
+
if test "${HAVE_GIF}" = "yes"; then
AC_DEFINE([HAVE_GIF], [1],
[Define to 1 if you have a gif (or ungif) library.])
fi
fi
AC_SUBST([LIBGIF])
+AC_SUBST([GIF_CFLAGS])
dnl Check for required libraries.
MISSING=
@@ -4852,10 +5675,13 @@ if test "${with_xml2}" != "no"; then
fi
if test "${HAVE_LIBXML2}" = "yes"; then
if test "${opsys}" != "mingw32"; then
- AC_CHECK_LIB([xml2], [htmlReadMemory],
+ SAVE_CFLAGS=$CFLAGS
+ CFLAGS="$CFLAGS $LIBXML2_CFLAGS"
+ EMACS_CHECK_LIB([xml2], [htmlReadMemory],
[HAVE_LIBXML2=yes],
[HAVE_LIBXML2=no],
- [$LIBXML2_LIBS])
+ [$LIBXML2_LIBS], [#include <libxml/HTMLparser.h>])
+ CFLAGS="$SAVE_CFLAGS"
else
LIBXML2_LIBS=""
fi
@@ -4981,7 +5807,7 @@ OLD_LIBS=$LIBS
LIBS="$LIB_PTHREAD $LIB_MATH $LIBS"
AC_CHECK_FUNCS([accept4 fchdir gethostname \
getrusage get_current_dir_name \
-lrand48 random rint trunc \
+lrand48 random rint tcdrain trunc \
select getpagesize setlocale newlocale \
getrlimit setrlimit shutdown \
pthread_sigmask strsignal setitimer \
@@ -4991,6 +5817,40 @@ getpwent endpwent getgrent endgrent \
renameat2 \
cfmakeraw cfsetspeed __executable_start log2 pthread_setname_np \
pthread_set_name_np])
+
+if test "$ac_cv_func_cfmakeraw" != "yes"; then
+ # On some systems (Android), cfmakeraw is inline, so AC_CHECK_FUNCS
+ # cannot find it. Check if some code including termios.h and using
+ # cfmakeraw builds.
+ AC_CACHE_CHECK([whether cfmakeraw is inline],
+ [emacs_cv_func_cfmakeraw_inline],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+ [[#include <termios.h>]],
+ [[&cfmakeraw;]])],
+ [emacs_cv_func_cfmakeraw_inline=yes],
+ [emacs_cv_func_cfmakeraw_inline=no])])
+
+ if test "$emacs_cv_func_cfmakeraw_inline" = "yes"; then
+ # Define HAVE_CFMAKERAW again.
+ AC_DEFINE([HAVE_CFMAKERAW], [1])
+ fi
+fi
+
+if test "$ac_cv_func_cfsetspeed" != "yes"; then
+ AC_CACHE_CHECK([whether cfsetspeed is inline],
+ [emacs_cv_func_cfsetspeed_inline],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+ [[#include <termios.h>]],
+ [[&cfsetspeed;]])],
+ [emacs_cv_func_cfsetspeed_inline=yes],
+ [emacs_cv_func_cfsetspeed_inline=no])])
+
+ if test "$emacs_cv_func_cfsetspeed_inline" = "yes"; then
+ # Define HAVE_CFSETSPEED again.
+ AC_DEFINE([HAVE_CFSETSPEED], [1])
+ fi
+fi
+
LIBS=$OLD_LIBS
if test "$ac_cv_func_pthread_setname_np" = "yes"; then
@@ -5042,8 +5902,8 @@ if test "$with_unexec" = yes && test "$opsys" = "haiku"; then
Please use the portable dumper instead.])
fi
-# Dump loading
-AC_CHECK_FUNCS([posix_madvise])
+# Dump loading. Android lacks posix_madvise.
+AC_CHECK_FUNCS([posix_madvise madvise])
dnl Cannot use AC_CHECK_FUNCS
AC_CACHE_CHECK([for __builtin_frame_address],
@@ -5096,7 +5956,7 @@ AC_DEFUN([tputs_link_source], [
# than to expect to find it in ncurses.
# Also we need tputs and friends to be able to build at all.
AC_CACHE_CHECK([for library containing tputs], [emacs_cv_tputs_lib],
-[if test "${opsys}" = "mingw32"; then
+[if test "${opsys}" = "mingw32" || test "$opsys" = "android"; then
emacs_cv_tputs_lib='none required'
else
# curses precedes termcap because of AIX (Bug#9736#35) and OpenIndiana.
@@ -5163,7 +6023,7 @@ fail;
fi
;;
- mingw32)
+ mingw32 | android)
TERMINFO=no
LIBS_TERMCAP=
;;
@@ -5710,7 +6570,7 @@ case $opsys in
AC_DEFINE([FIRST_PTY_LETTER], ['p'])
;;
- gnu-linux | gnu-kfreebsd | dragonfly | freebsd | openbsd | netbsd | darwin | nacl )
+ gnu-linux | gnu-kfreebsd | dragonfly | freebsd | openbsd | netbsd | darwin | nacl | android )
dnl if HAVE_GRANTPT
if test "x$ac_cv_func_grantpt" = xyes; then
AC_DEFINE([UNIX98_PTYS], [1], [Define if the system has Unix98 PTYs.])
@@ -6117,6 +6977,9 @@ AC_DEFINE_UNQUOTED([COPYRIGHT], ["$copyright"],
[Short copyright string for this version of Emacs.])
AC_SUBST([copyright])
+# This is needed for gnulib's printf modules.
+CFLAGS="$CFLAGS -DHAVE_CONFIG_H"
+
### Specify what sort of things we'll be editing into Makefile and config.h.
### Use configuration here uncanonicalized to avoid exceeding size limits.
AC_SUBST([version])
@@ -6389,6 +7252,38 @@ gl_INIT
CFLAGS=$SAVE_CFLAGS
LIBS=$SAVE_LIBS
+# Set up libgmp on Android. Make sure to override what gnulib has
+# found.
+LIBGMP_CFLAGS=
+if test "$REALLY_ANDROID" = "yes" && test "$with_libgmp" != "no"; then
+ HAVE_LIBGMP=no
+ ndk_SEARCH_MODULE([libgmp], [LIBGMP], [HAVE_LIBGMP=yes])
+
+ if test "$HAVE_LIBGMP" = "yes"; then
+ SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS $LIBGMP_CFLAGS"
+ unset ac_cv_header_gmp_h
+ unset ac_cv_header_gmp_gmp_h
+ AC_CHECK_HEADERS([gmp.h gmp/gmp.h], [break])
+ CFLAGS="$SAVE_CFLAGS"
+ GL_GENERATE_GMP_H=
+ GL_GENERATE_GMP_H_CONDITION=
+ GL_GENERATE_GMP_GMP_H=
+ GL_GENERATE_GMP_GMP_H_CONDITION=
+ GL_GENERATE_MINI_GMP_H=
+ GL_GENERATE_MINI_GMP_H_CONDITION=
+
+ if test "$ac_cv_header_gmp_h" != "no" \
+ || test "$ac_cv_header_gmp_gmp_h" != "no"; then
+ HAVE_LIBGMP=yes
+ GL_GENERATE_GMP_H=false
+ LIBGMP="$LIBGMP_LIBS"
+ GMP_H=
+ fi
+ fi
+fi
+AC_SUBST([LIBGMP_CFLAGS])
+
# timer_getoverrun needs the same library as timer_settime
OLD_LIBS=$LIBS
LIBS="$LIB_TIMER_TIME $LIBS"
@@ -6515,7 +7410,7 @@ if test "$window_system" != "none"; then
[Define if you poll periodically to detect C-g.])
WINDOW_SYSTEM_OBJ="fontset.o fringe.o image.o"
- if test "$window_system" = "x11"; then
+ if test "$window_system" = "x11" || test "$REALLY_ANDROID" = "yes"; then
AC_DEFINE([HAVE_TEXT_CONVERSION], [1],
[Define if the window system has text conversion support.])
WINDOW_SYSTEM_OBJ="$WINDOW_SYSTEM_OBJ textconv.o"
@@ -6524,6 +7419,35 @@ fi
AC_SUBST([WINDOW_SYSTEM_OBJ])
+# Some systems have MB_CUR_MAX defined to a call to
+# __ctype_get_mb_cur_max, but do not have __ctype_get_mb_cur_max in
+# libc. Check for that situation and define MB_CUR_MAX to something
+# sane.
+
+AC_CHECK_FUNC([__ctype_get_mb_cur_max])
+
+AC_CACHE_CHECK([whether MB_CUR_MAX is defined to function that won't link],
+ [emacs_cv_broken_mb_cur_max],
+ [AC_EGREP_CPP(__ctype_get_mb_cur_max, [
+#include <stdlib.h>
+#ifndef MB_CUR_MAX
+#define MB_CUR_MAX -1
+#endif
+static int foo = MB_CUR_MAX;
+], [AS_IF([test "$ac_cv_func___ctype_get_mb_cur_max" = "yes"],
+ [emacs_cv_broken_mb_cur_max=no],
+ [emacs_cv_broken_mb_cur_max=yes])],
+ [emacs_cv_broken_mb_cur_max=no])])
+
+AS_IF([test "$emacs_cv_broken_mb_cur_max" = "yes"],
+ # Define this to 4, which is right for Android.
+ [AS_CASE([$opsys], [android],
+ [AC_DEFINE([REPLACEMENT_MB_CUR_MAX], [4],
+ [Define to MB_CUR_MAX if stdlib.h is broken.])],
+ [AC_MSG_ERROR([MB_CUR_MAX does not work on your system.
+Please modify configure.ac to set an appropriate value, then
+send your change to bug-gnu-emacs@gnu.org])])])
+
AH_TOP([/* GNU Emacs site configuration template file.
Copyright (C) 1988, 1993-1994, 1999-2002, 2004-2021
@@ -6644,7 +7568,12 @@ done
AC_DEFINE_UNQUOTED([EMACS_CONFIG_FEATURES], ["${emacs_config_features}"],
[Summary of some of the main features enabled by configure.])
+# This is just a printable representation of the shared user ID.
+android_shared_user=
+AS_IF([test -n "$with_shared_user_id"],[android_shared_user="($with_shared_user_id)"])
+
AS_ECHO([" Does Emacs use -lXaw3d? ${HAVE_XAW3D}
+ Is Emacs being built for Android? ${ANDROID} ${android_shared_user}
Does Emacs use the X Double Buffer Extension? ${HAVE_XDBE}
Does Emacs use -lXpm? ${HAVE_XPM}
Does Emacs use -ljpeg? ${HAVE_JPEG}
@@ -6749,12 +7678,15 @@ fi
AC_CONFIG_FILES([Makefile lib/gnulib.mk])
dnl config.status treats $srcdir specially, so I think this is ok...
-AC_CONFIG_FILES([$srcdir/doc/man/emacs.1])
+ARCH_INDEPENDENT_CONFIG_FILES([$srcdir/doc/man/emacs.1])
+
+AC_CONFIG_FILES([lib/Makefile lib-src/Makefile oldXMenu/Makefile src/Makefile
+ lwlib/Makefile nextstep/Makefile nt/Makefile])
+ARCH_INDEPENDENT_CONFIG_FILES([doc/emacs/Makefile doc/misc/Makefile
+ doc/lispintro/Makefile doc/lispref/Makefile
+ lisp/Makefile leim/Makefile])
-m4_define([subdir_makefiles],
- [lib/Makefile lib-src/Makefile oldXMenu/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile src/Makefile lwlib/Makefile lisp/Makefile leim/Makefile nextstep/Makefile nt/Makefile])
-SUBDIR_MAKEFILES="subdir_makefiles"
-AC_CONFIG_FILES(subdir_makefiles)
+SUBDIR_MAKEFILES="lib/Makefile lib-src/Makefile oldXMenu/Makefile src/Makefile lwlib/Makefile nextstep/Makefile nt/Makefile doc/emacs/Makefile doc/misc/Makefile doc/lispintro/Makefile doc/lispref/Makefile lisp/Makefile leim/Makefile"
dnl The test/ directory is missing if './make-dist --no-tests' was used.
opt_makefile=test/Makefile
@@ -6762,24 +7694,27 @@ if test -f "$srcdir/$opt_makefile.in"; then
SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES $opt_makefile"
dnl Again, it's best not to use a variable. Though you can add
dnl ", [], [opt_makefile='$opt_makefile']" and it should work.
- AC_CONFIG_FILES([test/Makefile])
- AC_CONFIG_FILES([test/manual/noverlay/Makefile])
+ ARCH_INDEPENDENT_CONFIG_FILES([test/Makefile])
+ ARCH_INDEPENDENT_CONFIG_FILES([test/manual/noverlay/Makefile])
fi
opt_makefile=test/infra/Makefile
if test -f "$srcdir/$opt_makefile.in"; then
SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES $opt_makefile"
dnl Again, it's best not to use a variable. Though you can add
dnl ", [], [opt_makefile='$opt_makefile']" and it should work.
- AC_CONFIG_FILES([test/infra/Makefile])
+ ARCH_INDEPENDENT_CONFIG_FILES([test/infra/Makefile])
fi
+if test "$ANDROID" = "yes"; then
+ SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES java/Makefile cross/Makefile"
+fi
dnl The admin/ directory used to be excluded from tarfiles.
if test -d $srcdir/admin; then
SUBDIR_MAKEFILES="$SUBDIR_MAKEFILES admin/charsets/Makefile admin/unidata/Makefile admin/grammars/Makefile"
- AC_CONFIG_FILES([admin/charsets/Makefile])
- AC_CONFIG_FILES([admin/unidata/Makefile])
- AC_CONFIG_FILES([admin/grammars/Makefile])
+ ARCH_INDEPENDENT_CONFIG_FILES([admin/charsets/Makefile])
+ ARCH_INDEPENDENT_CONFIG_FILES([admin/unidata/Makefile])
+ ARCH_INDEPENDENT_CONFIG_FILES([admin/grammars/Makefile])
fi dnl -d admin
@@ -6790,65 +7725,106 @@ AC_SUBST([SUBDIR_MAKEFILES_IN])
SMALL_JA_DIC=$with_small_ja_dic
AC_SUBST([SMALL_JA_DIC])
-dnl You might wonder (I did) why epaths.h is generated by running make,
-dnl rather than just letting configure generate it from epaths.in.
-dnl One reason is that the various paths are not fully expanded (see above);
-dnl e.g., gamedir='${localstatedir}/games/emacs'.
-dnl Secondly, the GNU Coding standards require that one should be able
-dnl to run 'make prefix=/some/where/else' and override the values set
-dnl by configure. This also explains the 'move-if-change' test and
-dnl the use of force in the 'epaths-force' rule in Makefile.in.
-AC_CONFIG_COMMANDS([src/epaths.h], [
-if test "${opsys}" = "mingw32"; then
- ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-w32
-elif test "$HAVE_NS" = "yes" && test "$EN_NS_SELF_CONTAINED" = "yes"; then
- ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-ns-self-contained
-else
- ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force
-fi || AC_MSG_ERROR(['src/epaths.h' could not be made.])
-], [GCC="$GCC" CPPFLAGS="$CPPFLAGS" opsys="$opsys" HAVE_NS="$HAVE_NS"
- EN_NS_SELF_CONTAINED="$EN_NS_SELF_CONTAINED"])
-
-dnl NB we have to cheat and use the ac_... version because abs_top_srcdir
-dnl is not yet set, sigh. Or we could use ../$srcdir/src/.gdbinit,
-dnl or a symlink?
-AC_CONFIG_COMMANDS([src/.gdbinit], [
-if test ! -f src/.gdbinit && test -f "$srcdir/src/.gdbinit"; then
- AS_ECHO(["source $ac_abs_top_srcdir/src/.gdbinit"]) > src/.gdbinit
-fi
-])
+dnl The following commands are run on the host system when building
+dnl Emacs.
+
+if test "$XCONFIGURE" != "android"; then
+ dnl You might wonder (I did) why epaths.h is generated by running
+ dnl make, rather than just letting configure generate it from
+ dnl epaths.in. One reason is that the various paths are not fully
+ dnl expanded (see above); e.g.,
+ dnl gamedir='${localstatedir}/games/emacs'. Secondly, the GNU
+ dnl Coding standards require that one should be able to run 'make
+ dnl prefix=/some/where/else' and override the values set by
+ dnl configure. This also explains the 'move-if-change' test and the
+ dnl use of force in the 'epaths-force' rule in Makefile.in.
+ AC_CONFIG_COMMANDS([src/epaths.h], [
+ if test "${opsys}" = "mingw32"; then
+ ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-w32
+ elif test "$HAVE_NS" = "yes" && test "$EN_NS_SELF_CONTAINED" = "yes"; then
+ ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force-ns-self-contained
+ else
+ ${MAKE-make} MAKEFILE_NAME=do-not-make-Makefile epaths-force
+ fi || AC_MSG_ERROR(['src/epaths.h' could not be made.])
+ ], [GCC="$GCC" CPPFLAGS="$CPPFLAGS" opsys="$opsys" HAVE_NS="$HAVE_NS"
+ EN_NS_SELF_CONTAINED="$EN_NS_SELF_CONTAINED"])
+
+ dnl NB we have to cheat and use the ac_... version because abs_top_srcdir
+ dnl is not yet set, sigh. Or we could use ../$srcdir/src/.gdbinit,
+ dnl or a symlink?
+ AC_CONFIG_COMMANDS([src/.gdbinit], [
+ if test ! -f src/.gdbinit && test -f "$srcdir/src/.gdbinit"; then
+ AS_ECHO(["source $ac_abs_top_srcdir/src/.gdbinit"]) > src/.gdbinit
+ fi
+ ])
-dnl Perhaps this would be better named doc-emacs-emacsver.texi?
-dnl See comments for etc-refcards-emacsver.tex.
-dnl Since we get a doc/emacs directory generated anyway, for the Makefile,
-dnl it is not quite the same. But we are generating in $srcdir.
-AC_CONFIG_COMMANDS([doc/emacs/emacsver.texi], [
-${MAKE-make} -s --no-print-directory -C doc/emacs doc-emacsver || \
-AC_MSG_ERROR(['doc/emacs/emacsver.texi' could not be made.])
-])
+ dnl Perhaps this would be better named doc-emacs-emacsver.texi?
+ dnl See comments for etc-refcards-emacsver.tex.
+ dnl Since we get a doc/emacs directory generated anyway, for the Makefile,
+ dnl it is not quite the same. But we are generating in $srcdir.
+ AC_CONFIG_COMMANDS([doc/emacs/emacsver.texi], [
+ ${MAKE-make} -s --no-print-directory -C doc/emacs doc-emacsver || \
+ AC_MSG_ERROR(['doc/emacs/emacsver.texi' could not be made.])
+ ])
-dnl If we give this the more natural name, etc/refcards/emacsver.texi,
-dnl then a directory etc/refcards is created in the build directory,
-dnl which is probably harmless, but confusing (in out-of-tree builds).
-dnl (If we were to generate etc/refcards/Makefile, this might change.)
-dnl It is really $srcdir/etc/refcards/emacsver.tex that we generate.
-AC_CONFIG_COMMANDS([etc-refcards-emacsver.tex], [
-${MAKE-make} -s MAKEFILE_NAME=do-not-make-Makefile etc-emacsver || \
-AC_MSG_ERROR(['etc/refcards/emacsver.tex' could not be made.])
-])
+ dnl If we give this the more natural name, etc/refcards/emacsver.texi,
+ dnl then a directory etc/refcards is created in the build directory,
+ dnl which is probably harmless, but confusing (in out-of-tree builds).
+ dnl (If we were to generate etc/refcards/Makefile, this might change.)
+ dnl It is really $srcdir/etc/refcards/emacsver.tex that we generate.
+ AC_CONFIG_COMMANDS([etc-refcards-emacsver.tex], [
+ ${MAKE-make} -s MAKEFILE_NAME=do-not-make-Makefile etc-emacsver || \
+ AC_MSG_ERROR(['etc/refcards/emacsver.tex' could not be made.])
+ ])
-if test $AUTO_DEPEND = yes; then
- for dir in $AUTODEPEND_PARENTS; do
- AS_MKDIR_P([$dir/deps])
- done
-fi
-if $gl_gnulib_enabled_dynarray || $gl_gnulib_enabled_scratch_buffer; then
- AS_MKDIR_P([lib/malloc])
if test $AUTO_DEPEND = yes; then
- AS_MKDIR_P([lib/deps/malloc])
+ for dir in $AUTODEPEND_PARENTS; do
+ AS_MKDIR_P([$dir/deps])
+ AS_MKDIR_P([cross/$dir/deps])
+ done
fi
+ if $gl_gnulib_enabled_dynarray || $gl_gnulib_enabled_scratch_buffer; then
+ AS_MKDIR_P([lib/malloc])
+ AS_MKDIR_P([cross/lib/malloc])
+ if test $AUTO_DEPEND = yes; then
+ AS_MKDIR_P([lib/deps/malloc])
+ AS_MKDIR_P([cross/lib/deps/malloc])
+ fi
+ fi
+
+ dnl Make cross/lib, which various Makefiles in cross expect to
+ dnl always exist.
+ AS_MKDIR_P([cross/lib])
+ AS_MKDIR_P([cross/lib/malloc])
+ AS_MKDIR_P([cross/lib/sys])
+ AS_MKDIR_P([cross/lib-src])
+
+ dnl Link gnulib files to cross/lib as well.
+ dnl af_alg.h and lib/save-cwd.h are copied manually from
+ dnl gnulib, and as such aren't specified in gl_FILE_LIST.
+ emacs_files='gl_FILE_LIST lib/af_alg.h lib/save-cwd.h'
+ dnl These files are specific to Emacs.
+ emacs_files="$emacs_files lib/fingerprint.c lib/fingerprint.h \
+ lib/save-cwd.c lib/openat-die.c lib/save-cwd.c \
+ lib/min-max.h"
+ for file in $emacs_files; do
+ AS_IF([expr "X${file}J" : "Xlib/.*[[ch]]J" >/dev/null],
+ [AS_IF([test -f "$srcdir/$file"],
+ [AC_CONFIG_LINKS([cross/$file:$file])])])
+ done
fi
+# Make java/Makefile
+ARCH_INDEPENDENT_CONFIG_FILES([java/Makefile])
+ARCH_INDEPENDENT_CONFIG_FILES([cross/Makefile])
+
+# Make java/AndroidManifest.xml
+ARCH_INDEPENDENT_CONFIG_FILES([java/AndroidManifest.xml])
+
+# Make ndk-build Makefiles. This is only done inside the recursive
+# configure.
+ndk_CONFIG_FILES
+
AC_OUTPUT
if test ! "$with_mailutils"; then
diff --git a/cross/Makefile.in b/cross/Makefile.in
new file mode 100644
index 00000000000..5976272b253
--- /dev/null
+++ b/cross/Makefile.in
@@ -0,0 +1,190 @@
+### @configure_input@
+
+# 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/>.
+
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+top_builddir = @top_builddir@
+builddir = @builddir@
+
+-include $(top_builddir)/src/verbose.mk
+
+# Cross-compiling Emacs for Android.
+
+# The cross compiled binaries are built by having ``variant''
+# Makefiles generated at configure-time. First,
+# $(top_builddir)/src/Makefile.android,
+# $(top_builddir)/lib/Makefile.android,
+# $(top_builddir)/lib/gnulib.mk.android and
+# $(top_builddir)/lib-src/Makefile.android are copied to their usual
+# locations in this directory.
+
+# N.B. that LIB_SRCDIR is actually relative to builddir, because that
+# is where the gnulib files get linked.
+
+LIB_SRCDIR = $(realpath $(builddir)/lib)
+LIB_TOP_SRCDIR = $(realpath $(top_srcdir))
+
+SRC_SRCDIR = $(realpath $(top_srcdir)/src)
+SRC_TOP_SRCDIR = $(realpath $(top_srcdir))
+
+LIB_SRC_SRCDIR = $(realpath $(top_srcdir)/lib-src)
+LIB_SRC_TOP_SRCDIR = $(realpath $(top_src))
+
+# This is a list of binaries to build and install in lib-src.
+
+LIBSRC_BINARIES = lib-src/etags lib-src/ctags lib-src/emacsclient \
+ lib-src/ebrowse lib-src/hexl lib-src/movemail
+
+CLEAN_SUBDIRS=src lib-src lib
+
+.PHONY: all
+all: lib/libgnu.a src/libemacs.so src/android-emacs $(LIBSRC_BINARIES)
+
+# This Makefile relies on builddir and top_builddir being relative
+# paths in *.android.
+
+# This file is used to tell lib/gnulib.mk when
+# $(top_builddir)/config.status changes.
+config.status: $(top_builddir)/config.status
+ $(AM_V_GEN) touch config.status
+
+src/verbose.mk: $(srcdir)/verbose.mk.android
+ $(AM_V_SILENT) cp -f $(srcdir)/verbose.mk.android \
+ src/verbose.mk
+
+# Gnulib, make-fingerprint and make-docfile must be built before
+# entering any of the rules below, or they will get the Android
+# versions of many headers.
+
+.PHONY: $(top_builddir)/lib/libgnu.a
+$(top_builddir)/lib/libgnu.a:
+ $(MAKE) -C $(top_builddir)/lib libgnu.a
+
+.PHONY: $(top_builddir)/lib-src/make-fingerprint
+$(top_builddir)/lib-src/make-fingerprint: $(top_builddir)/lib/libgnu.a
+ $(MAKE) -C $(top_builddir)/lib-src make-fingerprint
+
+.PHONY: $(top_builddir)/lib-src/make-docfile
+$(top_builddir)/lib-src/make-docfile: $(top_builddir)/lib/libgnu.a
+ $(MAKE) -C $(top_builddir)/lib-src make-docfile
+
+PRE_BUILD_DEPS=$(top_builddir)/lib/libgnu.a \
+ $(top_builddir)/lib-src/make-fingerprint \
+ $(top_builddir)/lib-src/make-docfile
+
+lib/config.h: $(top_builddir)/src/config.h.android
+ $(AM_V_GEN) cp -f -p $(top_builddir)/src/config.h.android \
+ lib/config.h
+
+lib-src/config.h: $(top_builddir)/src/config.h.android
+ $(AM_V_GEN) cp -f -p $(top_builddir)/src/config.h.android \
+ lib-src/config.h
+
+# Figure out where build-aux is.
+# Then, replace the build-aux directory with its actual location,
+# in case MKDIR_P points there.
+
+relative_buildaux_dir := $(subst /,\/,$(top_srcdir)/build-aux)
+
+lib/gnulib.mk: $(top_builddir)/lib/gnulib.mk.android
+ $(AM_V_GEN) \
+ sed -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \
+ -e 's/$(relative_buildaux_dir)/$(subst /,\/,../$(top_builddir))\/build-aux/g' \
+ < $(top_builddir)/lib/gnulib.mk.android > $@
+
+lib/Makefile: $(top_builddir)/lib/Makefile.android
+ $(AM_V_GEN) \
+ sed -e 's/^top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \
+ -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(LIB_SRCDIR))/g' \
+ -e 's/^VPATH =.*$$/VPATH = $(subst /,\/,$(LIB_SRCDIR))/g' \
+ < $(top_builddir)/lib/Makefile.android > $@
+
+# What is needed to build gnulib.
+LIB_DEPS = lib/config.h lib/gnulib.mk lib/Makefile
+
+.PHONY: lib/libgnu.a
+lib/libgnu.a: src/verbose.mk config.status $(LIB_DEPS) $(PRE_BUILD_DEPS)
+ $(MAKE) -C lib libgnu.a
+
+# Edit srcdir and top_srcdir to the right locations.
+# Edit references to ../admin/unidata to read ../../admin/unidata.
+# Next, edit libsrc to the location at top_srcdir! It is important
+# that src/Makefile uses the binaries there, instead of any
+# cross-compiled binaries at ./lib-src.
+# Edit out anything saying -I($(top_srcdir)/lib) into
+# -I$../(srcdir)/lib; that should be covered by -I$(lib)
+
+src/Makefile: $(top_builddir)/src/Makefile.android
+ $(AM_V_GEN) \
+ sed -e 's/^srcdir =.*$$/srcdir = $(subst /,\/,$(SRC_SRCDIR))/g' \
+ -e 's/^top_srcdir =.*$$/top_srcdir = $(subst /,\/,$(LIB_TOP_SRCDIR))/g' \
+ -e 's/\.\.\/admin\/unidata/..\/..\/admin\/unidata/g' \
+ -e 's/\.\.\/admin\/charsets/..\/..\/admin\/charsets/g' \
+ -e 's/^libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \
+ -e 's/libsrc =.*$$/libsrc = \.\.\/\.\.\/lib-src/g' \
+ -e 's/-I\$$(top_srcdir)\/lib/-I..\/$(subst /,\/,$(srcdir))\/lib/g' \
+ < $(top_builddir)/src/Makefile.android > $@
+
+src/config.h: $(top_builddir)/src/config.h.android
+ $(AM_V_GEN) cp -f -p $< $@
+
+.PHONY: src/android-emacs src/libemacs.so
+
+src/libemacs.so: src/Makefile src/config.h src/verbose.mk \
+ lib/libgnu.a $(PRE_BUILD_DEPS)
+ $(MAKE) -C src libemacs.so
+
+src/android-emacs: src/Makefile src/config.h lib/libgnu.a \
+ $(PRE_BUILD_DEPS)
+ $(MAKE) -C src android-emacs
+
+# Edit out SCRIPTS, it interferes with the build.
+# Make BASE_CFLAGS also include cross/lib as well as ../lib.
+
+lib-src/Makefile: $(top_builddir)/lib-src/Makefile.android
+ $(AM_V_GEN) \
+ sed -e 's/-I\$${srcdir}\/\.\.\/lib//g' \
+ -e 's/^srcdir=.*$$/srcdir = $(subst /,\/,$(LIB_SRC_SRCDIR))/g' \
+ -e 's/^top_srcdir=.*$$/top_srcdir = $(subst /,\/,$(LIB_SRC_TOP_SRCDIR))/g' \
+ -e 's/^SCRIPTS=.*$$/SCRIPTS=/g' \
+ -e 's/-I\.\.\/lib/-I..\/lib -I..\/$(subst /,\/,$(srcdir))\/lib/g' \
+ < $(top_builddir)/lib-src/Makefile.android > $@
+
+.PHONY: $(LIBSRC_BINARIES)
+$(LIBSRC_BINARIES) &: src/verbose.mk $(top_builddir)/$@ lib/libgnu.a \
+ lib-src/config.h lib-src/Makefile $(PRE_BUILD_DEPS)
+# Finally, go into lib-src and make everything being built
+ $(MAKE) -C lib-src $(foreach bin,$(LIBSRC_BINARIES),$(notdir $(bin)))
+
+.PHONY: clean maintainer-clean distclean
+clean:
+ for dir in $(CLEAN_SUBDIRS); do \
+ find $$dir -type f -delete; \
+ done
+ rm -rf lib/config.h lib-src/config.h
+# ndk-build won't have been generated in a non-Android build.
+ -make -C ndk-build clean
+
+maintainer-clean distclean bootstrap-clean: clean
+# Remove links created by configure.
+ for dir in $(CLEAN_SUBDIRS); do \
+ find $$dir -type l -delete; \
+ done
+ rm -rf lib/Makefile lib/gnulib.mk ndk-build/Makefile
+ rm -rf ndk-build/ndk-build.mk Makefile
diff --git a/cross/README b/cross/README
new file mode 100644
index 00000000000..3ec6f2c0b3c
--- /dev/null
+++ b/cross/README
@@ -0,0 +1,5 @@
+This directory holds Makefiles and other required assets to build an
+Emacs binary independently for another toolchain.
+
+The directory ndk-build also contains an implementation of the Android
+`ndk-build' build system.
diff --git a/cross/langinfo.h b/cross/langinfo.h
new file mode 100644
index 00000000000..b296ba8db80
--- /dev/null
+++ b/cross/langinfo.h
@@ -0,0 +1,20 @@
+/* Replacement langinfo.h file for building GNU Emacs on Android.
+
+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/>. */
+
+#define nl_langinfo(ignore) "ASCII"
diff --git a/cross/ndk-build/Makefile.in b/cross/ndk-build/Makefile.in
new file mode 100644
index 00000000000..cdf18471ff3
--- /dev/null
+++ b/cross/ndk-build/Makefile.in
@@ -0,0 +1,144 @@
+### @configure_input@
+
+# Copyright 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/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+ srcdir = @srcdir@
+
+# This is a list of Android.mk files which provide targets.
+NDK_BUILD_ANDROID_MK = @NDK_BUILD_ANDROID_MK@
+ NDK_BUILD_ARCH = @NDK_BUILD_ARCH@
+ NDK_BUILD_ABI = @NDK_BUILD_ABI@
+ NDK_BUILD_SDK = @NDK_BUILD_SDK@
+ NDK_BUILD_CC = @NDK_BUILD_CC@
+ NDK_BUILD_CXX = @NDK_BUILD_CXX@
+ NDK_BUILD_AR = @NDK_BUILD_AR@
+ NDK_BUILD_NASM = @NDK_BUILD_NASM@
+ NDK_BUILD_CFLAGS = @NDK_BUILD_CFLAGS@
+
+# This is a list of targets to build.
+ NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
+
+# This is set by the Android in tree build system and is used by some
+# libraries to look for the NDK. Its value is unimportant.
+ NDK_ROOT = /tmp/
+
+# Finally, here are rules common to Emacs.
+.PHONY: all
+all: $(NDK_BUILD_MODULES)
+
+define uniqify
+$(if $1,$(firstword $1) $(call uniqify,$(filter-out $(firstword $1),$1)))
+endef
+
+# Remove duplicate files.
+NDK_BUILD_ANDROID_MK := $(call uniqify,$(NDK_BUILD_ANDROID_MK))
+
+# Remove duplicate modules as well. These can occur when a single
+# module imports a module and also declares it in
+# LOCAL_SHARED_LIBRARIES.
+NDK_BUILD_MODULES := $(call uniqify,$(NDK_BUILD_MODULES))
+
+# Define CFLAGS for compiling C++ code; this involves removing all
+# -std=NNN options.
+NDK_BUILD_CFLAGS_CXX := $(filter-out -std=%,$(NDK_BUILD_CFLAGS))
+
+define subr-1
+
+# Define ndk-build functions. Many of these are identical to those in
+# build-aux/ndk-build-helper.mk.
+
+# NDK_LAST_MAKEFILE is the last Makefile that was included.
+NDK_LAST_MAKEFILE = $$(lastword $$(filter %Android.mk,$$(MAKEFILE_LIST)))
+
+# local-makefile is the current Makefile being loaded.
+local-makefile = $$(NDK_LAST_MAKEFILE)
+
+# my-dir is a function that returns the Android module directory. If
+# no Android.mk has been loaded, use the directory of the Makefile
+# being included.
+my-dir = $$(or $$(and $$(local-makefile),$$(dir $$(local-makefile))),$(dir $(1)))
+
+# Return all Android.mk files under the first arg.
+all-makefiles-under = $$(wildcard $$(1)/*/Android.mk)
+
+# Return all Android.mk files in subdirectories of this Makefile's
+# location.
+all-subdir-makefiles = $$(call all-makefiles-under,$$(call my-dir))
+
+# NDK-defined include variables.
+
+CLEAR_VARS = $(srcdir)/ndk-clear-vars.mk
+BUILD_EXECUTABLE = $(srcdir)/ndk-build-executable.mk
+BUILD_SHARED_LIBRARY = $(srcdir)/ndk-build-shared-library.mk
+BUILD_STATIC_LIBRARY = $(srcdir)/ndk-build-static-library.mk
+PREBUILT_SHARED_LIBRARY = $(srcdir)/ndk-prebuilt-shared-library.mk
+PREBUILT_STATIC_LIBRARY = $(srcdir)/ndk-prebuilt-static-library.mk
+
+# Target information variables.
+
+TARGET_ARCH = $(NDK_BUILD_ARCH)
+TARGET_PLATFORM = android-$(NDK_BUILD_SDK)
+TARGET_ARCH_ABI = $(NDK_BUILD_ABI)
+TARGET_ABI = $(TARGET_PLATFORM)-$(TARGET_ABI)
+
+# Module description variables. These are defined by Android.mk.
+LOCAL_PATH :=
+LOCAL_MODULE :=
+LOCAL_MODULE_FILENAME :=
+LOCAL_SRC_FILES :=
+LOCAL_CPP_EXTENSION :=
+LOCAL_CPP_FEATURES :=
+LOCAL_C_INCLUDES :=
+LOCAL_CFLAGS :=
+LOCAL_CPPFLAGS :=
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_SHARED_LIBRARIES :=
+LOCAL_WHOLE_STATIC_LIBRARIES :=
+LOCAL_LDLIBS :=
+LOCAL_LDFLAGS :=
+LOCAL_ALLOW_UNDEFINED_SYMBOLS :=
+LOCAL_ARM_MODE :=
+LOCAL_ARM_NEON :=
+LOCAL_DISABLE_FORMAT_STRING_CHECKS :=
+LOCAL_EXPORT_CFLAGS :=
+LOCAL_EXPORT_CPPFLAGS :=
+LOCAL_EXPORT_C_INCLUDES :=
+LOCAL_EXPORT_LDFLAGS :=
+LOCAL_EXPORT_LDLIBS :=
+LOCAL_ASM_RULE_DEFINED :=
+LOCAL_ASM_RULE :=
+
+# Now load Android.mk.
+include $(1)
+
+endef
+
+# Now define rules for each Android.mk file.
+$(foreach android_mk,$(NDK_BUILD_ANDROID_MK),$(eval $(call subr-1,$(android_mk))))
+
+.PHONY: clean mostlyclean
+clean mostlyclean:
+ rm -rf *.o *.so *.a
+
+.PHONY: extraclean dist-clean maintainer-clean
+extraclean dist-clean maintainer-clean:
+ rm -rf Makefile
diff --git a/cross/ndk-build/README b/cross/ndk-build/README
new file mode 100644
index 00000000000..aca2e7230bf
--- /dev/null
+++ b/cross/ndk-build/README
@@ -0,0 +1,353 @@
+NDK BUILD SYSTEM IMPLEMENTATION
+
+Copyright (C) 2023 Free Software Foundation, Inc.
+See the end of the file for license conditions.
+
+Emacs implements ndk-build itself, because the version that comes with
+the Android NDK is not easy to use from another Makefile, and keeps
+accumulating incompatible changes.
+
+The Emacs implementation of ndk-build consists of one m4 file:
+
+ m4/ndk-build.m4
+
+four Makefiles in build-aux, run during configure:
+
+ build-aux/ndk-build-helper-1.mk
+ build-aux/ndk-build-helper-2.mk
+ build-aux/ndk-build-helper-3.mk
+ build-aux/ndk-build-helper.mk
+
+one awk script in build-awx, run during configure:
+
+ build-aux/ndk-module-extract.awk
+
+seven Makefiles in cross/ndk-build,
+
+ cross/ndk-build/ndk-build-shared-library.mk
+ cross/ndk-build/ndk-build-static-library.mk
+ cross/ndk-build/ndk-build-executable.mk
+ cross/ndk-build/ndk-clear-vars.mk
+ cross/ndk-build/ndk-prebuilt-shared-library.mk
+ cross/ndk-build/ndk-prebuilt-static-library.mk
+ cross/ndk-build/ndk-resolve.mk
+
+and finally, two more Makefiles in cross/ndk-build, generated by
+configure:
+
+ cross/ndk-build/Makefile (generated from cross/ndk-build/Makefile.in)
+ cross/ndk-build/ndk-build.mk (generated from cross/ndk-build/ndk-build.mk.in)
+
+m4/ndk-build.m4 is a collection of macros which are used by the
+configure script to set up the ndk-build system, look for modules, add
+the appropriate options to LIBS and CFLAGS, and generate the Makefiles
+necessary to build the rest of Emacs.
+
+Immediately after determining the list of directories in which to look
+for ``Android.mk'' files, the version and type of Android system being
+built for, configure calls:
+
+ ndk_INIT([$android_abi], [$ANDROID_SDK], [cross/ndk-build])
+
+This expands to a sequence of shell script that enumerates all of the
+Android.mk files specified in "$with_ndk_path", sets up some shell
+functions used by the rest of the ndk-build code run by the configure
+script, and teaches the ndk-build system that the Makefiles to be
+generated are found in the directory "cross/ndk-build/Makefile".
+
+When configure is cross-compiling for Android, the macro
+EMACS_CHECK_MODULES will expand to the macro ndk_CHECK_MODULES,
+instead of pkg-config.m4's PKG_CHECK_MODULES. Thus, the following
+code:
+
+ EMACS_CHECK_MODULES([PNG], [libpng >= 1.0.0])
+
+will actually expand to:
+
+ ndk_CHECK_MODULES([PNG], [libpng >= 1.0.0], [HAVE_PNG=yes],
+ [HAVE_PNG=no])
+
+which in turn expands to a sequence shell script that first invokes:
+
+ make -f build-aux/ndk-build-helper.mk
+
+for each ``Android.mk'' file found by ndk_INIT, with the following
+variables given to Make:
+
+ EMACS_SRCDIR=. # the source directory (in which configure is running)
+ BUILD_AUXDIR=$ndk_AUX_DIR # the build-aux directory
+ EMACS_ABI=$ndk_ABI # this is the $android_abi given to ndk_INIT
+ ANDROID_MAKEFILE="/opt/android/libpng/Android.mk"
+ ANDROID_MODULE_DIRECTORY="/opt/android/libpng"
+ NDK_BUILD_DIR="$ndk_DIR" # this is the directory given as to ndk_INIT
+
+build-aux/ndk-build-helper.mk will then evaluate the contents
+$(ANDROID_MAKEFILE), the ``Android.mk'' file, for the first time. The
+purpose of this evaluation is to establish a list of packages (or
+modules) provided by the ``Android.mk'' file, and the corresponding
+Makefile targets and compiler and linker flags required to build and
+link to those tagets.
+
+Before doing so, build-aux/ndk-build-helper.mk will define several
+variables and functions required by all ``Android.mk'' files. The
+most important of these are:
+
+ my-dir # the directory containing the Android.mk file.
+ BUILD_SHARED_LIBRARY # build-aux/ndk-build-helper-1.mk
+ BUILD_STATIC_LIBRARY # build-aux/ndk-build-helper-2.mk
+ BUILD_EXECUTABLE # build-aux/ndk-build-helper-3.mk
+ CLEAR_VARS # build-aux/ndk-build-helper-4.mk
+
+Then, ``Android.mk'' will include $(CLEAN_VARS), possibly other
+``Android.mk'' files, (to clear variables previously set), set several
+variables describing each module to the ndk-build system, and include
+one of $(BUILD_SHARED_LIBRARY), $(BUILD_STATIC_LIBRARY) and
+$(BUILD_EXECUTABLE).
+
+Each one of those three scripts will then read from the variables set
+by ``Android.mk'', resolve dependencies, and print out some text
+describing the module to Emacs. For example, the shared library
+module "libpng" results in the following text being printed:
+
+Building shared
+libpng
+/opt/android/libpng/png.c /opt/android/libpng/pngerror.c /opt/android/libpng/pngget.c /opt/android/libpng/pngmem.c /opt/android/libpng/pngpread.c /opt/android/libpng/pngread.c /opt/android/libpng/pngrio.c /opt/android/libpng/pngrtran.c /opt/android/libpng/pngrutil.c /opt/android/libpng/pngset.c /opt/android/libpng/pngtrans.c /opt/android/libpng/pngwio.c /opt/android/libpng/pngwrite.c /opt/android/libpng/pngwtran.c /opt/android/libpng/pngwutil.c
+-I/opt/android/libpng
+
+ -L/opt/emacs/cross/ndk-build -l:libpng_emacs.so
+libpng_emacs.so
+End
+
+The output is arranged as follows:
+
+ - The first line consists of the word ``Building'', followed by
+ either ``shared'', ``static'', or ``executable'', depending on
+ what type of module being built.
+
+ - The second line consists of the name of the module currently being
+ built.
+
+ - The third line consists of all of the source code files comprising
+ the module.
+
+ - The fourth line consists of the text that has to be added to
+ CFLAGS in order to find the includes associated with the module.
+
+ - The fifth line consists of the text that has to be added to LIBS
+ in order to link with this module and all of its dependencies.
+
+ - The sixth line consists of the Make targets (more on this later)
+ that will build the final shared object or library archive of this
+ module, along with all of its dependencies.
+
+ - The seventh line is either empty, or the name of a dependency on
+ the C++ standard library. This is used to determine whether or
+ not Emacs will include the C++ standard library in the application
+ package.
+
+The output from Make is given to an awk script,
+build-aux/ndk-module-extract.awk. This is responsible for parsing the
+that output and filtering out modules other than what is being built:
+
+ awk -f build-aux/ndk-module-extract.awk MODULE=libpng
+
+eventually generating this section of shell script:
+
+module_name=libpng
+module_kind=shared
+module_src="/opt/android/libpng/png.c /opt/android/libpng/pngerror.c /opt/android/libpng/pngget.c /opt/android/libpng/pngmem.c /opt/android/libpng/pngpread.c /opt/android/libpng/pngread.c /opt/android/libpng/pngrio.c /opt/android/libpng/pngrtran.c /opt/android/libpng/pngrutil.c /opt/android/libpng/pngset.c /opt/android/libpng/pngtrans.c /opt/android/libpng/pngwio.c /opt/android/libpng/pngwrite.c /opt/android/libpng/pngwtran.c /opt/android/libpng/pngwutil.c"
+module_includes="-I/opt/android/libpng"
+module_cflags=""
+module_ldflags=" -L/opt/emacs/cross/ndk-build -l:libpng_emacs.so"
+module_target="libpng_emacs.so"
+module_cxx_deps=""
+module_imports=""
+
+which is then evaluated by `configure'. Once the variable
+`module_name' is set, configure apends the remaining
+$(module_includes), $(module_cflags) and $(module_ldflags) to the
+module's CFLAGS and LIBS variables, and appends the list of Makefile
+targets specified to the variable NDK_BUILD_MODULES.
+
+In some cases, an ``Android.mk'' file may chose to import a module
+defined in ``--with-ndk-path'', but not defined inside its own
+``Android.mk'' file. build-aux/ndk-build-helper.mk defines the
+`import-module' function to add the modules being imported to a
+variable, which is then printed out after ``ndk-build-helper.mk''
+completes. For example, libxml2 imports the ``libicucc'' module,
+which results in the following text being printed:
+
+Building shared
+libxml2
+/home/oldosfan/libxml2/SAX.c /home/oldosfan/libxml2/entities.c /home/oldosfan/libxml2/encoding.c /home/oldosfan/libxml2/error.c /home/oldosfan/libxml2/parserInternals.c /home/oldosfan/libxml2/parser.c /home/oldosfan/libxml2/tree.c /home/oldosfan/libxml2/hash.c /home/oldosfan/libxml2/list.c /home/oldosfan/libxml2/xmlIO.c /home/oldosfan/libxml2/xmlmemory.c /home/oldosfan/libxml2/uri.c /home/oldosfan/libxml2/valid.c /home/oldosfan/libxml2/xlink.c /home/oldosfan/libxml2/debugXML.c /home/oldosfan/libxml2/xpath.c /home/oldosfan/libxml2/xpointer.c /home/oldosfan/libxml2/xinclude.c /home/oldosfan/libxml2/DOCBparser.c /home/oldosfan/libxml2/catalog.c /home/oldosfan/libxml2/globals.c /home/oldosfan/libxml2/threads.c /home/oldosfan/libxml2/c14n.c /home/oldosfan/libxml2/xmlstring.c /home/oldosfan/libxml2/buf.c /home/oldosfan/libxml2/xmlregexp.c /home/oldosfan/libxml2/xmlschemas.c /home/oldosfan/libxml2/xmlschemastypes.c /home/oldosfan/libxml2/xmlunicode.c /home/oldosfan/libxml2/xmlreader.c /home/oldosfan/libxml2/relaxng.c /home/oldosfan/libxml2/dict.c /home/oldosfan/libxml2/SAX2.c /home/oldosfan/libxml2/xmlwriter.c /home/oldosfan/libxml2/legacy.c /home/oldosfan/libxml2/chvalid.c /home/oldosfan/libxml2/pattern.c /home/oldosfan/libxml2/xmlsave.c /home/oldosfan/libxml2/xmlmodule.c /home/oldosfan/libxml2/schematron.c /home/oldosfan/libxml2/SAX.c /home/oldosfan/libxml2/entities.c /home/oldosfan/libxml2/encoding.c /home/oldosfan/libxml2/error.c /home/oldosfan/libxml2/parserInternals.c /home/oldosfan/libxml2/parser.c /home/oldosfan/libxml2/tree.c /home/oldosfan/libxml2/hash.c /home/oldosfan/libxml2/list.c /home/oldosfan/libxml2/xmlIO.c /home/oldosfan/libxml2/xmlmemory.c /home/oldosfan/libxml2/uri.c /home/oldosfan/libxml2/valid.c /home/oldosfan/libxml2/xlink.c /home/oldosfan/libxml2/debugXML.c /home/oldosfan/libxml2/xpath.c /home/oldosfan/libxml2/xpointer.c /home/oldosfan/libxml2/xinclude.c /home/oldosfan/libxml2/DOCBparser.c /home/oldosfan/libxml2/catalog.c /home/oldosfan/libxml2/globals.c /home/oldosfan/libxml2/threads.c /home/oldosfan/libxml2/c14n.c /home/oldosfan/libxml2/xmlstring.c /home/oldosfan/libxml2/buf.c /home/oldosfan/libxml2/xmlregexp.c /home/oldosfan/libxml2/xmlschemas.c /home/oldosfan/libxml2/xmlschemastypes.c /home/oldosfan/libxml2/xmlunicode.c /home/oldosfan/libxml2/xmlreader.c /home/oldosfan/libxml2/relaxng.c /home/oldosfan/libxml2/dict.c /home/oldosfan/libxml2/SAX2.c /home/oldosfan/libxml2/xmlwriter.c /home/oldosfan/libxml2/legacy.c /home/oldosfan/libxml2/chvalid.c /home/oldosfan/libxml2/pattern.c /home/oldosfan/libxml2/xmlsave.c /home/oldosfan/libxml2/xmlmodule.c /home/oldosfan/libxml2/schematron.c
+
+
+ -L/home/oldosfan/emacs-dev/emacs-android/cross/ndk-build -l:libxml2_emacs.so -l:libicuuc_emacs.so
+libxml2_emacs.so libicuuc_emacs.so
+End
+Start Imports
+libicuuc
+End Imports
+
+Upon encountering the ``Start Imports'' section,
+build-aux/ndk-module-extract.awk collects all imports until it
+encounters the line ``End Imports'', at which point it prints:
+
+module_imports="libicuuc"
+
+Then, if the list of imports is not empty, ndk_CHECK_MODULES
+additionally calls itself for each import before appending the
+module's own ``Android.mk'', ensuring that the module's imported
+dependencies are included by $ndk_DIR/Makefile before itself.
+
+Finally, immediately before generating src/Makefile.android, configure
+expands:
+
+ ndk_CONFIG_FILES
+
+to generate $ndk_DIR/Makefile and $ndk_DIR/ndk-build.mk.
+
+Now, the $ndk_DIR directory is set up to build all modules upon which
+depends, and $ndk_DIR/ndk-build.mk includes a list of files required
+to link Emacs, along with the rules to chdir into $ndk_DIR in order to
+build them.
+
+$ndk_DIR/ndk-build.mk is included by cross/src/Makefile
+(Makefile.android) and java/Makefile. It defines three different
+variables:
+
+ NDK_BUILD_MODULES the file names of all modules to be built.
+ NDK_BUILD_STATIC absolute names of all library archives
+ to be built.
+ NDK_BUILD_SHARED absolute names of all shared libraries to
+ be built.
+
+and then proceeds to define rules to build each of the modules in
+$(NDK_BUILD_MODULES).
+
+cross/src/Makefile arranges to have all dependencies of Emacs not
+already built built before linking ``libemacs.so'' with them.
+
+java/Makefile additionally arranges to have all shared object
+dependencies built before the application package is built, which is
+normally redundant because they should have already been built before
+linking ``libemacs.so''.
+
+Building the modules is performed through $ndk_DIR/Makefile, which
+contains the actual implementation of the ``ndk-build'' build system.
+First, it defines certain variables constant within the ``ndk-build''
+build system, such as the files included by ``Android.mk'' to build
+shared or static libraries, and CLEAR_VARS. The most important of
+these are:
+
+ CLEAR_VARS cross/ndk-build/ndk-clear-vars.mk
+ BUILD_EXECUTABLE cross/ndk-build/ndk-build-executable.mk
+ BUILD_SHARED_LIBRARY cross/ndk-build/ndk-build-shared-library.mk
+ BUILD_STATIC_LIBRARY cross/ndk-build/ndk-build-static-library.mk
+ PREBUILT_SHARED_LIBRARY cross/ndk-build/ndk-prebuilt-shared-library.mk
+ PREBUILT_STATIC_LIBRARY cross/ndk-build/ndk-prebuilt-static-library.mk
+
+Then, it loads each Emacs dependency's ``Android.mk'' file. For each
+module defined there, ``Android.mk'' includes $(CLEAR_VARS) to unset
+all variables specific to each module, and then includes
+$(BUILD_SHARED_LIBRARY) or $(BUILD_STATIC_LIBRARY) for each shared or
+static library module.
+
+This results in cross/ndk-build/ndk-build-shared-library.mk or
+cross/ndk-build/ndk-build-static-library being included, just like the
+Makefiles in build-aux were inside the configure script.
+
+Each one of those two scripts then defines rules to build all of the
+object files associated with the module, and then link or archive
+them. The name under which the module is linked is the same as the
+Make target found on the sixth line of output from
+build-aux/ndk-build-helper.mk.
+
+In doing so, they both include the file ndk-resolve.mk.
+ndk-resolve.mk is expected to recursively add all of the exported
+CFLAGS and includes of any dependencies to the compiler and linker
+command lines for the module being built.
+
+When building a shared library module, ndk-resolve.mk is also expected
+to define the variables NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) and
+NDK_WHOLE_A_NAMES_$(LOCAL_MODULE), containing all static library
+dependencies' archive files. They are to be linked in to the
+resulting shared object file.
+
+This is done by including cross/ndk-build/ndk-resolve.mk each time a
+shared or static library module is going to be built. How is this
+done?
+
+First, ndk-resolve.mk saves the LOCAL_PATH, LOCAL_STATIC_LIBRARIES,
+LOCAL_SHARED_LIBRARIES, LOCAL_EXPORT_CFLAGS and
+LOCAL_EXPORT_C_INCLUDES from the module.
+
+Next, ndk-resolve loops through the dependencies the module has
+specified, appending its CFLAGS and includes to the command line for
+the current module.
+
+Then, that process is repeated for each such dependency which has not
+already been resolved, until all dependencies have been resolved.
+
+libpng is a very simple module, providing only a single shared object
+module. This module is named libpng_emacs.so and is eventually built
+and packaged into the library directory of the Emacs application
+package. Now, let us look at a more complex module, libwebp:
+
+
+
+When built with libwebp, Emacs depends on a single library,
+libwebpdemux. This library is named ``libwebpdemux'' on Unix systems,
+and that is the name by which it is found with pkg-config.
+
+However, the library's module is only named ``webpdemux'' on Android.
+When ndk_CHECK_MODULES begins to look for a module, it first tries to
+see if its name is found in the variable `ndk_package_map', which was
+set inside ndk_INIT. In this case, it finds the following word:
+
+ libwebpdemux:webpdemux
+
+and immediately replaces ``libwebpdemux'' with ``webpdemux''.
+
+Then, it locates the ``Android.mk'' file containing a static library
+module named webpdemux and gives the output from
+build-aux/ndk-build-helper.mk to the awk script, resulting in:
+
+module_name=webpdemux
+module_kind=static
+module_src="/opt/android/webp/src/demux/anim_decode.c /opt/android/webp/src/demux/demux.c"
+module_includes="-I/opt/android/webp/src"
+module_cflags=""
+module_ldflags=" cross/ndk-build/libwebpdemux.a cross/ndk-build/libwebp.a cross/ndk-build/libwebpdecoder_static.a "
+module_target="libwebpdemux.a libwebp.a libwebpdecoder_static.a"
+
+The attentive reader will notice that in addition to the
+``libwebpdemux.a'' archive associated with the ``webpdemux'' library,
+Emacs has been made to link with two additional libraries. This is
+because the ``webpdemux'' module specifies a dependency on the
+``webp'' module (defined in the same Android.mk).
+build-aux/ndk-build-helper.mk resolved that dependency, noticing that
+it in turn specified another dependency on ``webpdecoder_static'',
+which in turn was added to the linker command line and list of targets
+to build.
+
+As a result, all three dependencies will be built and linked to Emacs,
+instead of just the single ``webpdemux'' dependency that was
+specified.
+
+
+
+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/>.
diff --git a/cross/ndk-build/ndk-build-executable.mk b/cross/ndk-build/ndk-build-executable.mk
new file mode 100644
index 00000000000..9591c862b18
--- /dev/null
+++ b/cross/ndk-build/ndk-build-executable.mk
@@ -0,0 +1,22 @@
+# Copyright 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/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+# Building executables is not supported
diff --git a/cross/ndk-build/ndk-build-shared-library.mk b/cross/ndk-build/ndk-build-shared-library.mk
new file mode 100644
index 00000000000..d60802da1d0
--- /dev/null
+++ b/cross/ndk-build/ndk-build-shared-library.mk
@@ -0,0 +1,171 @@
+# Copyright 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/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
+
+# Objects for shared libraries are prefixed with `-shared-' in
+# addition to the name of the module, because a common practice in
+# Android.mk files written by Google is to define two modules with the
+# same name but of different types.
+objname = $(1)-shared-$(subst /,_,$(2).o)
+
+# LOCAL_SRC_FILES sometimes contains absolute file names. Filter them
+# out with this function. If $(2), this is a file relative to the
+# build directory.
+maybe-absolute = $(or $(and $(2),$(1)),$(and $(wildcard $(1)),$(1)),$(LOCAL_PATH)/$(1))
+
+# Here are the default flags to link shared libraries with.
+NDK_SO_DEFAULT_LDFLAGS := -lc -lm
+
+define single-object-target
+
+ifeq (x$(suffix $(1)),x.c)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
+ $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_BUILD_CFLAGS) $(call LOCAL_C_ADDITIONAL_FLAGS,$(1))
+
+else
+ifeq (x$(suffix $(1)),x.$(or $(LOCAL_CPP_EXTENSION),cpp))
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1))
+ $(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_BUILD_CFLAGS_CXX) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
+
+else
+ifneq ($(or $(call eq,x$(suffix $(1)),x.s),$(call eq,x$(suffix $(1)),x.S)),)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
+ $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_ASFLAGS_$(LOCAL_MODULE))
+
+else
+ifneq (x$(suffix $(1)),x.asm)
+ifeq (x$(suffix $(1)),x.cc)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
+ $(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_BUILD_CFLAGS_CXX) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
+
+else
+$$(error Unsupported suffix: $(suffix $(1)))
+endif
+else
+ifneq (x$(LOCAL_ASM_RULE_DEFINED),x)
+# Call this function to define a rule that will generate $(1) from
+# $(2), a ``.asm'' file. This is an Emacs extension.
+
+$(call LOCAL_ASM_RULE,$(call objname,$(LOCAL_MODULE),$(basename $(1))),$(LOCAL_PATH)/$(strip $(1)))
+
+else
+ifeq ($(findstring x86,$(NDK_BUILD_ARCH)),)
+$$(error Trying to build nasm file on non-Intel platform!)
+else
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(LOCAL_PATH)/$(1)
+ $(NDK_BUILD_NASM) -felf$(findstring 64,$(NDK_BUILD_ARCH)) -o $$@ -i $(LOCAL_PATH) -i $$(dir $$<) $(NDK_ASFLAGS_$(LOCAL_MODULE)) $$<
+
+endif
+endif
+endif
+endif
+endif
+endif
+
+ALL_OBJECT_FILES$(LOCAL_MODULE) += $(call objname,$(LOCAL_MODULE),$(basename $(1)))
+
+endef
+
+define single-neon-target
+
+# Define rules for the target.
+$$(eval $$(call single-object-target,$(patsubst %.neon,%,$(1)),))
+
+endef
+
+# Make sure to not add a prefix to local includes that already specify
+# $(LOCAL_PATH).
+NDK_CFLAGS_$(LOCAL_MODULE) := $(addprefix -I,$(LOCAL_C_INCLUDES))
+NDK_CFLAGS_$(LOCAL_MODULE) += -fPIC -iquote $(LOCAL_PATH) $(LOCAL_EXPORT_CFLAGS) $(LOCAL_CFLAGS) $(LOCAL_CFLAGS_$(NDK_BUILD_ARCH))
+NDK_ASFLAGS_$(LOCAL_MODULE) := $(LOCAL_ASFLAGS) $(LOCAL_ASFLAGS_$(NDK_BUILD_ARCH)) $(and $(findstring clang,$(NDK_BUILD_CC)),$(LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH)))
+NDK_LDFLAGS_$(LOCAL_MODULE) := $(LOCAL_LDLIBS) $(LOCAL_LDFLAGS)
+NDK_CXXFLAGS_$(LOCAL_MODULE) := $(LOCAL_CPPFLAGS) $(LOCAL_RTTI_FLAG)
+
+# Now look for features in LOCAL_CPP_FEATURES and enable them.
+
+ifneq ($(findstring exceptions,$(LOCAL_CPPFLAGS)),)
+NDK_CXXFLAGS_$(LOCAL_MODULE) += -fexceptions
+endif
+
+ifneq ($(findstring rtti,$(LOCAL_CPPFLAGS)),)
+NDK_CXXFLAGS_$(LOCAL_MODULE) += -frtti
+endif
+
+ALL_OBJECT_FILES$(LOCAL_MODULE) :=
+
+ifeq ($(NDK_BUILD_ARCH)$(NDK_ARM_MODE),armarm)
+NDK_CFLAGS ::= -marm
+else
+ifeq ($(NDK_BUILD_ARCH),arm)
+NDK_CFLAGS ::= -mthumb
+endif
+endif
+
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE)_emacs
+else
+LOCAL_MODULE_FILENAME := lib$(LOCAL_MODULE)_emacs
+endif
+
+# Since a shared library is being built, suffix the library with
+# _emacs. Otherwise, libraries already on the system will be found
+# first, with potentially nasty consequences.
+
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE_FILENAME).so
+
+# Record this module's dependencies and exported includes and CFLAGS,
+# and then add that of its dependencies.
+
+include $(srcdir)/ndk-resolve.mk
+
+# Then define rules to build all objects.
+ALL_SOURCE_FILES := $(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES_$(NDK_BUILD_ARCH))
+
+# This defines all dependencies.
+ALL_OBJECT_FILES$(LOCAL_MODULE) :=
+
+# Now filter out code that is built with neon. Define rules to build
+# those separately.
+NEON_SOURCE_FILES := $(filter %.neon,$(ALL_SOURCE_FILES))
+ALL_SOURCE_FILES := $(filter-out %.neon,$(ALL_SOURCE_FILES))
+
+$(foreach source,$(ALL_SOURCE_FILES),$(eval $(call single-object-target,$(source),)))
+$(foreach source,$(NEON_SOURCE_FILES),$(eval $(call single-neon-target,$(source))))
+
+# Now define the rule to build the shared library. Shared libraries
+# link with all of the archive files from the static libraries on
+# which they depend, and also any shared libraries they depend on.
+
+define define-module-rule
+$(LOCAL_MODULE_FILENAME): $(ALL_OBJECT_FILES$(LOCAL_MODULE)) $(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)) $(NDK_WHOLE_A_NAMES_$(LOCAL_MODULE)) $(NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE))
+ $(NDK_BUILD_CC) $(1) $(2) -o $$@ -shared $(NDK_LDFLAGS_$(LOCAL_MODULE)) $(NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE)) $(NDK_SO_DEFAULT_LDFLAGS) $(foreach so,$(NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE)),-L $(abspath $(CURDIR)) -l:$(so))
+endef
+
+NDK_WHOLE_ARCHIVE_PREFIX = -Wl,--whole-archive
+NDK_WHOLE_ARCHIVE_SUFFIX = -Wl,--no-whole-archive
+
+$(eval $(call define-module-rule,$(ALL_OBJECT_FILES$(LOCAL_MODULE)) $(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)),$(and $(strip $(NDK_WHOLE_A_NAMES_$(LOCAL_MODULE))),$(NDK_WHOLE_ARCHIVE_PREFIX) $(NDK_WHOLE_A_NAMES_$(LOCAL_MODULE)) $(NDK_WHOLE_ARCHIVE_SUFFIX))))
diff --git a/cross/ndk-build/ndk-build-static-library.mk b/cross/ndk-build/ndk-build-static-library.mk
new file mode 100644
index 00000000000..98afd864ed6
--- /dev/null
+++ b/cross/ndk-build/ndk-build-static-library.mk
@@ -0,0 +1,142 @@
+# Copyright 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/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+eq = $(and $(findstring $(1),$(2)),$(findstring $(2),$(1)))
+objname = $(1)-static-$(subst /,_,$(2).o)
+maybe-absolute = $(or $(and $(2),$(1)),$(and $(wildcard $(1)),$(1)),$(LOCAL_PATH)/$(1))
+
+define single-object-target
+
+ifeq (x$(suffix $(1)),x.c)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
+ $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_BUILD_CFLAGS) $(NDK_CFLAGS_$(LOCAL_MODULE)) $(call LOCAL_C_ADDITIONAL_FLAGS,$(1))
+
+else
+ifeq (x$(suffix $(1)),x.$(or $(LOCAL_CPP_EXTENSION),cpp))
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
+ $(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_BUILD_CFLAGS_CXX) $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
+
+else
+ifneq ($(or $(call eq,x$(suffix $(1)),x.s),$(call eq,x$(suffix $(1)),x.S)),)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
+ $(NDK_BUILD_CC) -c $$< -o $$@ $(NDK_ASFLAGS_$(LOCAL_MODULE))
+
+else
+ifneq (x$(suffix $(1)),x.asm)
+ifeq (x$(suffix $(1)),x.cc)
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
+ $(NDK_BUILD_CXX) -c $$< -o $$@ $(NDK_BUILD_CFLAGS_CXX) $(NDK_CFLAGS_$(LOCAL_MODULE)) $(NDK_CXXFLAGS_$(LOCAL_MODULE))
+
+else
+$$(error Unsupported suffix: $(suffix $(1)))
+endif
+else
+ifneq (x$(LOCAL_ASM_RULE_DEFINED),x)
+# Call this function to define a rule that will generate $(1) from
+# $(2), a ``.asm'' file. This is an Emacs extension.
+
+$(call LOCAL_ASM_RULE,$(call objname,$(LOCAL_MODULE),$(basename $(1))),$(LOCAL_PATH)/$(strip $(1)))
+
+else
+ifeq ($(findstring x86,$(NDK_BUILD_ARCH)),)
+$$(error Trying to build nasm file on non-Intel platform!)
+else
+
+$(call objname,$(LOCAL_MODULE),$(basename $(1))): $(call maybe-absolute,$(1),$(2))
+ $(NDK_BUILD_NASM) -felf$(findstring 64,$(NDK_BUILD_ARCH)) -o $$@ -i $(LOCAL_PATH) -i $$(dir $$<) $(NDK_ASFLAGS_$(LOCAL_MODULE)) $$<
+
+endif
+endif
+endif
+endif
+endif
+endif
+
+ALL_OBJECT_FILES$(LOCAL_MODULE) += $(call objname,$(LOCAL_MODULE),$(basename $(1)))
+endef
+
+define single-neon-target
+
+# Define rules for the target.
+$$(eval $$(call single-object-target,$(patsubst %.neon,%,$(1)),))
+
+endef
+
+NDK_CFLAGS_$(LOCAL_MODULE) := $(addprefix -I,$(LOCAL_C_INCLUDES))
+NDK_CFLAGS_$(LOCAL_MODULE) += -fPIC -iquote $(LOCAL_PATH) $(LOCAL_EXPORT_CFLAGS) $(LOCAL_CFLAGS) $(LOCAL_CFLAGS_$(NDK_BUILD_ARCH))
+NDK_ASFLAGS_$(LOCAL_MODULE) := $(LOCAL_ASFLAGS) $(LOCAL_ASFLAGS_$(NDK_BUILD_ARCH)) $(and $(findstring clang,$(NDK_BUILD_CC)),$(LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH)))
+NDK_LDFLAGS_$(LOCAL_MODULE) := $(LOCAL_LDLIBS) $(LOCAL_LDFLAGS)
+NDK_CXXFLAGS_$(LOCAL_MODULE) := $(LOCAL_CPPFLAGS) $(LOCAL_RTTI_FLAG)
+ALL_OBJECT_FILES$(LOCAL_MODULE) :=
+
+# Now look for features in LOCAL_CPP_FEATURES and enable them.
+
+ifneq ($(findstring exceptions,$(LOCAL_CPPFLAGS)),)
+NDK_CXXFLAGS_$(LOCAL_MODULE) += -fexceptions
+endif
+
+ifneq ($(findstring rtti,$(LOCAL_CPPFLAGS)),)
+NDK_CXXFLAGS_$(LOCAL_MODULE) += -frtti
+endif
+
+
+ifeq ($(NDK_BUILD_ARCH)$(NDK_ARM_MODE),armarm)
+NDK_CFLAGS ::= -marm
+else
+ifeq ($(NDK_BUILD_ARCH),arm)
+NDK_CFLAGS ::= -mthumb
+endif
+endif
+
+ifeq ($(findstring lib,$(LOCAL_MODULE)),lib)
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE)
+else
+LOCAL_MODULE_FILENAME := lib$(LOCAL_MODULE)
+endif
+
+LOCAL_MODULE_FILENAME := $(LOCAL_MODULE_FILENAME).a
+
+# Record this module's dependencies and exported includes and CFLAGS,
+# and then add that of its dependencies.
+
+include $(srcdir)/ndk-resolve.mk
+
+# Then define rules to build all objects.
+ALL_SOURCE_FILES := $(LOCAL_SRC_FILES) $(LOCAL_SRC_FILES_$(NDK_BUILD_ARCH))
+
+# Now filter out code that is built with neon. Define rules to build
+# those separately.
+NEON_SOURCE_FILES := $(filter %.neon,$(ALL_SOURCE_FILES))
+ALL_SOURCE_FILES := $(filter-out %.neon,$(ALL_SOURCE_FILES))
+
+# This defines all dependencies.
+ALL_OBJECT_FILES$(LOCAL_MODULE) =
+
+$(foreach source,$(ALL_SOURCE_FILES),$(eval $(call single-object-target,$(source),)))
+$(foreach source,$(NEON_SOURCE_FILES),$(eval $(call single-neon-target,$(source),)))
+
+# Now define the rule to build the library.
+$(LOCAL_MODULE_FILENAME): $(ALL_OBJECT_FILES$(LOCAL_MODULE))
+ $(NDK_BUILD_AR) r $@ $^
diff --git a/cross/ndk-build/ndk-build.mk.in b/cross/ndk-build/ndk-build.mk.in
new file mode 100644
index 00000000000..57006901721
--- /dev/null
+++ b/cross/ndk-build/ndk-build.mk.in
@@ -0,0 +1,68 @@
+### @configure_input@
+
+# 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/>.
+
+# This file is included all over the place to get and build
+# prerequisites.
+
+NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
+NDK_BUILD_CXX_SHARED = @NDK_BUILD_CXX_SHARED@
+NDK_BUILD_ANY_CXX_MODULE = @NDK_BUILD_ANY_CXX_MODULE@
+NDK_BUILD_SHARED =
+NDK_BUILD_STATIC =
+
+define uniqify
+$(if $1,$(firstword $1) $(call uniqify,$(filter-out $(firstword $1),$1)))
+endef
+
+# Remove duplicate modules. These can occur when a single module
+# imports a module and also declares it in LOCAL_SHARED_LIBRARIES.
+NDK_BUILD_MODULES := $(call uniqify,$(NDK_BUILD_MODULES))
+
+# Here are all of the files to build.
+NDK_BUILD_ALL_FILES := $(foreach file,$(NDK_BUILD_MODULES), \
+ $(top_builddir)/cross/ndk-build/$(file))
+
+# The C++ standard library must be extracted from the Android NDK
+# directories and included in the application package, if any module
+# requires the C++ standard library.
+
+ifneq ($(NDK_BUILD_ANY_CXX_MODULE),)
+NDK_BUILD_SHARED += $(NDK_BUILD_CXX_SHARED)
+endif
+
+define subr-1
+ifeq ($(suffix $(1)),.so)
+NDK_BUILD_SHARED += $(top_builddir)/cross/ndk-build/$(1)
+else
+ifeq ($(suffix $(1)),.a)
+NDK_BUILD_STATIC += $(top_builddir)/cross/ndk-build/$(1)
+endif
+endif
+endef
+
+# Generate rules for each module.
+
+$(foreach module,$(NDK_BUILD_MODULES),$(eval $(call subr-1,$(module))))
+
+# Generate rules to build everything now.
+# Make sure to use the top_builddir currently defined.
+
+NDK_TOP_BUILDDIR := $(top_builddir)
+$(NDK_BUILD_ALL_FILES) &:
+ $(MAKE) -C $(NDK_TOP_BUILDDIR)/cross/ndk-build $(NDK_BUILD_MODULES)
diff --git a/cross/ndk-build/ndk-clear-vars.mk b/cross/ndk-build/ndk-clear-vars.mk
new file mode 100644
index 00000000000..7309b7bb513
--- /dev/null
+++ b/cross/ndk-build/ndk-clear-vars.mk
@@ -0,0 +1,57 @@
+# Copyright 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/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+LOCAL_MODULE :=
+LOCAL_MODULE_FILENAME :=
+LOCAL_SRC_FILES :=
+LOCAL_CPP_EXTENSION :=
+LOCAL_CPP_FEATURES :=
+LOCAL_C_INCLUDES :=
+LOCAL_CFLAGS :=
+LOCAL_CPPFLAGS :=
+LOCAL_STATIC_LIBRARIES :=
+LOCAL_SHARED_LIBRARIES :=
+LOCAL_WHOLE_STATIC_LIBRARIES :=
+LOCAL_LDLIBS :=
+LOCAL_LDFLAGS :=
+LOCAL_ALLOW_UNDEFINED_SYMBOLS :=
+LOCAL_ARM_MODE :=
+LOCAL_ARM_NEON :=
+LOCAL_DISABLE_FORMAT_STRING_CHECKS :=
+LOCAL_EXPORT_CFLAGS :=
+LOCAL_EXPORT_CPPFLAGS :=
+LOCAL_EXPORT_C_INCLUDES :=
+LOCAL_EXPORT_C_INCLUDE_DIRS :=
+LOCAL_EXPORT_LDFLAGS :=
+LOCAL_EXPORT_LDLIBS :=
+
+# AOSP extensions.
+LOCAL_SRC_FILES_$(NDK_BUILD_ARCH) :=
+LOCAL_ASFLAGS_$(NDK_BUILD_ARCH) :=
+LOCAL_CFLAGS_$(NDK_BUILD_ARCH) :=
+LOCAL_ADDITIONAL_DEPENDENCIES :=
+LOCAL_CLANG_ASFLAGS_$(NDK_BUILD_ARCH) :=
+LOCAL_IS_HOST_MODULE :=
+
+# Emacs extensions!
+LOCAL_ASM_RULE_DEFINED :=
+LOCAL_ASM_RULE :=
+LOCAL_C_ADDITIONAL_FLAGS :=
diff --git a/cross/ndk-build/ndk-prebuilt-shared-library.mk b/cross/ndk-build/ndk-prebuilt-shared-library.mk
new file mode 100644
index 00000000000..2a8260f9851
--- /dev/null
+++ b/cross/ndk-build/ndk-prebuilt-shared-library.mk
@@ -0,0 +1,24 @@
+### @configure_input@
+
+# Copyright 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/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+$(warn Prebuilt shared libraries are not supported)
diff --git a/cross/ndk-build/ndk-prebuilt-static-library.mk b/cross/ndk-build/ndk-prebuilt-static-library.mk
new file mode 100644
index 00000000000..9230f690bb1
--- /dev/null
+++ b/cross/ndk-build/ndk-prebuilt-static-library.mk
@@ -0,0 +1,24 @@
+### @configure_input@
+
+# Copyright 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/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+$(warn Prebuilt static libraries are not supported)
diff --git a/cross/ndk-build/ndk-resolve.mk b/cross/ndk-build/ndk-resolve.mk
new file mode 100644
index 00000000000..b29a2c6dc39
--- /dev/null
+++ b/cross/ndk-build/ndk-resolve.mk
@@ -0,0 +1,162 @@
+# Copyright 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/>.
+
+# ndk-build works by including a bunch of Makefiles which set
+# variables, and then having those Makefiles include another makefile
+# which actually builds targets.
+
+# List of system libraries to ignore.
+NDK_SYSTEM_LIBRARIES = z libz libc c libdl dl stdc++ libstdc++ log liblog android libandroid
+
+# Save information.
+NDK_LOCAL_PATH_$(LOCAL_MODULE) := $(LOCAL_PATH)
+NDK_LOCAL_STATIC_LIBRARIES_$(LOCAL_MODULE) := $(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES)
+NDK_LOCAL_WHOLE_LIBRARIES_$(LOCAL_MODULE) := $(LOCAL_WHOLE_STATIC_LIBRARIES)
+NDK_LOCAL_SHARED_LIBRARIES_$(LOCAL_MODULE) := $(LOCAL_SHARED_LIBRARIES)
+NDK_LOCAL_EXPORT_CFLAGS_$(LOCAL_MODULE) := $(LOCAL_EXPORT_CFLAGS)
+NDK_LOCAL_EXPORT_C_INCLUDES_$(LOCAL_MODULE) := $(LOCAL_EXPORT_C_INCLUDES) $(LOCAL_EXPORT_C_INCLUDE_DIRS)
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) :=
+NDK_WHOLE_A_NAMES_$(LOCAL_MODULE) :=
+NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE) :=
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) :=
+
+# List of all dependencies resolved for this module thus far.
+# Used to avoid infinite recursion.
+# Separate the variable which lists modules for which CFLAGS
+# have been resolved from the variable which lists modules
+# for which library dependencies have been resolved, in order
+# to catch the case where a library dependency is skipped
+# despite its CFLAGS being added.
+NDK_RESOLVED_$(LOCAL_MODULE) :=
+NDK_RESOLVED_CFLAGS_$(LOCAL_MODULE) :=
+
+define ndk-resolve
+
+ifeq ($$(filter $(1)$(and $(3),whole),$$(NDK_RESOLVED_CFLAGS_$(LOCAL_MODULE))),)
+# Always mark this module's cflags as having been resolved, even if
+# this is a whole library.
+NDK_RESOLVED_CFLAGS_$(LOCAL_MODULE) += $(1)
+
+NDK_CFLAGS_$(LOCAL_MODULE) += $(NDK_LOCAL_EXPORT_CFLAGS_$(1))
+NDK_CFLAGS_$(LOCAL_MODULE) += $(addprefix -I,$(NDK_LOCAL_EXPORT_C_INCLUDES_$(1)))
+endif
+
+ifeq ($$(filter $(1)$(and $(3),whole),$$(NDK_RESOLVED_$(LOCAL_MODULE))),)
+# Now append local libraries, as long as this library isn't a shared
+# library itself.
+ifeq ($(4),)
+
+# Mark this module's library dependencies as having been resolved.
+NDK_RESOLVED_$(LOCAL_MODULE) += $(1)
+
+# If this is a whole library, then mark this as resolved too, and
+# remove the library from the normal static library list.
+ifneq ($(3),)
+NDK_RESOLVED_$(LOCAL_MODULE) += $(1)whole
+endif
+
+# If the module happens to be zlib, then add -lz to the shared library
+# flags.
+ifeq ($(strip $(1)),libz)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lz
+endif
+
+ifeq ($(strip $(1)),z)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lz
+endif
+
+# Likewise for libdl.
+ifeq ($(strip $(1)),libdl)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -ldl
+endif
+
+ifeq ($(strip $(1)),dl)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -ldl
+endif
+
+# Likewise for libstdc++.
+ifeq ($(strip $(1)),libstdc++)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lstdc++
+endif
+
+ifeq ($(strip $(1)),dl)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -lstdc++
+endif
+
+# Likewise for liblog.
+ifeq ($(strip $(1)),liblog)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -llog
+endif
+
+ifeq ($(strip $(1)),log)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -llog
+endif
+
+# Likewise for libandroid.
+ifeq ($(strip $(1)),libandroid)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -landroid
+endif
+
+ifeq ($(strip $(1)),android)
+NDK_SO_EXTRA_FLAGS_$(LOCAL_MODULE) += -landroid
+endif
+
+ifeq ($(findstring $(1),$(NDK_SYSTEM_LIBRARIES))$(2)$(3),)
+ifneq ($(findstring lib,$(1)),)
+NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE) += $(1)_emacs.so
+else
+NDK_LOCAL_SO_NAMES_$(LOCAL_MODULE) += lib$(1)_emacs.so
+endif
+endif
+
+ifneq ($(2),)
+ifneq ($(findstring lib,$(1)),)
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) += $(1).a
+else
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) += lib$(1).a
+endif
+endif
+
+ifneq ($(3),)
+ifneq ($(findstring lib,$(1)),)
+NDK_WHOLE_A_NAMES_$(LOCAL_MODULE) += $(1).a
+else
+NDK_WHOLE_A_NAMES_$(LOCAL_MODULE) += lib$(1).a
+endif
+
+# Remove this archive from the regular archive list, should it already
+# exists. Any given archive should only appear once, and if an
+# archive has been specified as whole it should always be whole.
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) := $$(filter-out lib$(1).a,$$(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)))
+NDK_LOCAL_A_NAMES_$(LOCAL_MODULE) := $$(filter-out $(1).a,$$(NDK_LOCAL_A_NAMES_$(LOCAL_MODULE)))
+endif
+endif
+
+$$(foreach module,$$(NDK_LOCAL_STATIC_LIBRARIES_$(1)),$$(eval $$(call ndk-resolve,$$(module),1,,$(or $(4),$(if $(2)$(3),,1)))))
+$$(foreach module,$$(NDK_LOCAL_SHARED_LIBRARIES_$(1)),$$(eval $$(call ndk-resolve,$$(module),,,$(or $(4),$(if $(2)$(3),,1)))))
+$$(foreach module,$$(NDK_LOCAL_WHOLE_LIBRARIES_$(1)),$$(eval $$(call ndk-resolve,$$(module),,1,$(or $(4),$(if $(2)$(3),,1)))))
+endif
+
+endef
+
+# Add shared libraries to the shared object names when they appear as
+# a top level dependency. However, do not recursively add the names
+# of this module's shared library dependencies, if it is just a shared
+# library, since it will link to those shared libraries itself.
+$(foreach module,$(LOCAL_SHARED_LIBRARIES),$(eval $(call ndk-resolve,$(module),,,)))
+$(foreach module,$(LOCAL_STATIC_LIBRARIES),$(eval $(call ndk-resolve,$(module),1,,)))
+$(foreach module,$(LOCAL_WHOLE_STATIC_LIBRARIES), $(eval $(call ndk-resolve,$(module),,1,)))
diff --git a/cross/verbose.mk.android b/cross/verbose.mk.android
new file mode 100644
index 00000000000..998f9843c7d
--- /dev/null
+++ b/cross/verbose.mk.android
@@ -0,0 +1,55 @@
+### verbose.mk --- Makefile fragment for GNU Emacs during
+### cross-compilation.
+
+## 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/>.
+
+# 'make' verbosity.
+V = 0
+ifeq (${V},1)
+AM_V_AR =
+AM_V_at =
+AM_V_CC =
+AM_V_CXX =
+AM_V_CCLD =
+AM_V_CXXLD =
+AM_V_GEN =
+else
+
+# Whether $(info ...) works. This is to work around a bug in GNU Make
+# 4.3 and earlier, which implements $(info MSG) via two system calls
+# { write (..., "MSG", 3); write (..., "\n", 1); }
+# which looks bad when make -j interleaves two of these at about the same time.
+#
+# Later versions of GNU Make have the 'notintermediate' feature,
+# so assume that $(info ...) works if this feature is present.
+#
+have_working_info = $(filter notintermediate,$(value .FEATURES))
+#
+# The workaround is to use the shell and 'echo' rather than $(info ...).
+# The workaround is done only for AM_V_ELC and AM_V_ELN,
+# since the bug is not annoying elsewhere.
+
+AM_V_AR = @$(info $ AR $@)
+AM_V_at = @
+AM_V_CC = @$(info $ CC $@)
+AM_V_CXX = @$(info $ CXX $@)
+AM_V_CCLD = @$(info $ CCLD $@)
+AM_V_CXXLD = @$(info $ CXXLD $@)
+AM_V_GEN = @$(info $ GEN $@)
+AM_V_NO_PD = --no-print-directory
+endif
diff --git a/doc/emacs/Makefile.in b/doc/emacs/Makefile.in
index 161bdcb1c59..c7415312753 100644
--- a/doc/emacs/Makefile.in
+++ b/doc/emacs/Makefile.in
@@ -146,6 +146,8 @@ EMACSSOURCES= \
${srcdir}/glossary.texi \
${srcdir}/ack.texi \
${srcdir}/kmacro.texi \
+ ${srcdir}/android.texi \
+ ${srcdir}/input.texi \
$(EMACS_XTRA)
## Disable implicit rules.
diff --git a/doc/emacs/android.texi b/doc/emacs/android.texi
new file mode 100644
index 00000000000..d7fadd69e4b
--- /dev/null
+++ b/doc/emacs/android.texi
@@ -0,0 +1,647 @@
+@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 Android
+@appendix Emacs and Android
+@cindex Android
+
+ Android is a mobile operating system developed by the Open Handset
+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 and on-screen keyboard. For more information
+about using such devices with Emacs, @pxref{Other Input Devices}.
+
+@menu
+* What is Android?:: Preamble.
+* Android Startup:: Starting up Emacs on Android.
+* Android Environment:: Running Emacs under Android.
+* Android File System:: The Android file system.
+* Android Windowing:: The Android window system.
+* Android Fonts:: Font selection under Android.
+* Android Troubleshooting:: Dealing with problems.
+@end menu
+
+@node What is Android?
+@section Android history
+
+ Android is an operating system for mobile devices developed by the
+Open Handset Alliance, a group of companies interested in developing
+handsets that can run a common set of software. It is supposedly free
+software.
+
+ Like the X Consortium of times past, the Open Handset Alliance
+believes that ``openness'' (namely, the regular release of the Android
+source code) is simply a tool to increase the popularity of the
+Android platform. Computer companies normally produce proprietary
+software. The companies in the Open Handset Alliance are no different
+-- most versions of Android installed on devices are proprietary, by
+virtue of containing proprietary components, that often cannot even be
+replaced by the user.
+
+ Android is not designed to respect users' freedom. Almost all
+versions of Android (including some which are supposedly free
+software) include support for Digital Restrictions Management,
+technology that is designed to limit users' ability to copy media to
+and from their own devices. Most Android devices also come with
+proprietary Google applications which are required to run the system,
+and many other Android applications.
+
+ Thus, it must be necessary to consider Android proprietary software
+from a practical standpoint. That is an injustice. If you use
+Android, we urge you to switch to a free operating system, if only for
+your freedom's sake.
+
+ We support GNU Emacs on proprietary operating systems because we
+hope this taste of freedom will inspire users to escape from them.
+
+@node Android Startup
+@section Starting up Emacs on Android
+
+ Emacs is not installed on Android devices from source code, or by a
+package manager. Instead, Emacs is compiled for Android on a
+different operating system, with the resulting binaries packaged into
+an archive, that is then transferred to the device and unpacked.
+
+ After being unpacked, Emacs instructs the system to display an
+application icon on the desktop. Emacs then starts up once the
+application icon is clicked.
+
+@cindex ``adb logcat''
+
+ During startup, Emacs will display messages in the system log
+buffer; reading that buffer requires the Android Debug Bridge
+(@command{adb}) utility to be installed on another computer; it cannot
+be read on the computer running Android itself.
+
+ After enabling the ``USB Debugging'' feature on the Android system,
+and connecting it via USB to another system with the @command{adb}
+utility installed, the log can be viewed by running the following
+command on that other system:
+
+@example
+$ adb logcat | grep -E "(android_run_debug_thread|[Ee]macs)"
+@end example
+
+ Assuming that the @command{adb} utility is installed on a GNU/Linux
+or Unix system, follow the steps below to connect to your device.
+
+@enumerate
+@item
+Enable ``developer options'' on your device, by going to the ``About''
+page in the system settings application and clicking on the ``build
+version'' or ``kernel version'' items five to seven times.
+
+@item
+Open the ``developer options'' settings page, which should be under
+the ``system'' page in the settings application.
+
+@item
+Turn on the switch ``USB debugging''.
+
+@item
+Connect one end of a USB cable to your device, and the other end to
+your computer's USB port.
+
+@item
+Run the command @command{adb shell} on your computer. This will fail
+or hang because you have not yet granted your computer permission to
+access the connected device.
+
+@item
+Confirm the pop-up displayed on your device asking whether or not it
+should allow access from your computer.
+@end enumerate
+
+ Depending on the versions of Android and @command{adb} installed,
+there may be other ways to establish a connection. See the official
+documentation at
+@url{https://developer.android.com/studio/command-line/adb} for more
+details.
+
+@cindex emacsclient wrapper, android
+ Since there is no other way to start the @command{emacsclient}
+program (@pxref{Emacs Server}) from another Android program, Emacs
+provides a wrapper around the @command{emacsclient} program, which is
+registered with the system as an application that can open all text
+files.
+
+ When that wrapper is selected as the program with which to open a
+file, it invokes @command{emacsclient} with the options
+@command{--reuse-frame}, @command{--timeout=10}, @command{--no-wait},
+and the name of the file being opened. Then, upon success, the focus
+is transferred to any open Emacs frame.
+
+ However, if Emacs is not running at the time the wrapper is opened,
+it starts Emacs and gives it the file to open as an argument. Note
+that if that Emacs in turn does not start the Emacs server, subsequent
+attempts to open the file with the wrapper will fail.
+
+@cindex /content directory, android
+ Some files are given to Emacs as ``content identifiers'', which the
+system provides access to outside the normal filesystem APIs. Emacs
+internally supports a temporary @file{/content} directory which is
+used to access those files. Do not make any assumptions about the
+contents of this directory, or try to open files in it yourself.
+
+ This feature is not provided on Android 4.3 and earlier, in which
+case the file is copied to a temporary directory instead.
+
+@node Android File System
+@section What files Emacs can access under Android
+@cindex /assets directory, android
+
+ Emacs exposes a special directory on Android systems: the name of
+the directory is @file{/assets}, and it contains the @file{etc},
+@file{lisp} and @file{info} directories which are normally installed
+in @file{/usr/share/emacs} directory on GNU and Unix systems. On
+Android systems, the Lisp emulation of @command{ls} (@pxref{ls in
+Lisp}) is also enabled by default, as the @command{ls} binary which
+comes with the system varies by manufacturer and usually does not
+support all of the features required by Emacs. One copy of
+@command{ls} shipped with some Android devices is even known to lack
+support for the @code{-l} flag.
+
+@cindex limitations of the /assets directory
+
+ This directory exists because Android does not extract the contents
+of application packages on to the file system while unpacking them,
+but instead requires programs like Emacs to access its contents using
+a special ``asset manager'' interface. Here are the peculiarities
+that result from such an implementation:
+
+@itemize @bullet
+@item
+Subprocesses (such as @command{ls}) can not run from the
+@file{/assets} directory; if you try to run a subprocess with
+@code{current-directory} set to @file{/assets} or a subdirectory
+thereof, it will run from the home directory instead.
+
+@item
+There are no @file{.} and @file{..} directories inside the
+@file{/assets} directory.
+
+@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.
+@end itemize
+
+ Aside from the @file{/assets} directory, Android programs normally
+have access to three other directories. They are:
+
+@itemize @bullet
+@item
+The @dfn{app data} directory. This also serves as the home directory
+for Emacs, and is always accessible read-write.
+
+@item
+The @dfn{app library} directory. This is automatically appended to
+@code{exec-path} upon startup.
+
+@item
+The @dfn{external storage} directory. This is accessible to Emacs
+when the user grants the ``Files and Media'' permission to Emacs via
+system settings.
+@end itemize
+
+ The external storage directory is found at @file{/sdcard}. The
+other directories are not found at any fixed location, although the
+app data directory is typically symlinked to
+@file{/data/data/org.gnu.emacs}.
+
+@cindex temp~unlinked.NNNN files, Android
+ On Android devices running very old (2.6.29) versions of the Linux
+kernel, Emacs needs to create files named starting with
+@file{temp~unlinked} in the the temporary file directory in order to
+read from asset files. Do not create files with such names yourself,
+or they may be overwritten or removed.
+
+@cindex file system limitations, Android 11
+ On Android 11 and later, the Android system restricts applications
+from accessing files in the @file{/sdcard} directory using
+file-related system calls such as @code{open} and @code{readdir}.
+
+ This restriction is known as ``Scoped Storage'', and supposedly
+makes the system more secure. Unfortunately, it also means that Emacs
+cannot access files in those directories, despite holding the
+necessary permissions. Thankfully, the Open Handset Alliance's
+version of Android allows this restriction to be disabled on a
+per-program basis; the corresponding option in the system settings
+panel is:
+
+@example
+System -> Apps -> Special App Access -> All files access -> Emacs
+@end example
+
+ After you disable or enable this setting as appropriate and grant
+Emacs the ``Files and Media'' permission, it will be able to access
+files under @file{/sdcard} as usual.
+
+ These settings are not present on many proprietary versions of
+Android.
+
+@node Android Environment
+@section Running Emacs under Android
+
+ From the perspective of users, Android is mostly a single user
+operating system; however, from the perspective of applications and
+Emacs, the system has an overwhelming number of users.
+
+ Each application runs in its own user, with his own home directory,
+which is the app data directory (@pxref{Android File System}.)
+
+ Each application is also prohibited from accessing system
+directories, and the app data directories of other applications.
+
+ Emacs comes with several binaries. While being executable files,
+they are packaged as libraries in the library directory, because
+otherwise the system will not unpack them while Emacs is being
+installed. This means, instead of specifying @code{ctags} or
+@code{emacsclient} in a subprocess, Lisp code must specify
+@code{libctags.so} or @code{libemacsclient.so} on the command line
+instead when starting either of those programs in a subprocess.
+
+ The @file{/assets} directory containing Emacs start-up files is
+supposed to be inaccessible to processes not directly created by
+@code{zygote}, the system service responsible for starting
+applications. Since required Lisp is found in the @file{/assets}
+directory, it would thus follow that it is not possible for Emacs to
+start itself as a subprocess. A special binary named
+@command{libandroid-emacs.so} is provided with Emacs, and does its
+best to start Emacs, for the purpose of running Lisp in batch mode.
+However, the approach it takes was devised by reading Android source
+code, and is not sanctioned by the Android compatibility definition
+documents, so your mileage may vary.
+
+@cindex call-process, Android
+@vindex android-use-exec-loader
+ Android 10 and later also prohibit Emacs itself from running
+executables inside the app data directory, obstensibly for security
+readers. On these systems, Emacs normally applies a workaround;
+however, this workaround requires running all sub-processes through
+another subprocess which implements an executable loader and applies
+process tracing to all its children, which may prove to be problematic
+for various different reasons. In that case, the workaround can be
+disabled by changing the variable @code{android-use-exec-loader} to
+@code{nil}.
+
+ When this workaround is in effect, process IDs retrieved through the
+@code{process-id} function will be that of the executable loader
+process; its child will belong to the same process group as the
+loader. As a result, @code{interrupt-process}, and other related
+functions will work correctly, but using the process ID returned by
+@code{process-id} for other purposes will not.
+
+ One side effect of the mechanism by which process tracing is carried
+out is that job control facilities will not be able to stop
+subprocesses, and the @code{SIGSTOP} signal will appear to have no
+effect.
+
+ In addition, Android 12 also terminates subprocesses which are
+consuming CPU while Emacs itself is in the background. The system
+determines which processes are consuming too much CPU in intervals of
+five minutes, and terminates the process that has consumed the most
+CPU time.
+
+ Android 12.1 and Android 13 provide an option to disable this
+behavior; to use it, enable ``USB debugging'' (@pxref{Android
+Startup}) connect the Android system to another computer, and run:
+
+@example
+$ adb shell "settings put global settings_enable_monitor_phantom_procs false"
+@end example
+
+@section Running Emacs in the background
+@cindex emacs killed, android
+@cindex emacs in the background, android
+
+ Application processes are treated as disposable entities by the
+system. When all Emacs frames move to the background, Emacs is liable
+to be killed by the system at any time, for the purpose of saving
+system resources.
+
+ On Android 7.1 and earlier, Emacs tells the system to treat it as a
+``background service''. The system will try to avoid killing Emacs
+unless the device is under memory stress.
+
+ Android 8.0 removed the ability for background services to receive
+such special treatment. However, Emacs applies a workaround: the
+system considers applications that create a permanent notification to
+be performing active work, and will avoid killing such applications.
+Thus, on those systems, Emacs displays a permanent notification for as
+long as it is running. Once the notification is displayed, it can be
+safely hidden through the system settings without resulting in Emacs
+being killed.
+
+ However, it is not guaranteed that the system will not kill Emacs,
+even if the notification is being displayed. While the Open Handset
+Alliance's sample implementation of Android behaves correctly, many
+manufacturers place additional restrictions on program execution in
+the background in their proprietary versions of Android. There is a
+list of such troublesome manufacturers and sometimes workarounds, at
+@url{https://dontkillmyapp.com/}.
+
+@section Android permissions
+@cindex external storage, android
+
+ Android also defines a permissions system that determines what
+system services Emacs is allowed to access. Programs must specify
+what permissions they want; what then happens depends on the version
+of Android being used:
+
+@itemize @bullet
+@item
+On Android 5.1 and earlier, Emacs automatically receives the following
+permissions it has requested upon being installed:
+
+@itemize @minus
+@item
+@code{android.permission.READ_CONTACTS}
+@item
+@code{android.permission.WRITE_CONTACTS}
+@item
+@code{android.permission.VIBRATE}
+@item
+@code{android.permission.ACCESS_COARSE_LOCATION}
+@item
+@code{android.permission.ACCESS_NETWORK_STATE}
+@item
+@code{android.permission.INTERNET}
+@item
+@code{android.permission.SET_WALLPAPER}
+@item
+@code{android.permission.WRITE_EXTERNAL_STORAGE}
+@item
+@code{android.permission.SEND_SMS}
+@item
+@code{android.permission.RECEIVE_SMS}
+@item
+@code{android.permission.RECEIVE_MMS}
+@item
+@code{android.permission.WRITE_SMS}
+@item
+@code{android.permission.READ_SMS}
+@item
+@code{android.permission.NFC}
+@item
+@code{android.permission.TRANSMIT_IR}
+@item
+@code{android.permission.READ_PHONE_STATE}
+@item
+@code{android.permission.WAKE_LOCK}
+@item
+@code{android.permission.FOREGROUND_SEVICE}
+@item
+@code{android.permission.REQUEST_INSTALL_PACKAGES}
+@item
+@code{android.permission.REQUEST_DELETE_PACKAGES}
+@item
+@code{android.permission.SYSTEM_ALERT_WINDOW}
+@item
+@code{android.permission.RECORD_AUDIO}
+@item
+@code{android.permission.CAMERA}
+@end itemize
+
+While most of these permissions are left unused by Emacs itself, they
+are declared by Emacs as they could be useful for other programs; for
+example, the permission to access contacts may be useful for EUDC.
+
+@item
+On Android 6.0 and later, Emacs only receives the following
+permissions upon installation:
+
+@itemize @minus
+@item
+@code{android.permission.VIBRATE}
+@item
+@code{android.permission.ACCESS_NETWORK_STATE}
+@item
+@code{android.permission.INTERNET}
+@item
+@code{android.permission.SET_WALLPAPER}
+@item
+@code{android.permission.NFC}
+@item
+@code{android.permission.TRANSMIT_IR}
+@item
+@code{android.permission.WAKE_LOCK}
+@item
+@code{android.permission.POST_NOTIFICATIONS}
+@end itemize
+
+Other permissions must be granted by the user through the system
+settings application. Consult the manufacturer of your device for
+more details, as how to do this varies by device.
+@end itemize
+
+@node Android Windowing
+@section The Android window system
+
+ Android has an unusual window system; there, all windows are
+maximized or full-screen, and only one window can be displayed at a
+time. On larger devices, the system allows up to four windows to be
+tiled on the screen at any time.
+
+ Windows on Android do not continue to exist indefinitely after they
+are created. Instead, the system may choose to terminate windows that
+are not on screen in order to save memory, with the assumption that
+the program will save its contents to disk and restore them later,
+when the user asks for it to be opened again. As this is obviously
+not possible with Emacs, Emacs separates the resources associated with
+a frame from its system window.
+
+ Each system window created (including the initial window created
+during Emacs startup) is appended to a list of windows that do not
+have associated frames. When a frame is created, Emacs looks up any
+window within that list, and displays the contents of the frame
+within; if there is no window at all, then one is created. Likewise,
+when a new window is created by the system, Emacs places the contents
+of any frame that is not already displayed within a window inside.
+When a frame is closed, the corresponding system window is also
+closed. Upon startup, the system creates a window itself (within
+which Emacs displays the first window system frame shortly
+thereafter.) Emacs differentiates between that window and windows
+created on behalf of other frames to determine what to do when the
+system window associated with a frame is closed:
+
+@itemize @bullet
+@item
+When the system closes the window created during application startup
+in order to save memory, Emacs retains the frame for when that window
+is created later.
+
+@item
+When the user closes the window created during application startup,
+and the window was not previously closed by the system in order to
+save resources, Emacs deletes any frame displayed within that window.
+
+@item
+When the user or the system closes any window created by Emacs on
+behalf of a specific frame, Emacs deletes the frame displayed within
+that window.
+@end itemize
+
+@cindex windowing limitations, android
+@cindex frame parameters, android
+Emacs only supports a limited subset of GUI features on Android; the
+limitations are as follows:
+
+@itemize @bullet
+@item
+Scroll bars are not supported, as they are close to useless on Android
+devices.
+
+@item
+The @code{alpha}, @code{alpha-background}, @code{z-group},
+@code{override-redirect}, @code{mouse-color}, @code{title},
+@code{wait-for-wm}, @code{sticky}, @code{undecorated} and
+@code{tool-bar-position} frame parameters (@pxref{Frame Parameters,,,
+elisp, the Emacs Lisp Reference Manual}) are unsupported.
+
+@item
+On Android 4.0 and earlier, the @code{fullscreen} frame parameter is
+always @code{maximized} for top-level frames; on later versions of
+Android, it can also be @code{fullscreen}.
+@end itemize
+
+@cindex selections, android
+@cindex android clipboard
+ Emacs does not implement all selection related features supported
+under the X Window System on Android. For example, only the
+@code{CLIPBOARD} and @code{PRIMARY} selections (@pxref{Cut and Paste})
+are supported, and Emacs is only able to set selections to plain text.
+
+ In addition, the Android system itself places certain restrictions
+on what selection data Emacs can access:
+
+@itemize @bullet
+@item
+On Android 2.3 and earlier, the function @code{gui-selection-owner-p}
+always returns @code{nil} for the clipboard selection.
+
+@item
+Between Android 3.0 and Android 9.0, Emacs is able to access the
+clipboard whenever it wants, and @code{gui-selection-owner-p} always
+returns accurate results.
+
+@item
+Under Android 10.0 and later, Emacs can only access clipboard data
+when one of its frames has the input focus, and
+@code{gui-selection-owner-p} always returns @code{nil} for the
+clipboard selection.
+@end itemize
+
+ Since the Android system itself has no concept of a primary
+selection, Emacs provides an emulation instead. This means there is
+no way to transfer the contents of the primary selection to another
+application via cut-and-paste.
+
+@vindex android-pass-multimedia-buttons-to-system
+@cindex volume/multimedia buttons, Android
+ The volume keys are normally reserved by Emacs and used to provide
+the ability to quit Emacs without a physical keyboard
+(@pxref{On-Screen Keyboards}.) However, if you want them to adjust
+the volume instead, you can set the variable
+@code{android-pass-multimedia-buttons-to-system} to a non-@code{nil}
+value; note that you will no longer be able to quit Emacs using the
+volume buttons in that case.
+
+@cindex dialog boxes, android
+ Emacs is unable to display dialog boxes (@pxref{Dialog Boxes}) while
+it does not have the input focus on Android 6.0 or later. If this is
+important to you, this ability can be restored by granting Emacs
+permission to display over other programs. Normally, this can be done
+from the:
+
+@example
+System -> Apps -> Emacs -> More -> Display over other apps
+@end example
+
+menu in the system settings, but this procedure may vary by device.
+
+@node Android Fonts
+@section Font backends and selection under Android
+@cindex fonts, android
+
+ Emacs supports two font backends under Android: they are respectively
+named @code{sfnt-android} and @code{android}.
+
+ Upon startup, Emacs enumerates all the TrueType format fonts in the
+directories @file{/system/fonts} and @file{/product/fonts}, and the
+@file{fonts} directory (@dfn{user fonts directory}) inside the Emacs
+home directory. Emacs assumes there will always be a font named
+``Droid Sans Mono'', and then defaults to using this font. These
+fonts are then displayed by the @code{sfnt-android} font driver.
+
+ When running on Android, Emacs currently lacks support for OpenType
+fonts. This means that only a subset of the fonts installed on the
+system are currently available to Emacs. If you are interested in
+lifting this limitation, please contact @email{emacs-devel@@gnu.org}.
+
+ If the @code{sfnt-android} font driver fails to find any fonts at
+all, Emacs falls back to the @code{android} font driver. This is a
+very lousy font driver, because of limitations and inaccuracies in the
+font metrics provided by the Android platform. In that case, Emacs
+uses the ``Monospace'' typeface configured on your system; this should
+always be Droid Sans Mono.
+
+@cindex TrueType GX fonts, android
+@cindex distortable fonts, android
+
+ Like on X systems, Emacs supports distortable fonts under Android.
+These fonts (also termed ``TrueType GX fonts'', ``variable fonts'',
+and ``multiple master fonts'') provide multiple different styles
+(``Bold'', ``Italic'', etc) using a single font file.
+
+ When a user-installed distortable font is found, each font that a
+previously discovered font provided will no longer be used. In
+addition, any previously specified distortable fonts with the same
+family name are also removed. When a conventional font is found, any
+previous conventional font with the same style and family will be
+removed; distortable fonts with the same family will no longer be used
+to provide that style.
+
+@node Android Troubleshooting
+@section What to do when something goes wrong on Android
+@cindex troubleshooting, android
+
+@cindex emacs -Q, android
+ Since Android has no command line, there is normally no way to
+specify command-line arguments when starting Emacs. This is very
+nasty when you make a mistake in your Emacs initialization files that
+prevents Emacs from starting up at all, as the system normally
+prevents other programs from accessing Emacs's home directory.
+
+ However, Emacs can be started with the equivalent of the
+@code{--quick} option (@pxref{Initial Options}) through a special
+preferences screen, which can be accessed through the Emacs ``app
+info'' page in the system settings application.
+
+ Consult the manufacturer of your device for more details, as how to
+do this varies by device.
+
+@cindex dumping, android
+ The first time any given copy of Emacs starts on a device, it spends
+a while loading the preloaded Lisp files which normally come with
+Emacs. This produces a ``dump file'' (@pxref{Initial Options}) in the
+files directory, containing an identifier unique to this copy of
+Emacs.
+
+ The next time that same copy of Emacs starts up, it simply loads the
+data contained in that dump file, greatly improving start up time.
+
+ If by some unforeseen circumstance the dump file is corrupted, Emacs
+can crash. If that happens, the dump file stored in the Emacs files
+directory can be erased through the same preferences screen.
+
+@cindex accessing Emacs directories, Android
+ Emacs supports an alternative method of rescuing broken Emacs
+installations on Android 4.4 and later: Emacs exports a ``documents
+provider'' which accesses the contents of Emacs's home directory, that
+can then be accessed by any file manager program.
+
+ If you can find out how to open that documents provider in the file
+manager that comes with your device, you can rename, delete, or edit
+your initialization or dump files from there instead.
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 7071ea44edd..766180c2e17 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -223,7 +223,9 @@ Appendices
* Antinews:: Information about Emacs version 28.
* Mac OS / GNUstep:: Using Emacs under macOS and GNUstep.
* 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.
@@ -1260,6 +1262,21 @@ Emacs and Haiku
* Haiku Basics:: Basic Emacs usage and installation under Haiku.
* Haiku Fonts:: The various options for displaying fonts on Haiku.
+Emacs and Android
+
+* What is Android?:: Preamble.
+* Android Startup:: Starting up Emacs on Android.
+* Android Environment:: Running Emacs under Android.
+* Android File System:: The Android file system.
+* Android Windowing:: The Android window system.
+* Android Fonts:: Font selection under Android.
+* Android Troubleshooting:: Dealing with problems.
+
+Emacs and unconventional input devices
+
+* Touchscreens:: Using Emacs on touchscreens.
+* On-Screen Keyboards:: Using Emacs with virtual keyboards.
+
Emacs and Microsoft Windows/MS-DOS
* Windows Startup:: How to start Emacs on Windows.
@@ -1630,8 +1647,10 @@ Lisp programming.
@include anti.texi
@include macos.texi
@include haiku.texi
+@include android.texi
@c Includes msdos-xtra.
@include msdos.texi
+@include input.texi
@include gnu.texi
@include glossary.texi
@ifnottex
diff --git a/doc/emacs/input.texi b/doc/emacs/input.texi
new file mode 100644
index 00000000000..b4b37501a14
--- /dev/null
+++ b/doc/emacs/input.texi
@@ -0,0 +1,146 @@
+@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.
+* On-Screen Keyboards:: Using Emacs with virtual keyboards.
+@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.
+
+ If the tool is moved left or right, Emacs additionally scrolls the
+window horizontally to follow (@pxref{Horizontal Scrolling}.)
+
+@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}.
+
+@node On-Screen Keyboards
+@section Using Emacs with virtual keyboards
+@cindex virtual keyboards
+@cindex on-screen keyboards
+
+ When there is no physical keyboard attached to a system, the
+windowing system typically provides an on-screen keyboard, more often
+known as a ``virtual keyboard'', containing rows of clickable buttons
+that send keyboard input to the application, much like a real keyboard
+would. This virtual keyboard is hidden by default, as it uses up
+valuable on-screen real estate, and must be opened once the program
+being used is ready to accept keyboard input.
+
+ Under the X Window System, the client that provides the on-screen
+keyboard typically detects when the application is ready to accept
+keyboard input through a set of complex heuristics, and automatically
+displays the keyboard when necessary.
+
+ On other systems such as Android, Emacs must tell the system when it
+is ready to accept keyboard input. Typically, this is done in
+response to a touchscreen ``tap'' gesture (@pxref{Touchscreens}), or
+once to the minibuffer becomes in use (@pxref{Minibuffer}.)
+
+@vindex touch-screen-set-point-commands
+ When a ``tap'' gesture results in a command being executed, Emacs
+checks to see whether or not the command is supposed to set the point
+by looking for it in the list @code{touch-screen-set-point-commands}.
+If it is, then Emacs looks up whether or not the text under the point
+is read-only; if not, it activates the on-screen keyboard, assuming
+that the user is about to enter text in to the current buffer.
+
+@vindex touch-screen-display-keyboard
+ The user option @code{touch-screen-display-keyboard} forces Emacs to
+always display the on screen keyboard; it may also be bound buffer
+locally, meaning to always display the keyboard when the buffer is
+selected.
+
+ Emacs also provides a set of functions to show or hide the on-screen
+keyboard. For more details, @pxref{On-Screen Keyboards,,, elisp, The
+Emacs Lisp Reference Manual}.
+
+@cindex quitting, without a keyboard
+ Since it may not be possible for Emacs to display the on screen
+keyboard when it is executing a command, Emacs implements a feature on
+devices with only an on-screen keyboard, by which two rapid clicks of
+a hardware button that is always present on the device results in
+Emacs quitting. @xref{Quitting}.
+
+@vindex x-quit-keysym
+ The exact button is used to do this varies by system: on X, it is
+defined in the variable @code{x-quit-keysym}, and on Android, it is
+always the volume down button.
+
+@cindex text conversion, keyboards
+ Most input methods designed to work with on-screen keyboards perform
+buffer edits differently from desktop input methods.
+
+ On a conventional desktop windowing system, an input method will
+simply display the contents of any on going character compositions on
+screen, and send the appropriate key events to Emacs after completion.
+
+ However, on screen keyboard input methods directly perform edits to
+the selected window of each frame; this is known as ``text
+conversion'', or ``string conversion'' under the X Window System.
+Emacs enables these input methods whenever the buffer local value of
+@code{text-conversion-style} is non-@code{nil}, normally inside
+derivatives of @code{text-mode} and @code{prog-mode}.
+
+ Text conversion is performed asynchronously whenever Emacs receives
+a request to perform the conversion from the input method, and Emacs
+is not currently reading a key sequence for which one prefix key has
+already been read (@pxref{Keys}.) After the conversion completes, a
+@code{text-conversion} event is sent. @xref{Misc Events,,, elisp, the
+Emacs Reference Manual}.
+
+@vindex text-conversion-face
+ If the input method needs to work on a region of the buffer, then
+the region becomes known as the ``composing region'' (or
+``preconversion region''.) The variable @code{text-conversion-face}
+describes whether or not to display the composing region in a specific
+face.
diff --git a/doc/lispref/commands.texi b/doc/lispref/commands.texi
index cd1745614eb..783ab583ec4 100644
--- a/doc/lispref/commands.texi
+++ b/doc/lispref/commands.texi
@@ -2011,11 +2011,18 @@ the position of the finger when the event occurred.
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})
@@ -2024,6 +2031,45 @@ display, because another program took the grab, or because the user
raised the finger from the touchscreen.
@end table
+If a touchpoint is pressed against the menu bar, then Emacs will not
+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}.
+
+It behaves like @code{touch-screen-track-tap}, except that it returns
+@code{no-drag} and refrains from calling @var{update} if the
+touchpoint in @code{event} did not move far enough (by default, 5
+pixels from its position in @code{event}) to qualify as an actual
+drag.
+@end defun
+
@node Focus Events
@subsection Focus Events
@cindex focus event
@@ -2160,6 +2206,69 @@ the buffer in which the xwidget will be displayed, using
A few other event types represent occurrences within the system.
@table @code
+@cindex @code{text-conversion} event
+@item text-conversion
+This kind of event is sent @strong{after} a system-wide input method
+performs an edit to one or more buffers.
+
+@vindex text-conversion-edits
+Once the event is sent, the input method may already have made
+changes to multiple buffers inside many different frames. To
+determine which buffers have been changed, and what edits have
+been made to them, use the variable
+@code{text-conversion-edits}, which is set prior to each
+@code{text-conversion} event being sent; it is a list of the
+form:
+
+@example
+@w{@code{((@var{buffer} @var{beg} @var{end} @var{ephemeral}) ...)}}
+@end example
+
+Where @var{ephemeral} is the buffer which was modified, @var{beg} and
+@var{end} are markers set to the positions of the edit at the time it
+was completed, and @var{ephemeral} is either a string, containing any
+text which was inserted, or any text before point which was deleted,
+@code{t}, meaning that the edit is a temporary edit made by the input
+method, and @code{nil}, meaning that some text was deleted after
+point.
+
+@vindex text-conversion-style
+Whether or not this event is sent depends on the value of the
+buffer-local variable @code{text-conversion-style}, which determines
+how an input method that wishes to make edits to buffer contents will
+behave.
+
+This variable can have one of three values:
+
+@table @code
+@item nil
+This means that the input method will be disabled entirely, and key
+events will be sent instead of text conversion events.
+
+@item action
+This means that the input method will be enabled, but @key{RET} will
+be sent wherever the input method wanted to insert a new line.
+
+@item t
+This, or any other value, means that the input method will be enabled
+and make edits terminated by @code{text-conversion} events.
+@end table
+
+@findex disable-text-conversion
+Changes to the value of this variable will only take effect upon
+the next redisplay after the buffer becomes the selected buffer
+of a frame. If you need to disable text conversion in a way
+that takes immediate effect, call the function
+@code{set-text-conversion-style} instead. This can potentially
+lock up the input method for a significant amount of time, so do
+not do this lightly!
+
+@vindex disable-inhibit-text-conversion
+In addition, text conversion is automatically disabled after a prefix
+key is read by the command loop, or through @code{read-key-sequence}.
+This can be disabled by setting or binding the variable
+@code{disable-inhibit-text-conversion} to a non-@code{nil} value.
+
@cindex @code{delete-frame} event
@item (delete-frame (@var{frame}))
This kind of event indicates that the user gave the window manager
@@ -3036,19 +3145,21 @@ with any other events.
@cindex @code{right-divider}, prefix key
@cindex @code{bottom-divider}, prefix key
@cindex mouse events, in special parts of window or frame
-When mouse events occur in special parts of a window or frame, such as a mode
-line or a scroll bar, the event type shows nothing special---it is the
-same symbol that would normally represent that combination of mouse
-button and modifier keys. The information about the window part is kept
-elsewhere in the event---in the coordinates. But
-@code{read-key-sequence} translates this information into imaginary
-prefix keys, all of which are symbols: @code{tab-line}, @code{header-line},
-@code{horizontal-scroll-bar}, @code{menu-bar}, @code{tab-bar}, @code{mode-line},
+@cindex touch screen events, in special parts of window or frame
+When mouse or @code{touch-screen-begin} events occur in special parts
+of a window or frame, such as a mode line or a scroll bar, the event
+type shows nothing special---it is the same symbol that would normally
+represent that combination of mouse button and modifier keys. The
+information about the window part is kept elsewhere in the event---in
+the coordinates. But @code{read-key-sequence} translates this
+information into imaginary prefix keys, all of which are symbols:
+@code{tab-line}, @code{header-line}, @code{horizontal-scroll-bar},
+@code{menu-bar}, @code{tab-bar}, @code{mode-line},
@code{vertical-line}, @code{vertical-scroll-bar}, @code{left-margin},
@code{right-margin}, @code{left-fringe}, @code{right-fringe},
-@code{right-divider}, and @code{bottom-divider}. You can define meanings for
-mouse clicks in special window parts by defining key sequences using these
-imaginary prefix keys.
+@code{right-divider}, and @code{bottom-divider}. You can define
+meanings for mouse clicks in special window parts by defining key
+sequences using these imaginary prefix keys.
For example, if you call @code{read-key-sequence} and then click the
mouse on the window's mode line, you get two events, like this:
diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index f1b4b001889..f91a2715bca 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -2933,8 +2933,9 @@ apply to. Here are the possible values of @var{characteristic}:
The kind of window system the terminal uses---either @code{graphic}
(any graphics-capable display), @code{x}, @code{pc} (for the MS-DOS
console), @code{w32} (for MS Windows 9X/NT/2K/XP), @code{haiku} (for
-Haiku), @code{pgtk} (for pure GTK), or @code{tty} (a non-graphics-capable
-display). @xref{Window Systems, window-system}.
+Haiku), @code{pgtk} (for pure GTK), @code{android} (for Android), or
+@code{tty} (a non-graphics-capable display). @xref{Window Systems,
+window-system}.
@item class
What kinds of colors the terminal supports---either @code{color},
@@ -8857,6 +8858,8 @@ Emacs is displaying the frame using MS-DOS direct screen writes.
Emacs is displaying the frame using the Application Kit on Haiku.
@item pgtk
Emacs is displaying the frame using pure GTK facilities.
+@item android
+Emacs is displaying the frame on Android.
@item nil
Emacs is displaying the frame on a character-based terminal.
@end table
diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi
index a1d7b51b609..72441c8d442 100644
--- a/doc/lispref/elisp.texi
+++ b/doc/lispref/elisp.texi
@@ -1139,6 +1139,7 @@ Frames
* Dialog Boxes:: Displaying a box to ask yes or no.
* Pointer Shape:: Specifying the shape of the mouse pointer.
* Window System Selections::Transferring text to and from other X clients.
+* Accessing Selections:: The multiple different kinds of selections.
* Yanking Media:: Yanking things that aren't plain text.
* Drag and Drop:: Internals of Drag-and-Drop implementation.
* Color Names:: Getting the definitions of color names.
diff --git a/doc/lispref/frames.texi b/doc/lispref/frames.texi
index c78ab1c34ba..77b2dd9e7cd 100644
--- a/doc/lispref/frames.texi
+++ b/doc/lispref/frames.texi
@@ -104,9 +104,11 @@ window of another Emacs frame. @xref{Child Frames}.
* Mouse Tracking:: Getting events that say when the mouse moves.
* Mouse Position:: Asking where the mouse is, or moving it.
* Pop-Up Menus:: Displaying a menu for the user to select from.
+* On-Screen Keyboards:: Displaying the virtual keyboard.
* Dialog Boxes:: Displaying a box to ask yes or no.
* Pointer Shape:: Specifying the shape of the mouse pointer.
* Window System Selections:: Transferring text to and from other X clients.
+* Accessing Selections:: The multiple different kinds of selections.
* Yanking Media:: Yanking things that aren't plain text.
* Drag and Drop:: Internals of Drag-and-Drop implementation.
* Color Names:: Getting the definitions of color names.
@@ -695,7 +697,7 @@ The position of the top left corner of the native frame specifies the
indicate that position for the various builds:
@itemize @w{}
-@item (1) non-toolkit, Haiku, and terminal frames
+@item (1) non-toolkit, Android, Haiku, and terminal frames
@item (2) Lucid, Motif, and MS-Windows frames
@@ -2394,6 +2396,7 @@ engine), and @code{harfbuzz} (font driver for OTF and TTF fonts with
HarfBuzz text shaping) (@pxref{Windows Fonts,,, emacs, The GNU Emacs
Manual}). The @code{harfbuzz} driver is similarly recommended. On
Haiku, there can be several font drivers (@pxref{Haiku Fonts,,, emacs,
+The GNU Emacs Manual}), as on Android (@pxref{Android Fonts,,, emacs,
The GNU Emacs Manual}).
On other systems, there is only one available font backend, so it does
@@ -3739,9 +3742,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})
@@ -3826,6 +3829,30 @@ keymap. It won't be called if @code{x-popup-menu} returns for some
other reason without displaying a pop-up menu.
@end defvar
+@node On-Screen Keyboards
+@section On-Screen Keyboards
+
+ An on-screen keyboard is a special kind of pop up provided by the
+system, with rows of clickable buttons that act as a real keyboard.
+
+ On certain systems (@pxref{On-Screen Keyboards,,,emacs, The Emacs
+Manual}), Emacs is supposed to display and hide the on screen keyboard
+depending on whether or not the user is about to type something.
+
+@defun frame-toggle-on-screen-keyboard frame hide
+This function displays or hides the on-screen keyboard on behalf of
+the frame @var{frame}. If @var{hide} is non-@code{nil}, then the
+on-screen keyboard is hidden; otherwise, it is displayed.
+
+It returns whether or not the on screen keyboard @strong{may} have
+been displayed, which should be used to determine whether or not to
+hide the on-screen keyboard later.
+
+This has no effect if the system automatically detects when to display
+the on-screen keyboard, or when it does not provide any on-screen
+keyboard.
+@end defun
+
@node Dialog Boxes
@section Dialog Boxes
@cindex dialog boxes
@@ -4033,6 +4060,542 @@ For backward compatibility, there are obsolete aliases
names of @code{gui-get-selection} and @code{gui-set-selection} before
Emacs 25.1.
+@node Accessing Selections
+@section Accessing Selections
+
+ @code{gui-get-selection} is able to retrieve multiple different
+kinds of selection data from any number of selections. However, the
+data types and selections that Emacs understands is not precisely
+specified and differs depending on the window system on which Emacs is
+running.
+
+ At the same time, @code{gui-set-selection} hides a great deal of
+complexity behind its back, at least on some systems: its @var{data}
+argument need not be a string, but is actually given verbatim to
+system specific code.
+
+ Emacs's implementation of selections is most complete on the X
+Window System. This is both an artifact of history (X was the first
+window system supported by Emacs) and one of technical reasons:
+instead of using selections only to transfer text and multimedia
+content between clients, X uses selections as a general inter-client
+communication system, leading to a great proliferation of selection
+data types.
+
+ Even more confusingly, X also supports another inter-client
+communication mechanism: the Inter-Client Exchange. However, ICE is
+only used by Emacs to communicate with session managers, and is a
+separate topic.
+
+@menu
+* X Selections:: Selection data types (and more) on X.
+* Other Selections:: How they work on other window systems.
+@end menu
+
+@node X Selections
+@subsection X Selections
+
+ X refrains from defining fixed data types for selection data, or a
+fixed number of selections. Selections are instead identified by X
+``atoms'', which are unique 29-bit identifiers issued by the X server
+for a corresponding name. In Emacs, you can simply write a symbol
+with the name of the atom, and Emacs will transparently request these
+identifiers where necessary.
+
+ When a program ``sets'' a selection under X, it actually makes
+itself the ``owner'' of the selection---the X server will then deliver
+selection requests to the program, which is obliged to respond to the
+requesting client with the selection data.
+
+ Similarly, a program does not ``get'' selection data from the X
+server. Instead, its selection requests are sent to the client with
+the window which last took ownership over the selection, which then
+replies with the requested data.
+
+ Each selection request contains three parameters:
+
+@itemize @bullet
+@item
+The window which requested the selection; this is used to identify the
+@c Not a typo: X spells ``requestor'' with an o.
+requesting program, otherwise known as the @dfn{requestor}.
+
+@item
+An atom identifying the ``target'' to which the owner should convert
+the selection. It is easiest to think of the conversion target as the
+kind of data that the requestor wants: in selection requests made by
+Emacs, the target is determined by the @dfn{type} argument to
+@code{gui-get-selection}.
+
+@item
+A 32-bit timestamp containing the X server time at which the requestor
+last obtained input.
+@end itemize
+
+ The selection owner responds by tranferring to the requestor a
+series of bytes, 16 bit words, or 32 bit words, along with another
+atom identifying the type of those words. After requesting a
+selection, Emacs then applies its own interpretation of the data
+format and data type to convert the data transferred by the selection
+owner to a Lisp representation, which @code{gui-get-selection}
+returns.
+
+ By default, Emacs converts selection data consisting of any series
+of bytes to a unibyte string containing those bytes, selection data
+consisting of a single 16-bit or 32-bit word as an unsigned number,
+and selection data consisting of multiple such words as a vector of
+unsigned numbers. However, Emacs applies special treatment for
+several selection data types:
+
+@table @code
+@item INTEGER
+16-bit or 32-bit words of this type are treated as signed integers,
+instead of unsigned ones. If there are multiple words in the
+selection data, a vector is returned; otherwise, the integer is
+returned by itself.
+
+@item ATOM
+32-bit words of this type are treated as X atoms, and returned (either
+alone or as vectors) as Lisp symbols containing the names they
+identify. Invalid atoms are returned as @code{nil}.
+
+@item COMPOUND_TEXT
+@item UTF8_STRING
+@item STRING
+Unibyte strings returned for these data types will have a single
+@code{foreign-selection} text property set to a symbol with the type
+of the selection data.
+@end table
+
+ Each selection owner must return at least two selection targets:
+@code{TARGETS}, which returns a number of atoms describing the
+selection targets that the owner supports, and @code{MULTIPLE}, used
+for internal purposes by X clients. A selection owner may support any
+number of other targets, some of which may be standardized by the X
+Consortium's
+@url{http://x.org/releases/X11R7.6/doc/xorg-docs/specs/ICCCM/icccm.html,
+Inter-Client Communication Conventions Manual}, while others, such as
+@code{UTF8_STRING}, were supposed to be standardized by the XFree86
+Project, which unfortunately did not happen.
+
+ Requests for a given selection target may, by convention, return
+data in a specific type, or it may return data in one of several
+types, whichever is most convenient for the selection owner; the
+latter type of selection target is dubbed a @dfn{polymorphic target}.
+A selection target may also return no data at all: by convention, the
+selection owner performs some action a side effect upon responding to
+a selection request with that target, and as such these targets are
+referred to as @dfn{side-effect targets}.
+
+ Here are some selection targets which behave in a reasonably
+standard manner when used with the @code{CLIPBOARD}, @code{PRIMARY},
+or @code{SECONDARY} selections.
+
+@table @code
+@item ADOBE_PORTABLE_DOCUMENT_FORMAT
+This target returns data in Adobe System's ``Portable Document
+Format'' format, as a string.
+
+@item APPLE_PICT
+This target returns data in the ``PICT'' image format used on
+Macintosh computers, as a string.
+
+@item BACKGROUND
+@item BITMAP
+@item COLORMAP
+@item FOREGROUND
+Together, these four targets return integer data necessary to make use
+of a bitmap image stored on the X server: the pixel value of the
+bitmap's background color, the X identifier of the bitmap, the
+colormap inside which the background and foreground are allocated, and
+the pixel value of the bitmap's foreground color.
+
+@item CHARACTER_POSITION
+This target returns two unsigned 32-bit integers of type @code{SPAN}
+describing the start and end positions of the selection data in the
+text field containing it, in bytes.
+
+@item COMPOUND_TEXT
+This target returns a string of type @code{COMPOUND_TEXT} in the X
+Consortium's multi-byte text encoding system.
+
+@item DELETE
+This target returns nothing, but as a side-effect deletes the
+selection contents from any text field containing them.
+
+@item DRAWABLE
+@item PIXMAP
+This target returns a list of unsigned 32-bit integers, each of which
+corresponds to an X server drawable or pixmap.
+
+@item ENCAPSULATED_POSTSCRIPT
+@item _ADOBE_EPS
+This target returns a string containing encapsulated Postscript code.
+
+@item FILE_NAME
+This target returns a string containing one or more file names,
+separated by NULL characters.
+
+@item HOST_NAME
+This target returns a string containing the fully-qualified domain
+name of the machine on which the selection owner is running.
+
+@item USER
+This target returns a string containing the user name of the machine
+on which the selection owner is running.
+
+@item LENGTH
+This target returns an unsigned 32-bit or 16-bit integer containing
+the length of the selection data.
+
+@item LINE_NUMBER
+This target returns two unsigned 32-bit integers of type @code{SPAN}
+describing the line numbers corresponding to the start and end
+positions of the selection data in the text field containing it.
+
+@item MODULE
+This target returns the name of any function containing the selection
+data. It is mainly used by text editors.
+
+@item STRING
+This target returns the selection data as a string of type
+@code{STRING}, encoded in ISO Latin-1 format, with Unix newline
+characters.
+
+@item C_STRING
+This target returns the selection data as a ``C string''. This has
+been interpreted as meaning the raw selection data in whatever
+encoding used by the owner, either terminated with a NULL byte or not
+at all, or an ASCII string which may or may not be terminated.
+
+@item UTF8_STRING
+This returns the selection data as a string of type
+@code{UTF8_STRING}, encoded in UTF-8, with unspecified EOL format.
+
+@item TIMESTAMP
+This target returns the X server time at which the selection owner
+took ownership over the selection as a 16-bit or 32-bit word of type
+@code{CARDINAL}.
+
+@item TEXT
+This polymorphic target returns selection data as a string, either
+@code{COMPOUND_TEXT}, @code{STRING}, @code{C_STRING}, or
+@code{UTF8_STRING}, whichever data type is convenient for the
+selection owner.
+@end table
+
+ When a request for the targets @code{STRING}, @code{COMPOUND_TEXT},
+or @code{UTF8_STRING} is made using the function
+@code{gui-get-selection}, and neither @code{selection-coding-system}
+nor @code{next-selection-coding-system} are set, the returned strings
+are additionally decoded using the appropriate coding system for those
+data types: @code{iso-8859-1}, @code{compound-text-with-extensions}
+and @code{utf-8} respectively.
+
+ In addition to the targets specified above (and the many targets
+used by various programs for their own purposes), several popular
+programs and toolkits have decided to define selection data types of
+their own, without consulting the appropriate X standards bodies.
+These targets are usually named after MIME types, such as
+@code{text/html} or @code{image/jpeg}, and have been known to contain:
+
+@itemize @bullet
+@item
+Unterminated, newline terminated, or NULL character terminated file
+names of an image or text file.
+
+@item
+Image or text data in the appropriate format.
+
+@item
+@code{file://} URIs (or possibly newline or NUL terminated lists of
+URIs) leading to files in the appropriate format.
+@end itemize
+
+ These selection targets were first used by Netscape, but are now
+found in all kinds of programs, especially those based on recent
+versions of the GTK+ or Qt toolkits.
+
+ Emacs is also capable of acting as a selection owner. When
+@code{gui-set-selection} is called, the selection data specified is
+not transferred to the X server; instead, Emacs records it internally
+and obtains ownership of the selection.
+
+@defvar selection-converter-alist
+ Alist of selection targets to ``selection converter'' functions.
+When a selection request is received, Emacs looks up the selection
+converter associated with the requested selection target.
+
+ The selection converter is called with three arguments: the symbol
+corresponding to the atom identifying the selection being requested,
+the selection target that is being requested, and the value set with
+@code{gui-set-selection}. The value which it returns is either a cons
+of a symbol specifying the data type and a number, symbol, or a vector
+of numbers or symbols, or its cdr by itself.
+
+ If the value is the special symbol @code{NULL}, the data type is set
+to @code{NULL}, and no data is returned to the requestor.
+
+ If the value is a string, it must be a unibyte string; should no
+data type be explicitly specified, the data is transferred to the
+requestor with the type @code{STRING}.
+
+ If the value is a symbol, its ``atom'' is retrieved, and it is
+transferred to the requestor as a 32-bit value---if no data type was
+specified, its type is @code{ATOM}.
+
+ If the value is a number between @code{-32769} and @code{32768}, it
+is transferred to the requestor as a 16 bit value---if no data type
+was specified, its type is @code{INTEGER}.
+
+ If the value is any other number, it is returned as a 32 bit value.
+Even if the number returned is unsigned, the requestor will treat
+words of type @code{INTEGER} as signed. To return an unsigned value,
+specify the type @code{CARDINAL} instead.
+
+ If the value is a vector of symbols or numbers, it is returned as a
+list of multiple atoms or numbers. The data type returned by default
+is determined by that of its first element.
+@end defvar
+
+ By default, Emacs is configured with selection converters for the
+following selection targets:
+
+@table @code
+@item TEXT
+This selection converter returns selection data as:
+
+@itemize @bullet
+@item
+A string of type @code{C_STRING}, if the selection contents contain no
+multibyte characters, or contains 8-bit characters with all 8 bits
+set.
+
+@item
+A string of type @code{STRING}, if the selection contents can be
+represented as ISO-Latin-1 text.
+
+@item
+A string of type @code{COMPOUND_TEXT}, if the selection contents can
+be encoded in the X Consortium's Compound Text Encoding, and
+@code{selection-coding-system} or @code{next-selection-coding-system}
+is set to a coding system whose @code{:mime-charset} property is
+@code{x-ctext}.
+
+@item
+A string of type @code{UTF8_STRING} otherwise.
+@end itemize
+
+@item COMPOUND_TEXT
+This selection converter returns selection data as a string of type
+@code{COMPOUND_TEXT}.
+
+@item STRING
+This selection converter returns selection data as a string of type
+@code{STRING}, encoded in ISO-Latin-1 format.
+
+@item UTF8_STRING
+This selection converter returns selection data in UTF-8 format.
+
+@item text/plain
+@item text/plain;charset=utf-8
+@item text/uri-list
+@item text/x-xdnd-username
+@item XmTRANSFER_SUCCESS
+@item XmTRANSFER_FAILURE
+@item FILE
+@item _DT_NETFILE
+These selection converters are used for internal purposes during
+drag-and-drop operations and are not available for selections other
+than @code{XdndSelection}.
+
+@item TARGETS
+This selection converter returns a list of atoms, one for each
+selection target understood by Emacs.
+
+@item MULTIPLE
+This selection converter is implemented in C code and is used to
+implement efficient transfer of selection requests which specify
+multiple selection targets at the same time.
+
+@item LENGTH
+This selection converter returns the length of the selection data, in
+bytes.
+
+@item DELETE
+This selection converter is used for internal purposes during
+drag-and-drop operations.
+
+@item FILE_NAME
+This selection converter returns the file name of the buffer
+containing the selection data.
+
+@item CHARACTER_POSITION
+This selection converter returns the character positions of each end
+of the selection in the buffer containing the selection data.
+
+@item LINE_NUMBER
+@item COLUMN_NUMBER
+This selection converter returns the line and column numbers of each
+end of the selection in the buffer containing the selection data.
+
+@item OWNER_OS
+This selection converter returns the name of the operating system on
+which Emacs is running.
+
+@item HOST_NAME
+This selection converter returns the fully-qualified domain name of
+the machine on which Emacs is running.
+
+@item USER
+This selection converter returns the username of the user account
+under which Emacs is running.
+
+@item CLASS
+@item NAME
+These selection converters return the resource class and name used by
+Emacs.
+
+@item INTEGER
+This selection converter returns an integer value verbatim.
+
+@item SAVE_TARGETS
+@item _EMACS_INTERNAL
+These selection converters are used for internal purposes.
+@end table
+
+ With the exception of @code{INTEGER}, all selection converters
+expect the value given to @code{gui-set-selection} to be one of the
+following:
+
+@itemize @bullet
+@item
+A string.
+
+@item
+A list of the form @w{@code{(@var{beg} @var{end} @var{buf})}}, where
+@var{beg} and @var{end} are two markers or overlays describing the
+bounds of the selection data in the buffer @var{buf}.
+@end itemize
+
+@node Other Selections
+@subsection Other Selections
+
+ Window systems such as MS-Windows, Nextstep, Haiku and Android do
+not provide selections corresponding to the X semantics. Each window
+system provides its own ad-hoc emulation of selections, none of which
+make use of the ``selection converter'' mechanism described above. In
+addition, only the @code{PRIMARY}, @code{CLIPBOARD}, and
+@code{SECONDARY} selections are typically supported, alongside the
+@code{XdndSelection} used for drag-and-drop operations.
+
+ GTK itself exposes emulations of X selections to applications, but
+those emulations are of varying completeness. While Emacs built with
+PGTK will use the same selection interface as Emacs built with X, many
+selection targets will not be useful.
+
+ On MS-Windows, @code{gui-get-selection} accepts a single target,
+@code{STRING}. The value returned is the selection data decoded
+using @code{selection-coding-system}.
+
+ @code{gui-set-selection} also only accepts strings, encodes them
+in the selection coding system, and saves them to the clipboard.
+
+ On Nextstep, Emacs only supports saving strings to selections.
+However, requests for the following targets are accepted:
+
+@c FIXME: how is the text coding system determined, and do image/* or
+@c application/* return image data or file names?
+@itemize @bullet
+@item text/plain
+@item image/png
+@item text/html
+@item application/pdf
+@item application/rtf
+@item application/rtfd
+@item STRING
+@item text/plain
+@item image/tiff
+@end itemize
+
+ On Haiku, Emacs supports the same selection values as on X. In
+addition, Emacs fully implements the primary and secondary selections.
+However, instead of taking ownership over the selection data, Emacs
+transfers the selection data to the window server when
+@code{gui-set-selection} is called. The Haiku window server expects
+selection data to be provided in the form of a ``message'', containing
+associations between data types and selection data.
+
+@defvar haiku-normal-selection-encoders
+List of functions which act as selection encoders. When
+@code{gui-set-selection} is called, each function in this list is
+successively called with its @var{selection} and @var{value}
+arguments. If the function returns non-@code{nil}, it should return a
+list of the form @w{@code{(@var{key} @var{type} @var{value})}}, where
+@var{key} is the name of the data type being transferred, @var{type}
+is either a number identifying a data type (in which case @var{value}
+should be a unibyte string that is directly transferred to the window
+server), or a symbol identifying both a data type and how @var{value}
+should be interpreted.
+@end defvar
+
+ Here are the meaningful values of @var{type}, and what they will
+cause Emacs to interpret @var{value} as:
+
+@table @code
+@item string
+A unibyte string. The string is NULL-terminated after being placed in
+the message.
+
+@item ref
+A file name. The file is looked up and file system information
+identifying the file is placed in the message.
+
+@item short
+A 16-bit integer value.
+
+@item long
+A 32-bit integer value.
+
+@item llong
+A 64-bit integer value.
+
+@item byte
+@item char
+An unsigned byte between 0 and 255.
+
+@item size_t
+A number between 0 and 1 minus two to the power of the word size of
+the computer Emacs is running on.
+
+@item ssize_t
+A number which fits in the C type @code{ssize_t}.
+
+@item point
+A cons of two floats, specifying a coordinate on-screen.
+
+@item float
+@item double
+A single or double-precision floating point number in an unspecified
+format.
+
+@item (haiku-numeric-enum MIME)
+A unibyte string containing data in a certain MIME type.
+@end table
+
+ Under Haiku, @code{gui-get-selection} accepts either the targets
+@code{TARGETS} and @code{TIMESTAMP}, where the former returns a vector
+containing supported data types (much like on X), and the latter
+returns the number of times the selection has been set, the targets
+@code{STRING} and @code{UTF8_STRING}, which return text in ISO-Latin-1
+and UTF-8 format, or a MIME type, in which the data is returned
+undecoded as a unibyte string.
+
+ Under Android, @code{gui-get-selection} is restricted to returning
+UTF-8 string data of the type @code{STRING}, or image and application
+data associated with a MIME type. @code{gui-set-selection} will only
+set string data, as on MS-Windows.
+
@node Yanking Media
@section Yanking Media
diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi
index 4bcc9d5fea6..763782425c2 100644
--- a/doc/lispref/os.texi
+++ b/doc/lispref/os.texi
@@ -972,6 +972,9 @@ Hewlett-Packard HPUX operating system.
@item nacl
Google Native Client (@acronym{NaCl}) sandboxing system.
+@item android
+The Open Handset Alliance's Android operating system.
+
@item ms-dos
Microsoft's DOS@. Emacs compiled with DJGPP for MS-DOS binds
@code{system-type} to @code{ms-dos} even when you run it on MS-Windows.
diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi
index c901215d35d..7cbe87240c9 100644
--- a/doc/lispref/processes.texi
+++ b/doc/lispref/processes.texi
@@ -185,6 +185,24 @@ respective remote host. In case of a local @code{default-directory},
the function returns just the value of the variable @code{exec-path}.
@end defun
+@cindex programs distributed with Emacs, starting
+@vindex ctags-program-name
+@vindex etags-program-name
+@vindex hexl-program-name
+@vindex emacsclient-program-name
+@vindex movemail-program-name
+@vindex ebrowse-program-manem
+ When starting a program that is part of the Emacs distribution,
+you must take into account that the program may have been renamed in
+order to comply with executable naming restrictions present on the
+system.
+
+ Instead of starting @command{ctags}, for example, you should specify
+the value of @code{ctags-program-name} instead. Likewise, instead of
+starting @command{movemail}, you must start
+@code{movemail-program-name}, and the same goes for @command{etags},
+@command{hexl}, @command{emacsclient}, and @command{ebrowse}.
+
@node Shell Arguments
@section Shell Arguments
@cindex arguments for shell commands
diff --git a/etc/DEBUG b/etc/DEBUG
index c4f0852abb3..1cc575598bf 100644
--- a/etc/DEBUG
+++ b/etc/DEBUG
@@ -1099,6 +1099,39 @@ Please refer to the LLDB reference on the web for more information
about LLDB. If you already know GDB, you will also find a mapping
from GDB commands to corresponding LLDB commands there.
+** Debugging Emacs on Android.
+
+Attaching GDB to Emacs running inside the Android application setup
+requires a special script found in the java/ directory, and a suitable
+GDB server binary to be present on the Android device, which is
+present on the free versions of Android. Connecting to the device
+also requires the `adb' (Android Debug Bridge) utility, and telling
+the Android system to resume the Emacs process after startup requires
+the Java debugger (jdb).
+
+If all three of those tools are present, simply run (from the Emacs
+source directory):
+
+ ../java/debug.sh -- [any extra arguments you wish to pass to gdb]
+
+After which, upon waiting a while, the GDB prompt will show up.
+
+If Emacs crashes and "JNI ERROR" shows up in the Android system log,
+then placing a breakpoint on:
+
+ break art::JavaVMExt::JniAbort
+
+will let you find the source of the crash.
+
+If there is no `gdbserver' binary present on the device, then you can
+specify one to upload, like so:
+
+ ../java/debug.sh --gdbserver /path/to/gdbserver
+
+In addition, when Emacs runs as a 64-bit process on a system
+supporting both 64 and 32-bit binaries, you must specify the path to a
+64-bit gdbserver binary.
+
This file is part of GNU Emacs.
diff --git a/etc/MACHINES b/etc/MACHINES
index 8c6f3f48ce7..c9fdc6cea96 100644
--- a/etc/MACHINES
+++ b/etc/MACHINES
@@ -131,6 +131,18 @@ the list at the end of this file.
The earliest release of Haiku that will successfully compile Emacs
is R1/Beta2. For windowing support, R1/Beta3 or later is required.
+** Android
+
+ Emacs is known to run on all Android versions from 2.2 onwards, on
+ Linux kernel 2.26.29 or later.
+
+ Android 2.2 has only been tested on ARM. mips64 has not been
+ tested, but builds. With these exceptions, Emacs is known to run on
+ all supported versions of Android on all supported machines: arm,
+ armv7, arm64, x86, x86_64, and mips.
+
+ See the file INSTALL.android for detailed installation instructions.
+
* Obsolete platforms
diff --git a/etc/NEWS b/etc/NEWS
index b4846eb11b0..a342614a9ef 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 'java/INSTALL' for more details.
+
* Startup Changes in Emacs 30.1
@@ -46,6 +52,12 @@ compositing manager, Emacs will now redisplay such a frame even though
'frame-visible-p' returns nil or 'icon' for it. This can happen, for
example, as part of preview for iconified frames.
+---
+** New user option 'menu-bar-close-window'.
+When non-nil, selecting Close from the File menu or clicking Close in
+the tool bar will result in the current window being closed, if
+possible.
+
+++
** 'write-region-inhibit-fsync' now defaults to t in interactive mode,
as it has in batch mode since Emacs 24.
@@ -79,6 +91,12 @@ plus, minus, check-mark, start, etc.
* 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.
+
+
---
** On X, Emacs now supports input methods which perform "string conversion".
This means an input method can now ask Emacs to delete text
@@ -381,6 +399,24 @@ hooks named after the feature name, like 'esh-mode-unload-hook'.
* Lisp Changes in Emacs 30.1
+++
+** New variables describing the names of built in programs.
+The new variables 'ctags-program-name', 'ebrowse-program-name',
+'etags-program-name', 'hexl-program-name', 'emacsclient-program-name'
+and 'movemail-program-name' should be used instead of "ctags",
+"ebrowse", "etags", "hexl", and "emacsclient", when starting one of
+these built in programs in a subprocess.
+
++++
+** '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 variable 'safe-local-variable-directories'.
This variable names directories in which Emacs will treat all
directory-local variables as safe.
diff --git a/etc/PROBLEMS b/etc/PROBLEMS
index bda6c4d4f34..c4363310541 100644
--- a/etc/PROBLEMS
+++ b/etc/PROBLEMS
@@ -3301,6 +3301,56 @@ Compose key to stop working.
On X Windows, users should not use Emacs configured with PGTK, since
this and many other problems do not exist on the regular X builds.
+* Runtime problems specific to Android
+
+** Text displayed in the default monospace font looks horrible.
+
+Droid Sans Mono (the default Monospace font which comes with Android)
+comes with instruction code designed for Microsoft's proprietary
+TrueType font scaler. When this code is executed by Emacs to instruct
+a glyph containing more than one component, it tries to address
+"reference points" which are set to the values of two extra "phantom
+points" in the glyph, that are a proprietary extension of the MS font
+scaler.
+
+Emacs does not support these extensions, and as a result characters
+such as
+
+ ĥ
+
+display incorrectly, with the right most edge of the `h' component
+stretched very far out to the right, on some low density displays.
+
+The solution is to replace the MS-specific hinting code in Droid Sans
+Mono with automatically generated code from the FreeType project's
+"ttfautohint" program. First, extract
+'/system/fonts/DroidSansMono.ttf' from your device:
+
+ $ adb pull /system/fonts/DroidSansMono.ttf
+ /system/fonts/DroidSansMono.ttf: 1 file pulled, 0 skipped.
+ 23.1 MB/s (90208 bytes in 0.004s)
+
+install the "ttfautohint" program:
+
+ http://freetype.org/ttfautohint/
+
+generate a font file with new hinting instructions:
+
+ $ ttfautohint DroidSansMono.ttf > DroidSansMono.ttf.rpl
+
+and upload them to your device, either back to /system/fonts (which is
+allowed by free versions of Android, such as Replicant):
+
+ $ adb root
+ $ adb remount
+ $ adb push DroidSansMono.ttf.rpl /system/fonts/DroidSansMono.ttf
+
+or to the user fonts directory described in the "Android Fonts" node
+of the Emacs manual. You may want to perform this procedure even if
+you are not seeing problems with character display, as the
+automatically generated instructions result in superior display
+results that are easier to read.
+
* Build-time problems
** Configuration
diff --git a/exec/Makefile.in b/exec/Makefile.in
new file mode 100644
index 00000000000..b2f134e85e5
--- /dev/null
+++ b/exec/Makefile.in
@@ -0,0 +1,140 @@
+### @configure_input@
+
+# 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/>.
+
+# Configure build directory information.
+
+ srcdir = @srcdir@
+ VPATH = @srcdir@
+ builddir = @builddir@
+
+# Set up compilation tools.
+
+ CC = @CC@
+ AS = @AS@
+ LD = @LD@
+ M4 = @M4@
+ CPP = @CPP@
+ ASFLAGS = @ASFLAGS@
+ ARFLAGS = @ARFLAGS@
+ CFLAGS = @CFLAGS@
+ CPPFLAGS = @CPPFLAGS@
+ LDFLAGS = @LDFLAGS@
+LOADERFLAGS = @LOADERFLAGS@
+FIND_DELETE = @FIND_DELETE@
+
+# Set up object files.
+
+ LOADER = @exec_loader@
+ OBJS = @OBJS@
+ LOADOBJS = $(patsubst %.s,%.o,$(LOADER))
+
+# Set up automatic dependency tracking.
+
+AUTO_DEPEND = @AUTO_DEPEND@
+DEPDIR = deps
+ifeq ($(AUTO_DEPEND),yes)
+DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP
+-include $(OBJS:%.o=$(DEPDIR)/%.d)
+-include $(DEPDIR)/test.d
+-include $(DEPDIR)/exec1.d
+else
+DEPFLAGS =
+include $(srcdir)/deps.mk
+endif
+
+# Set up the appropriate targets.
+
+all: libexec.a loader
+
+# Set up automatic Makefile regeneration.
+
+$(srcdir)/configure: $(srcdir)/configure.ac
+ cd $(srcdir) && autoreconf
+
+config.status: $(srcdir)/configure
+ if [ -x config.status ]; then \
+ ./config.status --recheck; \
+ else \
+ $(srcdir)/configure; \
+ fi
+
+Makefile: config.status Makefile.in
+ MAKE="$(MAKE)" ./config.status
+
+# Set up rules to build targets.
+
+.SUFFIXES: .c .s
+.c.o:
+ $(CC) -c $(CPPFLAGS) $(CFLAGS) $(DEPFLAGS) -I. -I$(srcdir) $< -o $@
+.s.o:
+ $(M4) $< > $(notdir $<).s
+ $(AS) $(ASFLAGS) $(notdir $<).s -o $@
+
+# Set up dependencies for config-mips.m4.
+
+config-mips.m4: config-mips.m4.in
+ cd $(srcdir) && ./config.status $@
+$(LOADOBJS): config-mips.m4
+
+# Set up rules to build libexec.a.
+
+libexec.a: $(OBJS)
+ $(AR) cru $(ARFLAGS) $@ $^
+
+# And loader.
+
+loader: $(LOADOBJS)
+ $(LD) -o $@ $(LOADERFLAGS) $(LOADOBJS)
+
+# And test.
+
+test: test.o libexec.a
+ $(CC) $(LDFLAGS) $< libexec.a -o $@
+
+# And exec1.
+
+exec1: exec1.o libexec.a
+ $(CC) $(LDFLAGS) $< libexec.a -o $@
+
+# Set up targets for cleaning.
+
+.PHONY: clean distclean maintainer-clean extraclean bootstrap-clean
+clean:
+ rm -f *.o *.a loader test *.s.s
+ifeq ($(AUTO_DEPEND),yes)
+ rm -rf deps/*.d
+endif
+
+distclean: clean
+ rm -f Makefile config.status config.h config-mips.m4
+
+maintainer-clean: distclean
+
+### This doesn't actually appear in the coding standards, but Karl
+### says GCC supports it, and that's where the configuration part of
+### the coding standards seem to come from. It's like distclean, but
+### it deletes backup and autosave files too.
+
+extraclean: maintainer-clean
+ -rm -f config-tmp-* $(srcdir)/aclocal.m4 $(srcdir)/configure \
+ $(srcdir)/src/config.in
+ -[ "$(srcdir)" = "." ] || \
+ find $(srcdir) '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE)
+ -find . '(' -name '*~' -o -name '#*' ')' $(FIND_DELETE)
+bootstrap-clean: extraclean
diff --git a/exec/README b/exec/README
new file mode 100644
index 00000000000..f7eb21cfc84
--- /dev/null
+++ b/exec/README
@@ -0,0 +1,3 @@
+This directory holds the source code to a library used to replace the
+`execve' and `execveat' system calls, used by the Android port of
+Emacs to start executables without intervention from the system.
diff --git a/exec/config-mips.m4.in b/exec/config-mips.m4.in
new file mode 100644
index 00000000000..72632765bd0
--- /dev/null
+++ b/exec/config-mips.m4.in
@@ -0,0 +1,42 @@
+dnl Assembler templates for MIPS computers.
+dnl
+dnl Copyright (C) 2023 Free Software Foundation, Inc.
+dnl
+dnl This file is part of GNU Emacs.
+dnl
+dnl GNU Emacs is free software: you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation, either version 3 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl GNU Emacs is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+define(`SYSCALL_open', `ifelse(`@MIPS_N32@',`yes',`6002',`4005')')
+define(`SYSCALL_close', `ifelse(`@MIPS_N32@',`yes',`6003',`4006')')
+define(`SYSCALL_mmap', `ifelse(`@MIPS_N32@',`yes',`6009',`4090')')
+define(`SYSCALL_nanosleep', `ifelse(`@MIPS_N32@',`yes',`6034',`4166')')
+define(`SYSCALL_exit', `ifelse(`@MIPS_N32@',`yes',`6058',`4001')')
+define(`SYSCALL_prctl', `ifelse(`@MIPS_N32@',`yes',`6153',`4192')')
+
+define(`SYSCALL', `ifelse(`@MIPS_N32@',`yes',` move $a4, $1
+ move $a5, $2
+ move $a6, $3
+ move $a7, $4',` addi $sp, -32
+ sw $1, 16($sp)
+ sw $2, 20($sp)
+ sw $3, 24($sp)
+ sw $4, 28($sp)')')
+
+define(`RESTORE', `ifelse(`@MIPS_N32@',`yes',` nop',` addi $sp, 32')')
+
+dnl For mips64. Some assemblers don't want to assemble `daddi'.
+define(`DADDI2', `ifelse(`@DADDI_BROKEN@',`yes',` li $at, $2
+dadd $1, $1, $at',` daddi $1, $2')')
+define(`DADDI3', `ifelse(`@DADDI_BROKEN@',`yes',` li $at, $3
+dadd $1, $2, $at',` daddi $1, $2, $3')')
diff --git a/exec/config.guess b/exec/config.guess
new file mode 100755
index 00000000000..c7f17e8fb97
--- /dev/null
+++ b/exec/config.guess
@@ -0,0 +1,1768 @@
+#!/usr/bin/sh
+# Attempt to guess a canonical system name.
+# Copyright 1992-2022 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2022-05-25'
+
+# 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX. However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2022 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."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+# Just in case it came from the environment.
+GUESS=
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+ # prevent multiple calls if $tmp is already set
+ test "$tmp" && return 0
+ : "${TMPDIR=/tmp}"
+ # shellcheck disable=SC2039,SC3028
+ { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+ dummy=$tmp/dummy
+ case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+ ,,) echo "int x;" > "$dummy.c"
+ for driver in cc gcc c89 c99 ; do
+ if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+ CC_FOR_BUILD=$driver
+ break
+ fi
+ done
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+ esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case $UNAME_SYSTEM in
+Linux|GNU|GNU/*)
+ LIBC=unknown
+
+ set_cc_for_build
+ cat <<-EOF > "$dummy.c"
+ #include <features.h>
+ #if defined(__UCLIBC__)
+ LIBC=uclibc
+ #elif defined(__dietlibc__)
+ LIBC=dietlibc
+ #elif defined(__GLIBC__)
+ LIBC=gnu
+ #else
+ #include <stdarg.h>
+ /* First heuristic to detect musl libc. */
+ #ifdef __DEFINED_va_list
+ LIBC=musl
+ #endif
+ #endif
+ EOF
+ cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+ eval "$cc_set_libc"
+
+ # Second heuristic to detect musl libc.
+ if [ "$LIBC" = unknown ] &&
+ command -v ldd >/dev/null &&
+ ldd --version 2>&1 | grep -q ^musl; then
+ LIBC=musl
+ fi
+
+ # If the system lacks a compiler, then just pick glibc.
+ # We could probably try harder.
+ if [ "$LIBC" = unknown ]; then
+ LIBC=gnu
+ fi
+ ;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+ /sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+ /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+ echo unknown)`
+ case $UNAME_MACHINE_ARCH in
+ aarch64eb) machine=aarch64_be-unknown ;;
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ earmv*)
+ arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+ endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+ machine=${arch}${endian}-unknown
+ ;;
+ *) machine=$UNAME_MACHINE_ARCH-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently (or will in the future) and ABI.
+ case $UNAME_MACHINE_ARCH in
+ earm*)
+ os=netbsdelf
+ ;;
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # Determine ABI tags.
+ case $UNAME_MACHINE_ARCH in
+ earm*)
+ expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+ abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case $UNAME_VERSION in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ GUESS=$machine-${os}${release}${abi-}
+ ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+ GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE
+ ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+ GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE
+ ;;
+ *:SecBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'`
+ GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE
+ ;;
+ *:LibertyBSD:*:*)
+ UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+ GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE
+ ;;
+ *:MidnightBSD:*:*)
+ GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE
+ ;;
+ *:ekkoBSD:*:*)
+ GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE
+ ;;
+ *:SolidBSD:*:*)
+ GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE
+ ;;
+ *:OS108:*:*)
+ GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE
+ ;;
+ macppc:MirBSD:*:*)
+ GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE
+ ;;
+ *:MirBSD:*:*)
+ GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE
+ ;;
+ *:Sortix:*:*)
+ GUESS=$UNAME_MACHINE-unknown-sortix
+ ;;
+ *:Twizzler:*:*)
+ GUESS=$UNAME_MACHINE-unknown-twizzler
+ ;;
+ *:Redox:*:*)
+ GUESS=$UNAME_MACHINE-unknown-redox
+ ;;
+ mips:OSF1:*.*)
+ GUESS=mips-dec-osf1
+ ;;
+ alpha:OSF1:*:*)
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ trap '' 0
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+ ;;
+ *5.*)
+ UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+ case $ALPHA_CPU_TYPE in
+ "EV4 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE=alpha ;;
+ "EV5 (21164)")
+ UNAME_MACHINE=alphaev5 ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE=alphaev56 ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE=alphapca56 ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE=alphapca57 ;;
+ "EV6 (21264)")
+ UNAME_MACHINE=alphaev6 ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE=alphaev67 ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE=alphaev69 ;;
+ "EV7 (21364)")
+ UNAME_MACHINE=alphaev7 ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE=alphaev79 ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+ GUESS=$UNAME_MACHINE-dec-osf$OSF_REL
+ ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ GUESS=m68k-unknown-sysv4
+ ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ GUESS=$UNAME_MACHINE-unknown-amigaos
+ ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ GUESS=$UNAME_MACHINE-unknown-morphos
+ ;;
+ *:OS/390:*:*)
+ GUESS=i370-ibm-openedition
+ ;;
+ *:z/VM:*:*)
+ GUESS=s390-ibm-zvmoe
+ ;;
+ *:OS400:*:*)
+ GUESS=powerpc-ibm-os400
+ ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ GUESS=arm-acorn-riscix$UNAME_RELEASE
+ ;;
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
+ GUESS=arm-unknown-riscos
+ ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ GUESS=hppa1.1-hitachi-hiuxmpp
+ ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ case `(/bin/universe) 2>/dev/null` in
+ att) GUESS=pyramid-pyramid-sysv3 ;;
+ *) GUESS=pyramid-pyramid-bsd ;;
+ esac
+ ;;
+ NILE*:*:*:dcosx)
+ GUESS=pyramid-pyramid-svr4
+ ;;
+ DRS?6000:unix:4.0:6*)
+ GUESS=sparc-icl-nx6
+ ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case `/usr/bin/uname -p` in
+ sparc) GUESS=sparc-icl-nx7 ;;
+ esac
+ ;;
+ s390x:SunOS:*:*)
+ SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+ GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL
+ ;;
+ sun4H:SunOS:5.*:*)
+ SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+ GUESS=sparc-hal-solaris2$SUN_REL
+ ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+ GUESS=sparc-sun-solaris2$SUN_REL
+ ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ GUESS=i386-pc-auroraux$UNAME_RELEASE
+ ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ set_cc_for_build
+ SUN_ARCH=i386
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH=x86_64
+ fi
+ fi
+ SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+ GUESS=$SUN_ARCH-pc-solaris2$SUN_REL
+ ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+ GUESS=sparc-sun-solaris3$SUN_REL
+ ;;
+ sun4*:SunOS:*:*)
+ case `/usr/bin/arch -k` in
+ Series*|S4*)
+ UNAME_RELEASE=`uname -v`
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
+ GUESS=sparc-sun-sunos$SUN_REL
+ ;;
+ sun3*:SunOS:*:*)
+ GUESS=m68k-sun-sunos$UNAME_RELEASE
+ ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+ test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+ case `/bin/arch` in
+ sun3)
+ GUESS=m68k-sun-sunos$UNAME_RELEASE
+ ;;
+ sun4)
+ GUESS=sparc-sun-sunos$UNAME_RELEASE
+ ;;
+ esac
+ ;;
+ aushp:SunOS:*:*)
+ GUESS=sparc-auspex-sunos$UNAME_RELEASE
+ ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ GUESS=m68k-atari-mint$UNAME_RELEASE
+ ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ GUESS=m68k-atari-mint$UNAME_RELEASE
+ ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ GUESS=m68k-atari-mint$UNAME_RELEASE
+ ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ GUESS=m68k-milan-mint$UNAME_RELEASE
+ ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ GUESS=m68k-hades-mint$UNAME_RELEASE
+ ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ GUESS=m68k-unknown-mint$UNAME_RELEASE
+ ;;
+ m68k:machten:*:*)
+ GUESS=m68k-apple-machten$UNAME_RELEASE
+ ;;
+ powerpc:machten:*:*)
+ GUESS=powerpc-apple-machten$UNAME_RELEASE
+ ;;
+ RISC*:Mach:*:*)
+ GUESS=mips-dec-mach_bsd4.3
+ ;;
+ RISC*:ULTRIX:*:*)
+ GUESS=mips-dec-ultrix$UNAME_RELEASE
+ ;;
+ VAX*:ULTRIX*:*:*)
+ GUESS=vax-dec-ultrix$UNAME_RELEASE
+ ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ GUESS=clipper-intergraph-clix$UNAME_RELEASE
+ ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+ dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+ SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ GUESS=mips-mips-riscos$UNAME_RELEASE
+ ;;
+ Motorola:PowerMAX_OS:*:*)
+ GUESS=powerpc-motorola-powermax
+ ;;
+ Motorola:*:4.3:PL8-*)
+ GUESS=powerpc-harris-powermax
+ ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ GUESS=powerpc-harris-powermax
+ ;;
+ Night_Hawk:Power_UNIX:*:*)
+ GUESS=powerpc-harris-powerunix
+ ;;
+ m88k:CX/UX:7*:*)
+ GUESS=m88k-harris-cxux7
+ ;;
+ m88k:*:4*:R4*)
+ GUESS=m88k-motorola-sysv4
+ ;;
+ m88k:*:3*:R3*)
+ GUESS=m88k-motorola-sysv3
+ ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
+ then
+ if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+ test "$TARGET_BINARY_INTERFACE"x = x
+ then
+ GUESS=m88k-dg-dgux$UNAME_RELEASE
+ else
+ GUESS=m88k-dg-dguxbcs$UNAME_RELEASE
+ fi
+ else
+ GUESS=i586-dg-dgux$UNAME_RELEASE
+ fi
+ ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ GUESS=m88k-dolphin-sysv3
+ ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ GUESS=m88k-motorola-sysv3
+ ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ GUESS=m88k-tektronix-sysv3
+ ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ GUESS=m68k-tektronix-bsd
+ ;;
+ *:IRIX*:*:*)
+ IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'`
+ GUESS=mips-sgi-irix$IRIX_REL
+ ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ ;; # Note that: echo "'`uname -s`'" gives 'AIX '
+ i*86:AIX:*:*)
+ GUESS=i386-ibm-aix
+ ;;
+ ia64:AIX:*:*)
+ if test -x /usr/bin/oslevel ; then
+ IBM_REV=`/usr/bin/oslevel`
+ else
+ IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+ fi
+ GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV
+ ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+ then
+ GUESS=$SYSTEM_NAME
+ else
+ GUESS=rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ GUESS=rs6000-ibm-aix3.2.4
+ else
+ GUESS=rs6000-ibm-aix3.2
+ fi
+ ;;
+ *:AIX:*:[4567])
+ IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+ if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if test -x /usr/bin/lslpp ; then
+ IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+ else
+ IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+ fi
+ GUESS=$IBM_ARCH-ibm-aix$IBM_REV
+ ;;
+ *:AIX:*:*)
+ GUESS=rs6000-ibm-aix
+ ;;
+ ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+ GUESS=romp-ibm-bsd4.4
+ ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to
+ ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ GUESS=rs6000-bull-bosx
+ ;;
+ DPX/2?00:B.O.S.:*:*)
+ GUESS=m68k-bull-sysv3
+ ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ GUESS=m68k-hp-bsd
+ ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ GUESS=m68k-hp-bsd4.4
+ ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+ case $UNAME_MACHINE in
+ 9000/31?) HP_ARCH=m68000 ;;
+ 9000/[34]??) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if test -x /usr/bin/getconf; then
+ sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+ sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+ case $sc_cpu_version in
+ 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case $sc_kernel_bits in
+ 32) HP_ARCH=hppa2.0n ;;
+ 64) HP_ARCH=hppa2.0w ;;
+ '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if test "$HP_ARCH" = ""; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if test "$HP_ARCH" = hppa2.0w
+ then
+ set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH=hppa2.0w
+ else
+ HP_ARCH=hppa64
+ fi
+ fi
+ GUESS=$HP_ARCH-hp-hpux$HPUX_REV
+ ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+ GUESS=ia64-hp-hpux$HPUX_REV
+ ;;
+ 3050*:HI-UX:*:*)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+ GUESS=unknown-hitachi-hiuxwe2
+ ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+ GUESS=hppa1.1-hp-bsd
+ ;;
+ 9000/8??:4.3bsd:*:*)
+ GUESS=hppa1.0-hp-bsd
+ ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ GUESS=hppa1.0-hp-mpeix
+ ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+ GUESS=hppa1.1-hp-osf
+ ;;
+ hp8??:OSF1:*:*)
+ GUESS=hppa1.0-hp-osf
+ ;;
+ i*86:OSF1:*:*)
+ if test -x /usr/sbin/sysversion ; then
+ GUESS=$UNAME_MACHINE-unknown-osf1mk
+ else
+ GUESS=$UNAME_MACHINE-unknown-osf1
+ fi
+ ;;
+ parisc*:Lites*:*:*)
+ GUESS=hppa1.1-hp-lites
+ ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ GUESS=c1-convex-bsd
+ ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ GUESS=c34-convex-bsd
+ ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ GUESS=c38-convex-bsd
+ ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ GUESS=c4-convex-bsd
+ ;;
+ CRAY*Y-MP:*:*:*)
+ CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+ GUESS=ymp-cray-unicos$CRAY_REL
+ ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+ GUESS=t90-cray-unicos$CRAY_REL
+ ;;
+ CRAY*T3E:*:*:*)
+ CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+ GUESS=alphaev5-cray-unicosmk$CRAY_REL
+ ;;
+ CRAY*SV1:*:*:*)
+ CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+ GUESS=sv1-cray-unicos$CRAY_REL
+ ;;
+ *:UNICOS/mp:*:*)
+ CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+ GUESS=craynv-cray-unicosmp$CRAY_REL
+ ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+ GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+ ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+ FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+ GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+ ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE
+ ;;
+ sparc*:BSD/OS:*:*)
+ GUESS=sparc-unknown-bsdi$UNAME_RELEASE
+ ;;
+ *:BSD/OS:*:*)
+ GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE
+ ;;
+ arm:FreeBSD:*:*)
+ UNAME_PROCESSOR=`uname -p`
+ set_cc_for_build
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+ GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
+ else
+ FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+ GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
+ fi
+ ;;
+ *:FreeBSD:*:*)
+ UNAME_PROCESSOR=`/usr/bin/uname -p`
+ case $UNAME_PROCESSOR in
+ amd64)
+ UNAME_PROCESSOR=x86_64 ;;
+ i386)
+ UNAME_PROCESSOR=i586 ;;
+ esac
+ FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+ GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL
+ ;;
+ i*:CYGWIN*:*)
+ GUESS=$UNAME_MACHINE-pc-cygwin
+ ;;
+ *:MINGW64*:*)
+ GUESS=$UNAME_MACHINE-pc-mingw64
+ ;;
+ *:MINGW*:*)
+ GUESS=$UNAME_MACHINE-pc-mingw32
+ ;;
+ *:MSYS*:*)
+ GUESS=$UNAME_MACHINE-pc-msys
+ ;;
+ i*:PW*:*)
+ GUESS=$UNAME_MACHINE-pc-pw32
+ ;;
+ *:SerenityOS:*:*)
+ GUESS=$UNAME_MACHINE-pc-serenity
+ ;;
+ *:Interix*:*)
+ case $UNAME_MACHINE in
+ x86)
+ GUESS=i586-pc-interix$UNAME_RELEASE
+ ;;
+ authenticamd | genuineintel | EM64T)
+ GUESS=x86_64-unknown-interix$UNAME_RELEASE
+ ;;
+ IA64)
+ GUESS=ia64-unknown-interix$UNAME_RELEASE
+ ;;
+ esac ;;
+ i*:UWIN*:*)
+ GUESS=$UNAME_MACHINE-pc-uwin
+ ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ GUESS=x86_64-pc-cygwin
+ ;;
+ prep*:SunOS:5.*:*)
+ SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+ GUESS=powerpcle-unknown-solaris2$SUN_REL
+ ;;
+ *:GNU:*:*)
+ # the GNU system
+ GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'`
+ GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'`
+ GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL
+ ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
+ GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+ GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
+ ;;
+ *:Minix:*:*)
+ GUESS=$UNAME_MACHINE-unknown-minix
+ ;;
+ aarch64:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ alpha:Linux:*:*)
+ case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ arm*:Linux:*:*)
+ set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi
+ else
+ GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf
+ fi
+ fi
+ ;;
+ avr32*:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ cris:Linux:*:*)
+ GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+ ;;
+ crisv32:Linux:*:*)
+ GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+ ;;
+ e2k:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ frv:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ hexagon:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ i*86:Linux:*:*)
+ GUESS=$UNAME_MACHINE-pc-linux-$LIBC
+ ;;
+ ia64:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ k1om:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ m32r*:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ m68*:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ set_cc_for_build
+ IS_GLIBC=0
+ test x"${LIBC}" = xgnu && IS_GLIBC=1
+ sed 's/^ //' << EOF > "$dummy.c"
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #undef mips64
+ #undef mips64el
+ #if ${IS_GLIBC} && defined(_ABI64)
+ LIBCABI=gnuabi64
+ #else
+ #if ${IS_GLIBC} && defined(_ABIN32)
+ LIBCABI=gnuabin32
+ #else
+ LIBCABI=${LIBC}
+ #endif
+ #endif
+
+ #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa64r6
+ #else
+ #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa32r6
+ #else
+ #if defined(__mips64)
+ CPU=mips64
+ #else
+ CPU=mips
+ #endif
+ #endif
+ #endif
+
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ MIPS_ENDIAN=el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ MIPS_ENDIAN=
+ #else
+ MIPS_ENDIAN=
+ #endif
+ #endif
+EOF
+ cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`
+ eval "$cc_set_vars"
+ test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+ ;;
+ mips64el:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ openrisc*:Linux:*:*)
+ GUESS=or1k-unknown-linux-$LIBC
+ ;;
+ or32:Linux:*:* | or1k*:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ padre:Linux:*:*)
+ GUESS=sparc-unknown-linux-$LIBC
+ ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ GUESS=hppa64-unknown-linux-$LIBC
+ ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+ PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;;
+ PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;;
+ *) GUESS=hppa-unknown-linux-$LIBC ;;
+ esac
+ ;;
+ ppc64:Linux:*:*)
+ GUESS=powerpc64-unknown-linux-$LIBC
+ ;;
+ ppc:Linux:*:*)
+ GUESS=powerpc-unknown-linux-$LIBC
+ ;;
+ ppc64le:Linux:*:*)
+ GUESS=powerpc64le-unknown-linux-$LIBC
+ ;;
+ ppcle:Linux:*:*)
+ GUESS=powerpcle-unknown-linux-$LIBC
+ ;;
+ riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ GUESS=$UNAME_MACHINE-ibm-linux-$LIBC
+ ;;
+ sh64*:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ sh*:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ tile*:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ vax:Linux:*:*)
+ GUESS=$UNAME_MACHINE-dec-linux-$LIBC
+ ;;
+ x86_64:Linux:*:*)
+ set_cc_for_build
+ CPU=$UNAME_MACHINE
+ LIBCABI=$LIBC
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
+ ABI=64
+ sed 's/^ //' << EOF > "$dummy.c"
+ #ifdef __i386__
+ ABI=x86
+ #else
+ #ifdef __ILP32__
+ ABI=x32
+ #endif
+ #endif
+EOF
+ cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
+ eval "$cc_set_abi"
+ case $ABI in
+ x86) CPU=i686 ;;
+ x32) LIBCABI=${LIBC}x32 ;;
+ esac
+ fi
+ GUESS=$CPU-pc-linux-$LIBCABI
+ ;;
+ xtensa*:Linux:*:*)
+ GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+ ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ GUESS=i386-sequent-sysv4
+ ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
+ ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ GUESS=$UNAME_MACHINE-pc-os2-emx
+ ;;
+ i*86:XTS-300:*:STOP)
+ GUESS=$UNAME_MACHINE-unknown-stop
+ ;;
+ i*86:atheos:*:*)
+ GUESS=$UNAME_MACHINE-unknown-atheos
+ ;;
+ i*86:syllable:*:*)
+ GUESS=$UNAME_MACHINE-pc-syllable
+ ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ GUESS=i386-unknown-lynxos$UNAME_RELEASE
+ ;;
+ i*86:*DOS:*:*)
+ GUESS=$UNAME_MACHINE-pc-msdosdjgpp
+ ;;
+ i*86:*:4.*:*)
+ UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL
+ else
+ GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL
+ fi
+ ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case `/bin/uname -X | grep "^Machine"` in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+ ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+ GUESS=$UNAME_MACHINE-pc-isc$UNAME_REL
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL
+ else
+ GUESS=$UNAME_MACHINE-pc-sysv32
+ fi
+ ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configure will decide that
+ # this is a cross-build.
+ GUESS=i586-pc-msdosdjgpp
+ ;;
+ Intel:Mach:3*:*)
+ GUESS=i386-pc-mach3
+ ;;
+ paragon:*:*:*)
+ GUESS=i860-intel-osf1
+ ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4
+ fi
+ ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ GUESS=m68010-convergent-sysv
+ ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ GUESS=m68k-convergent-sysv
+ ;;
+ M680?0:D-NIX:5.3:*)
+ GUESS=m68k-diab-dnix
+ ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ GUESS=m68k-unknown-lynxos$UNAME_RELEASE
+ ;;
+ mc68030:UNIX_System_V:4.*:*)
+ GUESS=m68k-atari-sysv4
+ ;;
+ TSUNAMI:LynxOS:2.*:*)
+ GUESS=sparc-unknown-lynxos$UNAME_RELEASE
+ ;;
+ rs6000:LynxOS:2.*:*)
+ GUESS=rs6000-unknown-lynxos$UNAME_RELEASE
+ ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ GUESS=powerpc-unknown-lynxos$UNAME_RELEASE
+ ;;
+ SM[BE]S:UNIX_SV:*:*)
+ GUESS=mips-dde-sysv$UNAME_RELEASE
+ ;;
+ RM*:ReliantUNIX-*:*:*)
+ GUESS=mips-sni-sysv4
+ ;;
+ RM*:SINIX-*:*:*)
+ GUESS=mips-sni-sysv4
+ ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ GUESS=$UNAME_MACHINE-sni-sysv4
+ else
+ GUESS=ns32k-sni-sysv
+ fi
+ ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ GUESS=i586-unisys-sysv4
+ ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ GUESS=hppa1.1-stratus-sysv4
+ ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ GUESS=i860-stratus-sysv4
+ ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ GUESS=$UNAME_MACHINE-stratus-vos
+ ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ GUESS=hppa1.1-stratus-vos
+ ;;
+ mc68*:A/UX:*:*)
+ GUESS=m68k-apple-aux$UNAME_RELEASE
+ ;;
+ news*:NEWS-OS:6*:*)
+ GUESS=mips-sony-newsos6
+ ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if test -d /usr/nec; then
+ GUESS=mips-nec-sysv$UNAME_RELEASE
+ else
+ GUESS=mips-unknown-sysv$UNAME_RELEASE
+ fi
+ ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ GUESS=powerpc-be-beos
+ ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ GUESS=powerpc-apple-beos
+ ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ GUESS=i586-pc-beos
+ ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ GUESS=i586-pc-haiku
+ ;;
+ ppc:Haiku:*:*) # Haiku running on Apple PowerPC
+ GUESS=powerpc-apple-haiku
+ ;;
+ *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat)
+ GUESS=$UNAME_MACHINE-unknown-haiku
+ ;;
+ SX-4:SUPER-UX:*:*)
+ GUESS=sx4-nec-superux$UNAME_RELEASE
+ ;;
+ SX-5:SUPER-UX:*:*)
+ GUESS=sx5-nec-superux$UNAME_RELEASE
+ ;;
+ SX-6:SUPER-UX:*:*)
+ GUESS=sx6-nec-superux$UNAME_RELEASE
+ ;;
+ SX-7:SUPER-UX:*:*)
+ GUESS=sx7-nec-superux$UNAME_RELEASE
+ ;;
+ SX-8:SUPER-UX:*:*)
+ GUESS=sx8-nec-superux$UNAME_RELEASE
+ ;;
+ SX-8R:SUPER-UX:*:*)
+ GUESS=sx8r-nec-superux$UNAME_RELEASE
+ ;;
+ SX-ACE:SUPER-UX:*:*)
+ GUESS=sxace-nec-superux$UNAME_RELEASE
+ ;;
+ Power*:Rhapsody:*:*)
+ GUESS=powerpc-apple-rhapsody$UNAME_RELEASE
+ ;;
+ *:Rhapsody:*:*)
+ GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE
+ ;;
+ arm64:Darwin:*:*)
+ GUESS=aarch64-apple-darwin$UNAME_RELEASE
+ ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=`uname -p`
+ case $UNAME_PROCESSOR in
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ if command -v xcode-select > /dev/null 2> /dev/null && \
+ ! xcode-select --print-path > /dev/null 2> /dev/null ; then
+ # Avoid executing cc if there is no toolchain installed as
+ # cc will be a stub that puts up a graphical alert
+ # prompting the user to install developer tools.
+ CC_FOR_BUILD=no_compiler_found
+ else
+ set_cc_for_build
+ fi
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
+ # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+ if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_PPC >/dev/null
+ then
+ UNAME_PROCESSOR=powerpc
+ fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # uname -m returns i386 or x86_64
+ UNAME_PROCESSOR=$UNAME_MACHINE
+ fi
+ GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE
+ ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=`uname -p`
+ if test "$UNAME_PROCESSOR" = x86; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE
+ ;;
+ *:QNX:*:4*)
+ GUESS=i386-pc-qnx
+ ;;
+ NEO-*:NONSTOP_KERNEL:*:*)
+ GUESS=neo-tandem-nsk$UNAME_RELEASE
+ ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
+ GUESS=nse-tandem-nsk$UNAME_RELEASE
+ ;;
+ NSR-*:NONSTOP_KERNEL:*:*)
+ GUESS=nsr-tandem-nsk$UNAME_RELEASE
+ ;;
+ NSV-*:NONSTOP_KERNEL:*:*)
+ GUESS=nsv-tandem-nsk$UNAME_RELEASE
+ ;;
+ NSX-*:NONSTOP_KERNEL:*:*)
+ GUESS=nsx-tandem-nsk$UNAME_RELEASE
+ ;;
+ *:NonStop-UX:*:*)
+ GUESS=mips-compaq-nonstopux
+ ;;
+ BS2000:POSIX*:*:*)
+ GUESS=bs2000-siemens-sysv
+ ;;
+ DS/*:UNIX_System_V:*:*)
+ GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE
+ ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ if test "${cputype-}" = 386; then
+ UNAME_MACHINE=i386
+ elif test "x${cputype-}" != x; then
+ UNAME_MACHINE=$cputype
+ fi
+ GUESS=$UNAME_MACHINE-unknown-plan9
+ ;;
+ *:TOPS-10:*:*)
+ GUESS=pdp10-unknown-tops10
+ ;;
+ *:TENEX:*:*)
+ GUESS=pdp10-unknown-tenex
+ ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ GUESS=pdp10-dec-tops20
+ ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ GUESS=pdp10-xkl-tops20
+ ;;
+ *:TOPS-20:*:*)
+ GUESS=pdp10-unknown-tops20
+ ;;
+ *:ITS:*:*)
+ GUESS=pdp10-unknown-its
+ ;;
+ SEI:*:*:SEIUX)
+ GUESS=mips-sei-seiux$UNAME_RELEASE
+ ;;
+ *:DragonFly:*:*)
+ DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+ GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL
+ ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=`(uname -p) 2>/dev/null`
+ case $UNAME_MACHINE in
+ A*) GUESS=alpha-dec-vms ;;
+ I*) GUESS=ia64-dec-vms ;;
+ V*) GUESS=vax-dec-vms ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ GUESS=i386-pc-xenix
+ ;;
+ i*86:skyos:*:*)
+ SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
+ GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
+ ;;
+ i*86:rdos:*:*)
+ GUESS=$UNAME_MACHINE-pc-rdos
+ ;;
+ i*86:Fiwix:*:*)
+ GUESS=$UNAME_MACHINE-pc-fiwix
+ ;;
+ *:AROS:*:*)
+ GUESS=$UNAME_MACHINE-unknown-aros
+ ;;
+ x86_64:VMkernel:*:*)
+ GUESS=$UNAME_MACHINE-unknown-esx
+ ;;
+ amd64:Isilon\ OneFS:*:*)
+ GUESS=x86_64-unknown-onefs
+ ;;
+ *:Unleashed:*:*)
+ GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
+ ;;
+esac
+
+# Do we have a guess based on uname results?
+if test "x$GUESS" != x; then
+ echo "$GUESS"
+ exit
+fi
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname un;
+ uname (&un);
+ printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname *un;
+ uname (&un);
+ printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case $UNAME_MACHINE:$UNAME_SYSTEM in
+ mips:Linux | mips64:Linux)
+ # If we got here on MIPS GNU/Linux, output extra information.
+ cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+ ;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+ https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+and
+ https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+EOF
+
+our_year=`echo $timestamp | sed 's,-.*,,'`
+thisyear=`date +%Y`
+# shellcheck disable=SC2003
+script_age=`expr "$thisyear" - "$our_year"`
+if test "$script_age" -lt 3 ; then
+ cat >&2 <<EOF
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo = `(hostinfo) 2>/dev/null`
+/bin/universe = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+fi
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/exec/config.h.in b/exec/config.h.in
new file mode 100644
index 00000000000..3e04af37f79
--- /dev/null
+++ b/exec/config.h.in
@@ -0,0 +1,358 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* 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/>. */
+
+/* Define to number of reserved bytes past the stack frame. */
+#undef ABI_RED_ZONE
+
+/* Define if building universal (internal helper macro) */
+#undef AC_APPLE_UNIVERSAL_BUILD
+
+/* Define to number of the `clone3' system call. */
+#undef CLONE3_SYSCALL
+
+/* Define to number of the `clone' system call. */
+#undef CLONE_SYSCALL
+
+/* Virtual address for loading PIC executables */
+#undef EXECUTABLE_BASE
+
+/* Define to 1 if the system utilizes 64-bit ELF. */
+#undef EXEC_64
+
+/* Define to number of the `exec' system call. */
+#undef EXEC_SYSCALL
+
+/* Define to 1 if you have the declaration of `stpcpy', and to 0 if you don't.
+ */
+#undef HAVE_DECL_STPCPY
+
+/* Define to 1 if you have the declaration of `stpncpy', and to 0 if you
+ don't. */
+#undef HAVE_DECL_STPNCPY
+
+/* Define to 1 if you have the `getpagesize' function. */
+#undef HAVE_GETPAGESIZE
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the <minix/config.h> header file. */
+#undef HAVE_MINIX_CONFIG_H
+
+/* Define to 1 if process_vm_readv is available. */
+#undef HAVE_PROCESS_VM
+
+/* Define to 1 if `si_syscall' is a member of `siginfo_t'. */
+#undef HAVE_SIGINFO_T_SI_SYSCALL
+
+/* Define to 1 if stdbool.h conforms to C99. */
+#undef HAVE_STDBOOL_H
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#undef HAVE_STDIO_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `stpcpy' function. */
+#undef HAVE_STPCPY
+
+/* Define to 1 if you have the `stpncpy' function. */
+#undef HAVE_STPNCPY
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#undef HAVE_SYS_UIO_H
+
+/* Define to 1 if the system has the type `uintptr_t'. */
+#undef HAVE_UINTPTR_T
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#undef HAVE_WCHAR_H
+
+/* Define to 1 if the system has the type `_Bool'. */
+#undef HAVE__BOOL
+
+/* Virtual address for loading PIC interpreters */
+#undef INTERPRETER_BASE
+
+/* Define to 1 if MIPS NABI calling convention is being used. */
+#undef MIPS_NABI
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to number of the `readlinkat' system call. */
+#undef READLINKAT_SYSCALL
+
+/* Define to number of the `readlink' system call. */
+#undef READLINK_SYSCALL
+
+/* Define to 1 if the library is used within a signal handler. */
+#undef REENTRANT
+
+/* Define to 1 if the stack grows downwards. */
+#undef STACK_GROWS_DOWNWARDS
+
+/* Define to register holding the stack pointer. */
+#undef STACK_POINTER
+
+/* Define to 1 if all of the C90 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#undef STDC_HEADERS
+
+/* Define to register holding arg1 to system calls. */
+#undef SYSCALL_ARG1_REG
+
+/* Define to register holding arg2 to system calls. */
+#undef SYSCALL_ARG2_REG
+
+/* Define to register holding arg3 to system calls. */
+#undef SYSCALL_ARG3_REG
+
+/* Define to register holding arg0 to system calls. */
+#undef SYSCALL_ARG_REG
+
+/* Define to header holding system call numbers. */
+#undef SYSCALL_HEADER
+
+/* Define to register holding the system call number. */
+#undef SYSCALL_NUM_REG
+
+/* Define to register holding value of system calls. */
+#undef SYSCALL_RET_REG
+
+/* Define to header holding USER_REGS_STRUCT. */
+#undef USER_HEADER
+
+/* Define to structure holding user registers. */
+#undef USER_REGS_STRUCT
+
+/* Define to word type used by tracees. */
+#undef USER_WORD
+
+/* Enable extensions on AIX 3, Interix. */
+#ifndef _ALL_SOURCE
+# undef _ALL_SOURCE
+#endif
+/* Enable general extensions on macOS. */
+#ifndef _DARWIN_C_SOURCE
+# undef _DARWIN_C_SOURCE
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+# undef _GNU_SOURCE
+#endif
+/* Enable X/Open compliant socket functions that do not require linking
+ with -lxnet on HP-UX 11.11. */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+# undef _HPUX_ALT_XOPEN_SOCKET_API
+#endif
+/* Identify the host operating system as Minix.
+ This macro does not affect the system headers' behavior.
+ A future release of Autoconf may stop defining this macro. */
+#ifndef _MINIX
+# undef _MINIX
+#endif
+/* Enable general extensions on NetBSD.
+ Enable NetBSD compatibility extensions on Minix. */
+#ifndef _NETBSD_SOURCE
+# undef _NETBSD_SOURCE
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+ Oddly enough, this does nothing on OpenBSD. */
+#ifndef _OPENBSD_SOURCE
+# undef _OPENBSD_SOURCE
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_SOURCE
+# undef _POSIX_SOURCE
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_1_SOURCE
+# undef _POSIX_1_SOURCE
+#endif
+/* Enable POSIX-compatible threading on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+# undef _POSIX_PTHREAD_SEMANTICS
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+# undef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+# undef __STDC_WANT_IEC_60559_BFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+# undef __STDC_WANT_IEC_60559_DFP_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+# undef __STDC_WANT_IEC_60559_TYPES_EXT__
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
+#ifndef __STDC_WANT_LIB_EXT2__
+# undef __STDC_WANT_LIB_EXT2__
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009. */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+# undef __STDC_WANT_MATH_SPEC_FUNCS__
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+# undef _TANDEM_SOURCE
+#endif
+/* Enable X/Open extensions. Define to 500 only if necessary
+ to make mbstate_t available. */
+#ifndef _XOPEN_SOURCE
+# undef _XOPEN_SOURCE
+#endif
+
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+# define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+# undef WORDS_BIGENDIAN
+# endif
+#endif
+
+/* Define for Solaris 2.5.1 so the uint32_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT32_T
+
+/* Define for Solaris 2.5.1 so the uint64_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT64_T
+
+/* Define for Solaris 2.5.1 so the uint8_t typedef from <sys/synch.h>,
+ <pthread.h>, or <semaphore.h> is not used. If the typedef were allowed, the
+ #define below would cause a syntax error. */
+#undef _UINT8_T
+
+/* Define as a signed integer type capable of holding a process identifier. */
+#undef pid_t
+
+/* Define to `unsigned int' if <sys/types.h> does not define. */
+#undef size_t
+
+/* Define to `int' if <sys/types.h> does not define. */
+#undef ssize_t
+
+/* Define to the type of an unsigned integer type of width exactly 16 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint16_t
+
+/* Define to the type of an unsigned integer type of width exactly 32 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint32_t
+
+/* Define to the type of an unsigned integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint64_t
+
+/* Define to the type of an unsigned integer type of width exactly 8 bits if
+ such a type exists and the standard includes do not define it. */
+#undef uint8_t
+
+/* Define to the type of an unsigned integer type wide enough to hold a
+ pointer, if such a type exists, and if the system does not define it. */
+#undef uintptr_t
+
+
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# ifndef HAVE__BOOL
+# ifdef __cplusplus
+typedef bool _Bool;
+# else
+# define _Bool signed char
+# endif
+# endif
+# define bool _Bool
+# define false 0
+# define true 1
+# define __bool_true_false_are_defined 1
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+
diff --git a/exec/config.sub b/exec/config.sub
new file mode 100755
index 00000000000..b41da55df45
--- /dev/null
+++ b/exec/config.sub
@@ -0,0 +1,1890 @@
+#!/usr/bin/sh
+# Configuration validation subroutine script.
+# Copyright 1992-2022 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2022-01-03'
+
+# 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX. However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2022 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."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo "$1"
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Split fields of configuration type
+# shellcheck disable=SC2162
+saved_IFS=$IFS
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+IFS=$saved_IFS
+
+# Separate into logical components for further validation
+case $1 in
+ *-*-*-*-*)
+ echo Invalid configuration \`"$1"\': more than four components >&2
+ exit 1
+ ;;
+ *-*-*-*)
+ basic_machine=$field1-$field2
+ basic_os=$field3-$field4
+ ;;
+ *-*-*)
+ # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+ # parts
+ maybe_os=$field2-$field3
+ case $maybe_os in
+ nto-qnx* | linux-* | uclinux-uclibc* \
+ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
+ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+ | storm-chaos* | os2-emx* | rtmk-nova*)
+ basic_machine=$field1
+ basic_os=$maybe_os
+ ;;
+ android-linux)
+ basic_machine=$field1-unknown
+ basic_os=linux-android
+ ;;
+ *)
+ basic_machine=$field1-$field2
+ basic_os=$field3
+ ;;
+ esac
+ ;;
+ *-*)
+ # A lone config we happen to match not fitting any pattern
+ case $field1-$field2 in
+ decstation-3100)
+ basic_machine=mips-dec
+ basic_os=
+ ;;
+ *-*)
+ # Second component is usually, but not always the OS
+ case $field2 in
+ # Prevent following clause from handling this valid os
+ sun*os*)
+ basic_machine=$field1
+ basic_os=$field2
+ ;;
+ zephyr*)
+ basic_machine=$field1-unknown
+ basic_os=$field2
+ ;;
+ # Manufacturers
+ dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
+ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
+ | unicom* | ibm* | next | hp | isi* | apollo | altos* \
+ | convergent* | ncr* | news | 32* | 3600* | 3100* \
+ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
+ | ultra | tti* | harris | dolphin | highlevel | gould \
+ | cbm | ns | masscomp | apple | axis | knuth | cray \
+ | microblaze* | sim | cisco \
+ | oki | wec | wrs | winbond)
+ basic_machine=$field1-$field2
+ basic_os=
+ ;;
+ *)
+ basic_machine=$field1
+ basic_os=$field2
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ *)
+ # Convert single-component short-hands not valid as part of
+ # multi-component configurations.
+ case $field1 in
+ 386bsd)
+ basic_machine=i386-pc
+ basic_os=bsd
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ basic_os=udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ basic_os=scout
+ ;;
+ alliant)
+ basic_machine=fx80-alliant
+ basic_os=
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ basic_os=
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ basic_os=bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ basic_os=sysv
+ ;;
+ amiga)
+ basic_machine=m68k-unknown
+ basic_os=
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ basic_os=amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ basic_os=sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ basic_os=sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ basic_os=bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ basic_os=aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ basic_os=aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ basic_os=dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ basic_os=linux
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ basic_os=cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ basic_os=bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ basic_os=bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ basic_os=bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ basic_os=bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ basic_os=bsd
+ ;;
+ cray)
+ basic_machine=j90-cray
+ basic_os=unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ basic_os=
+ ;;
+ da30)
+ basic_machine=m68k-da30
+ basic_os=
+ ;;
+ decstation | pmax | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ basic_os=
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ basic_os=sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ basic_os=dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ basic_os=msdosdjgpp
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ basic_os=ebmon
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ basic_os=ose
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ basic_os=sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ basic_os=go32
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ basic_os=hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ basic_os=xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ basic_os=hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ basic_os=sysv3
+ ;;
+ hp300 | hp300hpux)
+ basic_machine=m68k-hp
+ basic_os=hpux
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ basic_os=bsd
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ basic_os=osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ basic_os=proelf
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ basic_os=mach
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ basic_os=sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ basic_os=linux
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ basic_os=sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ basic_os=sysv
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ basic_os=mingw64
+ ;;
+ mingw32)
+ basic_machine=i686-pc
+ basic_os=mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ basic_os=mingw32ce
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ basic_os=coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ basic_os=morphos
+ ;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ basic_os=moxiebox
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ basic_os=msdos
+ ;;
+ msys)
+ basic_machine=i686-pc
+ basic_os=msys
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ basic_os=mvs
+ ;;
+ nacl)
+ basic_machine=le32-unknown
+ basic_os=nacl
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ basic_os=sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-pc
+ basic_os=netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ basic_os=linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ basic_os=newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ basic_os=newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ basic_os=sysv
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ basic_os=cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ basic_os=cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ basic_os=nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ basic_os=mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ basic_os=nonstopux
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ basic_os=os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ basic_os=ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ basic_os=os68k
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ basic_os=osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ basic_os=linux
+ ;;
+ psp)
+ basic_machine=mipsallegrexel-sony
+ basic_os=psp
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ basic_os=pw32
+ ;;
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ basic_os=rdos
+ ;;
+ rdos32)
+ basic_machine=i386-pc
+ basic_os=rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ basic_os=coff
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ basic_os=udi
+ ;;
+ sei)
+ basic_machine=mips-sei
+ basic_os=seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ basic_os=
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ basic_os=sysv2
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ basic_os=
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ basic_os=sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ basic_os=
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ basic_os=sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ basic_os=sunos4
+ ;;
+ sun3)
+ basic_machine=m68k-sun
+ basic_os=
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ basic_os=sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ basic_os=sunos4
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ basic_os=
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ basic_os=sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ basic_os=sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ basic_os=solaris2
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ basic_os=
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ basic_os=unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ basic_os=dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ basic_os=unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ basic_os=unicos
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ basic_os=tops20
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ basic_os=tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ basic_os=udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ basic_os=sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ basic_os=none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ basic_os=sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ basic_os=vms
+ ;;
+ vsta)
+ basic_machine=i386-pc
+ basic_os=vsta
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ basic_os=vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ basic_os=vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ basic_os=vxworks
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ basic_os=mingw32
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ basic_os=unicos
+ ;;
+ *)
+ basic_machine=$1
+ basic_os=
+ ;;
+ esac
+ ;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+ # Here we handle the default manufacturer of certain CPU types. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ cpu=hppa1.1
+ vendor=winbond
+ ;;
+ op50n)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ op60c)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ ibm*)
+ cpu=i370
+ vendor=ibm
+ ;;
+ orion105)
+ cpu=clipper
+ vendor=highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ cpu=m68k
+ vendor=apple
+ ;;
+ pmac | pmac-mpw)
+ cpu=powerpc
+ vendor=apple
+ ;;
+
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ cpu=m68000
+ vendor=att
+ ;;
+ 3b*)
+ cpu=we32k
+ vendor=att
+ ;;
+ bluegene*)
+ cpu=powerpc
+ vendor=ibm
+ basic_os=cnk
+ ;;
+ decsystem10* | dec10*)
+ cpu=pdp10
+ vendor=dec
+ basic_os=tops10
+ ;;
+ decsystem20* | dec20*)
+ cpu=pdp10
+ vendor=dec
+ basic_os=tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ cpu=m68k
+ vendor=motorola
+ ;;
+ dpx2*)
+ cpu=m68k
+ vendor=bull
+ basic_os=sysv3
+ ;;
+ encore | umax | mmax)
+ cpu=ns32k
+ vendor=encore
+ ;;
+ elxsi)
+ cpu=elxsi
+ vendor=elxsi
+ basic_os=${basic_os:-bsd}
+ ;;
+ fx2800)
+ cpu=i860
+ vendor=alliant
+ ;;
+ genix)
+ cpu=ns32k
+ vendor=ns
+ ;;
+ h3050r* | hiux*)
+ cpu=hppa1.1
+ vendor=hitachi
+ basic_os=hiuxwe2
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ cpu=m68000
+ vendor=hp
+ ;;
+ hp9k3[2-9][0-9])
+ cpu=m68k
+ vendor=hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ i*86v32)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ basic_os=sysv32
+ ;;
+ i*86v4*)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ basic_os=sysv4
+ ;;
+ i*86v)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ basic_os=sysv
+ ;;
+ i*86sol2)
+ cpu=`echo "$1" | sed -e 's/86.*/86/'`
+ vendor=pc
+ basic_os=solaris2
+ ;;
+ j90 | j90-cray)
+ cpu=j90
+ vendor=cray
+ basic_os=${basic_os:-unicos}
+ ;;
+ iris | iris4d)
+ cpu=mips
+ vendor=sgi
+ case $basic_os in
+ irix*)
+ ;;
+ *)
+ basic_os=irix4
+ ;;
+ esac
+ ;;
+ miniframe)
+ cpu=m68000
+ vendor=convergent
+ ;;
+ *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ cpu=m68k
+ vendor=atari
+ basic_os=mint
+ ;;
+ news-3600 | risc-news)
+ cpu=mips
+ vendor=sony
+ basic_os=newsos
+ ;;
+ next | m*-next)
+ cpu=m68k
+ vendor=next
+ case $basic_os in
+ openstep*)
+ ;;
+ nextstep*)
+ ;;
+ ns2*)
+ basic_os=nextstep2
+ ;;
+ *)
+ basic_os=nextstep3
+ ;;
+ esac
+ ;;
+ np1)
+ cpu=np1
+ vendor=gould
+ ;;
+ op50n-* | op60c-*)
+ cpu=hppa1.1
+ vendor=oki
+ basic_os=proelf
+ ;;
+ pa-hitachi)
+ cpu=hppa1.1
+ vendor=hitachi
+ basic_os=hiuxwe2
+ ;;
+ pbd)
+ cpu=sparc
+ vendor=tti
+ ;;
+ pbb)
+ cpu=m68k
+ vendor=tti
+ ;;
+ pc532)
+ cpu=ns32k
+ vendor=pc532
+ ;;
+ pn)
+ cpu=pn
+ vendor=gould
+ ;;
+ power)
+ cpu=power
+ vendor=ibm
+ ;;
+ ps2)
+ cpu=i386
+ vendor=ibm
+ ;;
+ rm[46]00)
+ cpu=mips
+ vendor=siemens
+ ;;
+ rtpc | rtpc-*)
+ cpu=romp
+ vendor=ibm
+ ;;
+ sde)
+ cpu=mipsisa32
+ vendor=sde
+ basic_os=${basic_os:-elf}
+ ;;
+ simso-wrs)
+ cpu=sparclite
+ vendor=wrs
+ basic_os=vxworks
+ ;;
+ tower | tower-32)
+ cpu=m68k
+ vendor=ncr
+ ;;
+ vpp*|vx|vx-*)
+ cpu=f301
+ vendor=fujitsu
+ ;;
+ w65)
+ cpu=w65
+ vendor=wdc
+ ;;
+ w89k-*)
+ cpu=hppa1.1
+ vendor=winbond
+ basic_os=proelf
+ ;;
+ none)
+ cpu=none
+ vendor=none
+ ;;
+ leon|leon[3-9])
+ cpu=sparc
+ vendor=$basic_machine
+ ;;
+ leon-*|leon[3-9]-*)
+ cpu=sparc
+ vendor=`echo "$basic_machine" | sed 's/-.*//'`
+ ;;
+
+ *-*)
+ # shellcheck disable=SC2162
+ saved_IFS=$IFS
+ IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+ IFS=$saved_IFS
+ ;;
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ cpu=$basic_machine
+ vendor=pc
+ ;;
+ # These rules are duplicated from below for sake of the special case above;
+ # i.e. things that normalized to x86 arches should also default to "pc"
+ pc98)
+ cpu=i386
+ vendor=pc
+ ;;
+ x64 | amd64)
+ cpu=x86_64
+ vendor=pc
+ ;;
+ # Recognize the basic CPU types without company name.
+ *)
+ cpu=$basic_machine
+ vendor=unknown
+ ;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+ # Here we handle the default manufacturer of certain CPU types in canonical form. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ craynv-unknown)
+ vendor=cray
+ basic_os=${basic_os:-unicosmp}
+ ;;
+ c90-unknown | c90-cray)
+ vendor=cray
+ basic_os=${Basic_os:-unicos}
+ ;;
+ fx80-unknown)
+ vendor=alliant
+ ;;
+ romp-unknown)
+ vendor=ibm
+ ;;
+ mmix-unknown)
+ vendor=knuth
+ ;;
+ microblaze-unknown | microblazeel-unknown)
+ vendor=xilinx
+ ;;
+ rs6000-unknown)
+ vendor=ibm
+ ;;
+ vax-unknown)
+ vendor=dec
+ ;;
+ pdp11-unknown)
+ vendor=dec
+ ;;
+ we32k-unknown)
+ vendor=att
+ ;;
+ cydra-unknown)
+ vendor=cydrome
+ ;;
+ i370-ibm*)
+ vendor=ibm
+ ;;
+ orion-unknown)
+ vendor=highlevel
+ ;;
+ xps-unknown | xps100-unknown)
+ cpu=xps100
+ vendor=honeywell
+ ;;
+
+ # Here we normalize CPU types with a missing or matching vendor
+ armh-unknown | armh-alt)
+ cpu=armv7l
+ vendor=alt
+ basic_os=${basic_os:-linux-gnueabihf}
+ ;;
+ dpx20-unknown | dpx20-bull)
+ cpu=rs6000
+ vendor=bull
+ basic_os=${basic_os:-bosx}
+ ;;
+
+ # Here we normalize CPU types irrespective of the vendor
+ amd64-*)
+ cpu=x86_64
+ ;;
+ blackfin-*)
+ cpu=bfin
+ basic_os=linux
+ ;;
+ c54x-*)
+ cpu=tic54x
+ ;;
+ c55x-*)
+ cpu=tic55x
+ ;;
+ c6x-*)
+ cpu=tic6x
+ ;;
+ e500v[12]-*)
+ cpu=powerpc
+ basic_os=${basic_os}"spe"
+ ;;
+ mips3*-*)
+ cpu=mips64
+ ;;
+ ms1-*)
+ cpu=mt
+ ;;
+ m68knommu-*)
+ cpu=m68k
+ basic_os=linux
+ ;;
+ m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+ cpu=s12z
+ ;;
+ openrisc-*)
+ cpu=or32
+ ;;
+ parisc-*)
+ cpu=hppa
+ basic_os=linux
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ cpu=i586
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+ cpu=i686
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ cpu=i686
+ ;;
+ pentium4-*)
+ cpu=i786
+ ;;
+ pc98-*)
+ cpu=i386
+ ;;
+ ppc-* | ppcbe-*)
+ cpu=powerpc
+ ;;
+ ppcle-* | powerpclittle-*)
+ cpu=powerpcle
+ ;;
+ ppc64-*)
+ cpu=powerpc64
+ ;;
+ ppc64le-* | powerpc64little-*)
+ cpu=powerpc64le
+ ;;
+ sb1-*)
+ cpu=mipsisa64sb1
+ ;;
+ sb1el-*)
+ cpu=mipsisa64sb1el
+ ;;
+ sh5e[lb]-*)
+ cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+ ;;
+ spur-*)
+ cpu=spur
+ ;;
+ strongarm-* | thumb-*)
+ cpu=arm
+ ;;
+ tx39-*)
+ cpu=mipstx39
+ ;;
+ tx39el-*)
+ cpu=mipstx39el
+ ;;
+ x64-*)
+ cpu=x86_64
+ ;;
+ xscale-* | xscalee[bl]-*)
+ cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+ ;;
+ arm64-* | aarch64le-*)
+ cpu=aarch64
+ ;;
+
+ # Recognize the canonical CPU Types that limit and/or modify the
+ # company names they are paired with.
+ cr16-*)
+ basic_os=${basic_os:-elf}
+ ;;
+ crisv32-* | etraxfs*-*)
+ cpu=crisv32
+ vendor=axis
+ ;;
+ cris-* | etrax*-*)
+ cpu=cris
+ vendor=axis
+ ;;
+ crx-*)
+ basic_os=${basic_os:-elf}
+ ;;
+ neo-tandem)
+ cpu=neo
+ vendor=tandem
+ ;;
+ nse-tandem)
+ cpu=nse
+ vendor=tandem
+ ;;
+ nsr-tandem)
+ cpu=nsr
+ vendor=tandem
+ ;;
+ nsv-tandem)
+ cpu=nsv
+ vendor=tandem
+ ;;
+ nsx-tandem)
+ cpu=nsx
+ vendor=tandem
+ ;;
+ mipsallegrexel-sony)
+ cpu=mipsallegrexel
+ vendor=sony
+ ;;
+ tile*-*)
+ basic_os=${basic_os:-linux-gnu}
+ ;;
+
+ *)
+ # Recognize the canonical CPU types that are allowed with any
+ # company name.
+ case $cpu in
+ 1750a | 580 \
+ | a29k \
+ | aarch64 | aarch64_be \
+ | abacus \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
+ | alphapca5[67] | alpha64pca5[67] \
+ | am33_2.0 \
+ | amdgcn \
+ | arc | arceb | arc32 | arc64 \
+ | arm | arm[lb]e | arme[lb] | armv* \
+ | avr | avr32 \
+ | asmjs \
+ | ba \
+ | be32 | be64 \
+ | bfin | bpf | bs2000 \
+ | c[123]* | c30 | [cjt]90 | c4x \
+ | c8051 | clipper | craynv | csky | cydra \
+ | d10v | d30v | dlx | dsp16xx \
+ | e2k | elxsi | epiphany \
+ | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+ | h8300 | h8500 \
+ | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
+ | i370 | i*86 | i860 | i960 | ia16 | ia64 \
+ | ip2k | iq2000 \
+ | k1om \
+ | le32 | le64 \
+ | lm32 \
+ | loongarch32 | loongarch64 | loongarchx32 \
+ | m32c | m32r | m32rle \
+ | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
+ | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
+ | m88110 | m88k | maxq | mb | mcore | mep | metag \
+ | microblaze | microblazeel \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64eb | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r3 | mipsisa32r3el \
+ | mipsisa32r5 | mipsisa32r5el \
+ | mipsisa32r6 | mipsisa32r6el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r3 | mipsisa64r3el \
+ | mipsisa64r5 | mipsisa64r5el \
+ | mipsisa64r6 | mipsisa64r6el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
+ | mipstx39 | mipstx39el \
+ | mmix \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nds32 | nds32le | nds32be \
+ | nfp \
+ | nios | nios2 | nios2eb | nios2el \
+ | none | np1 | ns16k | ns32k | nvptx \
+ | open8 \
+ | or1k* \
+ | or32 \
+ | orion \
+ | picochip \
+ | pdp10 | pdp11 | pj | pjl | pn | power \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
+ | pru \
+ | pyramid \
+ | riscv | riscv32 | riscv32be | riscv64 | riscv64be \
+ | rl78 | romp | rs6000 | rx \
+ | s390 | s390x \
+ | score \
+ | sh | shl \
+ | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
+ | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
+ | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
+ | spu \
+ | tahoe \
+ | thumbv7* \
+ | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
+ | tron \
+ | ubicom32 \
+ | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
+ | vax \
+ | visium \
+ | w65 \
+ | wasm32 | wasm64 \
+ | we32k \
+ | x86 | x86_64 | xc16x | xgate | xps100 \
+ | xstormy16 | xtensa* \
+ | ymp \
+ | z8k | z80)
+ ;;
+
+ *)
+ echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
+ exit 1
+ ;;
+ esac
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+ digital*)
+ vendor=dec
+ ;;
+ commodore*)
+ vendor=cbm
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if test x$basic_os != x
+then
+
+# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just
+# set os.
+case $basic_os in
+ gnu/linux*)
+ kernel=linux
+ os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
+ ;;
+ os2-emx)
+ kernel=os2
+ os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
+ ;;
+ nto-qnx*)
+ kernel=nto
+ os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
+ ;;
+ *-*)
+ # shellcheck disable=SC2162
+ saved_IFS=$IFS
+ IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+ IFS=$saved_IFS
+ ;;
+ # Default OS when just kernel was specified
+ nto*)
+ kernel=nto
+ os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
+ ;;
+ linux*)
+ kernel=linux
+ os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
+ ;;
+ *)
+ kernel=
+ os=$basic_os
+ ;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
+case $os in
+ # First match some system type aliases that might get confused
+ # with valid system types.
+ # solaris* is a basic system type, with this one exception.
+ auroraux)
+ os=auroraux
+ ;;
+ bluegene*)
+ os=cnk
+ ;;
+ solaris1 | solaris1.*)
+ os=`echo "$os" | sed -e 's|solaris1|sunos4|'`
+ ;;
+ solaris)
+ os=solaris2
+ ;;
+ unixware*)
+ os=sysv4.2uw
+ ;;
+ # es1800 is here to avoid being matched by es* (a different OS)
+ es1800*)
+ os=ose
+ ;;
+ # Some version numbers need modification
+ chorusos*)
+ os=chorusos
+ ;;
+ isc)
+ os=isc2.2
+ ;;
+ sco6)
+ os=sco5v6
+ ;;
+ sco5)
+ os=sco3.2v5
+ ;;
+ sco4)
+ os=sco3.2v4
+ ;;
+ sco3.2.[4-9]*)
+ os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'`
+ ;;
+ sco*v* | scout)
+ # Don't match below
+ ;;
+ sco*)
+ os=sco3.2v2
+ ;;
+ psos*)
+ os=psos
+ ;;
+ qnx*)
+ os=qnx
+ ;;
+ hiux*)
+ os=hiuxwe2
+ ;;
+ lynx*178)
+ os=lynxos178
+ ;;
+ lynx*5)
+ os=lynxos5
+ ;;
+ lynxos*)
+ # don't get caught up in next wildcard
+ ;;
+ lynx*)
+ os=lynxos
+ ;;
+ mac[0-9]*)
+ os=`echo "$os" | sed -e 's|mac|macos|'`
+ ;;
+ opened*)
+ os=openedition
+ ;;
+ os400*)
+ os=os400
+ ;;
+ sunos5*)
+ os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+ ;;
+ sunos6*)
+ os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+ ;;
+ wince*)
+ os=wince
+ ;;
+ utek*)
+ os=bsd
+ ;;
+ dynix*)
+ os=bsd
+ ;;
+ acis*)
+ os=aos
+ ;;
+ atheos*)
+ os=atheos
+ ;;
+ syllable*)
+ os=syllable
+ ;;
+ 386bsd)
+ os=bsd
+ ;;
+ ctix* | uts*)
+ os=sysv
+ ;;
+ nova*)
+ os=rtmk-nova
+ ;;
+ ns2)
+ os=nextstep2
+ ;;
+ # Preserve the version number of sinix5.
+ sinix5.*)
+ os=`echo "$os" | sed -e 's|sinix|sysv|'`
+ ;;
+ sinix*)
+ os=sysv4
+ ;;
+ tpf*)
+ os=tpf
+ ;;
+ triton*)
+ os=sysv3
+ ;;
+ oss*)
+ os=sysv3
+ ;;
+ svr4*)
+ os=sysv4
+ ;;
+ svr3)
+ os=sysv3
+ ;;
+ sysvr4)
+ os=sysv4
+ ;;
+ ose*)
+ os=ose
+ ;;
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ os=mint
+ ;;
+ dicos*)
+ os=dicos
+ ;;
+ pikeos*)
+ # Until real need of OS specific support for
+ # particular features comes up, bare metal
+ # configurations are quite functional.
+ case $cpu in
+ arm*)
+ os=eabi
+ ;;
+ *)
+ os=elf
+ ;;
+ esac
+ ;;
+ *)
+ # No normalization, but not necessarily accepted, that comes below.
+ ;;
+esac
+
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+kernel=
+case $cpu-$vendor in
+ score-*)
+ os=elf
+ ;;
+ spu-*)
+ os=elf
+ ;;
+ *-acorn)
+ os=riscix1.2
+ ;;
+ arm*-rebel)
+ kernel=linux
+ os=gnu
+ ;;
+ arm*-semi)
+ os=aout
+ ;;
+ c4x-* | tic4x-*)
+ os=coff
+ ;;
+ c8051-*)
+ os=elf
+ ;;
+ clipper-intergraph)
+ os=clix
+ ;;
+ hexagon-*)
+ os=elf
+ ;;
+ tic54x-*)
+ os=coff
+ ;;
+ tic55x-*)
+ os=coff
+ ;;
+ tic6x-*)
+ os=coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=tops20
+ ;;
+ pdp11-*)
+ os=none
+ ;;
+ *-dec | vax-*)
+ os=ultrix4.2
+ ;;
+ m68*-apollo)
+ os=domain
+ ;;
+ i386-sun)
+ os=sunos4.0.2
+ ;;
+ m68000-sun)
+ os=sunos3
+ ;;
+ m68*-cisco)
+ os=aout
+ ;;
+ mep-*)
+ os=elf
+ ;;
+ mips*-cisco)
+ os=elf
+ ;;
+ mips*-*)
+ os=elf
+ ;;
+ or32-*)
+ os=coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=sysv3
+ ;;
+ sparc-* | *-sun)
+ os=sunos4.1.1
+ ;;
+ pru-*)
+ os=elf
+ ;;
+ *-be)
+ os=beos
+ ;;
+ *-ibm)
+ os=aix
+ ;;
+ *-knuth)
+ os=mmixware
+ ;;
+ *-wec)
+ os=proelf
+ ;;
+ *-winbond)
+ os=proelf
+ ;;
+ *-oki)
+ os=proelf
+ ;;
+ *-hp)
+ os=hpux
+ ;;
+ *-hitachi)
+ os=hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=sysv
+ ;;
+ *-cbm)
+ os=amigaos
+ ;;
+ *-dg)
+ os=dgux
+ ;;
+ *-dolphin)
+ os=sysv3
+ ;;
+ m68k-ccur)
+ os=rtu
+ ;;
+ m88k-omron*)
+ os=luna
+ ;;
+ *-next)
+ os=nextstep
+ ;;
+ *-sequent)
+ os=ptx
+ ;;
+ *-crds)
+ os=unos
+ ;;
+ *-ns)
+ os=genix
+ ;;
+ i370-*)
+ os=mvs
+ ;;
+ *-gould)
+ os=sysv
+ ;;
+ *-highlevel)
+ os=bsd
+ ;;
+ *-encore)
+ os=bsd
+ ;;
+ *-sgi)
+ os=irix
+ ;;
+ *-siemens)
+ os=sysv4
+ ;;
+ *-masscomp)
+ os=rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=uxpv
+ ;;
+ *-rom68k)
+ os=coff
+ ;;
+ *-*bug)
+ os=coff
+ ;;
+ *-apple)
+ os=macos
+ ;;
+ *-atari*)
+ os=mint
+ ;;
+ *-wrs)
+ os=vxworks
+ ;;
+ *)
+ os=none
+ ;;
+esac
+
+fi
+
+# Now, validate our (potentially fixed-up) OS.
+case $os in
+ # Sometimes we do "kernel-libc", so those need to count as OSes.
+ musl* | newlib* | relibc* | uclibc*)
+ ;;
+ # Likewise for "kernel-abi"
+ eabi* | gnueabi*)
+ ;;
+ # VxWorks passes extra cpu info in the 4th filed.
+ simlinux | simwindows | spe)
+ ;;
+ # Now accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST end in a * to match a version number.
+ gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+ | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \
+ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \
+ | hiux* | abug | nacl* | netware* | windows* \
+ | os9* | macos* | osx* | ios* \
+ | mpw* | magic* | mmixware* | mon960* | lnews* \
+ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+ | aos* | aros* | cloudabi* | sortix* | twizzler* \
+ | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+ | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+ | mirbsd* | netbsd* | dicos* | openedition* | ose* \
+ | bitrig* | openbsd* | secbsd* | solidbsd* | libertybsd* | os108* \
+ | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
+ | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+ | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+ | udi* | lites* | ieee* | go32* | aux* | hcos* \
+ | chorusrdb* | cegcc* | glidix* | serenity* \
+ | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+ | midipix* | mingw32* | mingw64* | mint* \
+ | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+ | interix* | uwin* | mks* | rhapsody* | darwin* \
+ | openstep* | oskit* | conix* | pw32* | nonstopux* \
+ | storm-chaos* | tops10* | tenex* | tops20* | its* \
+ | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \
+ | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \
+ | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+ | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \
+ | fiwix* )
+ ;;
+ # This one is extra strict with allowed versions
+ sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ ;;
+ none)
+ ;;
+ *)
+ echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os in
+ linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* \
+ | linux-musl* | linux-relibc* | linux-uclibc* )
+ ;;
+ uclinux-uclibc* )
+ ;;
+ -dietlibc* | -newlib* | -musl* | -relibc* | -uclibc* )
+ # These are just libc implementations, not actual OSes, and thus
+ # require a kernel.
+ echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2
+ exit 1
+ ;;
+ kfreebsd*-gnu* | kopensolaris*-gnu*)
+ ;;
+ vxworks-simlinux | vxworks-simwindows | vxworks-spe)
+ ;;
+ nto-qnx*)
+ ;;
+ os2-emx)
+ ;;
+ *-eabi* | *-gnueabi*)
+ ;;
+ -*)
+ # Blank kernel with real OS is always fine.
+ ;;
+ *-*)
+ echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+case $vendor in
+ unknown)
+ case $cpu-$os in
+ *-riscix*)
+ vendor=acorn
+ ;;
+ *-sunos*)
+ vendor=sun
+ ;;
+ *-cnk* | *-aix*)
+ vendor=ibm
+ ;;
+ *-beos*)
+ vendor=be
+ ;;
+ *-hpux*)
+ vendor=hp
+ ;;
+ *-mpeix*)
+ vendor=hp
+ ;;
+ *-hiux*)
+ vendor=hitachi
+ ;;
+ *-unos*)
+ vendor=crds
+ ;;
+ *-dgux*)
+ vendor=dg
+ ;;
+ *-luna*)
+ vendor=omron
+ ;;
+ *-genix*)
+ vendor=ns
+ ;;
+ *-clix*)
+ vendor=intergraph
+ ;;
+ *-mvs* | *-opened*)
+ vendor=ibm
+ ;;
+ *-os400*)
+ vendor=ibm
+ ;;
+ s390-* | s390x-*)
+ vendor=ibm
+ ;;
+ *-ptx*)
+ vendor=sequent
+ ;;
+ *-tpf*)
+ vendor=ibm
+ ;;
+ *-vxsim* | *-vxworks* | *-windiss*)
+ vendor=wrs
+ ;;
+ *-aux*)
+ vendor=apple
+ ;;
+ *-hms*)
+ vendor=hitachi
+ ;;
+ *-mpw* | *-macos*)
+ vendor=apple
+ ;;
+ *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
+ vendor=atari
+ ;;
+ *-vos*)
+ vendor=stratus
+ ;;
+ esac
+ ;;
+esac
+
+echo "$cpu-$vendor-${kernel:+$kernel-}$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/exec/configure.ac b/exec/configure.ac
new file mode 100644
index 00000000000..e78d8ebea90
--- /dev/null
+++ b/exec/configure.ac
@@ -0,0 +1,537 @@
+dnl Autoconf script for GNU Emacs's exec library.
+dnl To rebuild the 'configure' script from this, execute the command
+dnl autoconf
+dnl in the directory containing this script.
+dnl If you changed any AC_DEFINES, also run autoheader.
+dnl
+dnl Copyright (C) 2023 Free Software Foundation, Inc.
+dnl
+dnl This file is part of GNU Emacs.
+dnl
+dnl GNU Emacs is free software: you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation, either version 3 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl GNU Emacs is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+AC_PREREQ([2.65])
+AC_INIT([libexec], [30.0.50], [bug-gnu-emacs@gnu.org], [],
+ [https://www.gnu.org/software/emacs/])
+
+AH_TOP([/* 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/>. */])
+
+AC_ARG_WITH([reentrancy],
+ [AS_HELP_STRING([--with-reentrancy],
+ [Generate library which can be used within a signal handler.])],
+ [AC_DEFINE([REENTRANT], [1])])
+
+AC_USE_SYSTEM_EXTENSIONS
+AC_PROG_CC
+AC_PROG_CPP
+AC_PROG_INSTALL
+
+AC_TYPE_UINT8_T
+AC_TYPE_UINT16_T
+AC_TYPE_UINT32_T
+AC_TYPE_UINT64_T
+AC_TYPE_UINTPTR_T
+AC_TYPE_SIZE_T
+AC_TYPE_SSIZE_T
+AC_TYPE_PID_T
+
+AC_HEADER_STDBOOL
+AC_CHECK_FUNCS([getpagesize stpcpy stpncpy])
+AC_CHECK_DECLS([stpcpy, stpncpy])
+AC_CHECK_FUNC([process_vm_readv],
+ [AC_CHECK_FUNC([process_vm_writev],
+ [AC_CHECK_DECL([process_vm_readv],
+ [AC_DEFINE([HAVE_PROCESS_VM], [1],
+ [Define to 1 if process_vm_readv is available.])],
+ [], [[
+#include <sys/uio.h>
+ ]])])])
+AC_CHECK_HEADERS([sys/param.h sys/uio.h])
+AC_CHECK_MEMBERS([siginfo_t.si_syscall], [], [],
+ [[
+#include <signal.h>
+ ]])
+
+AH_BOTTOM([
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# ifndef HAVE__BOOL
+# ifdef __cplusplus
+typedef bool _Bool;
+# else
+# define _Bool signed char
+# endif
+# endif
+# define bool _Bool
+# define false 0
+# define true 1
+# define __bool_true_false_are_defined 1
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif /* HAVE_SYS_PARAM_H */
+
+#ifndef MAX
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+#endif /* MAX */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif /* MIN */
+])
+
+AC_C_BIGENDIAN
+
+AH_TEMPLATE([SYSCALL_HEADER], [Define to header holding system call numbers.])
+AH_TEMPLATE([USER_HEADER], [Define to header holding USER_REGS_STRUCT.])
+AH_TEMPLATE([USER_REGS_STRUCT], [Define to structure holding user registers.])
+AH_TEMPLATE([SYSCALL_NUM_REG], [Define to register holding the system call number.])
+AH_TEMPLATE([SYSCALL_ARG_REG], [Define to register holding arg0 to system calls.])
+AH_TEMPLATE([SYSCALL_ARG1_REG], [Define to register holding arg1 to system calls.])
+AH_TEMPLATE([SYSCALL_ARG2_REG], [Define to register holding arg2 to system calls.])
+AH_TEMPLATE([SYSCALL_ARG3_REG], [Define to register holding arg3 to system calls.])
+AH_TEMPLATE([SYSCALL_RET_REG], [Define to register holding value of system calls.])
+AH_TEMPLATE([STACK_POINTER], [Define to register holding the stack pointer.])
+AH_TEMPLATE([EXEC_SYSCALL], [Define to number of the `exec' system call.])
+AH_TEMPLATE([USER_WORD], [Define to word type used by tracees.])
+AH_TEMPLATE([EXEC_64], [Define to 1 if the system utilizes 64-bit ELF.])
+AH_TEMPLATE([STACK_GROWS_DOWNWARDS], [Define to 1 if the stack grows downwards.])
+AH_TEMPLATE([ABI_RED_ZONE], [Define to number of reserved bytes past the stack frame.])
+AH_TEMPLATE([EXECUTABLE_BASE], [Virtual address for loading PIC executables])
+AH_TEMPLATE([INTERPRETER_BASE], [Virtual address for loading PIC interpreters])
+AH_TEMPLATE([CLONE_SYSCALL], [Define to number of the `clone' system call.])
+AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.])
+AH_TEMPLATE([READLINK_SYSCALL], [Define to number of the `readlink' system call.])
+AH_TEMPLATE([READLINKAT_SYSCALL], [Define to number of the `readlinkat' system call.])
+AH_TEMPLATE([REENTRANT], [Define to 1 if the library is used within a signal handler.])
+
+AC_CANONICAL_HOST
+
+# Check whether or not sys/user exists. If it doesn't, try
+# asm/user.h, and croak if that doesn't exist either.
+AS_CASE([$host], [*mips*], [], [*],
+ [AC_CHECK_HEADER([sys/user.h], [user_h="<sys/user.h>"],
+ [AC_CHECK_HEADER([asm/user.h], [user_h="<asm/user.h>"],
+ [AC_MSG_ERROR([Can not find working user.h])])])])
+
+# Look for required tools.
+
+AC_ARG_VAR([M4], [`m4' preprocessor command.])
+AC_ARG_VAR([AS], [`as' assembler command.])
+AC_ARG_VAR([LD], [`ld' linker command.])
+
+# Check for a working m4.
+AC_CHECK_PROGS([M4], [gm4 m4],
+ [AC_MSG_ERROR([Cannot find m4])])
+
+# Check for a working assembler.
+AC_CHECK_TOOL([AS], [as],
+ [AC_MSG_ERROR([Cannot find a working assembler])])
+
+# And ar.
+AC_CHECK_TOOL([AR], [ar],
+ [AC_MSG_ERROR([Cannot find a working ar])])
+
+# And ld.
+AC_CHECK_TOOL([LD], [ld],
+ [AC_MSG_ERROR([Cannot find a working linker])])
+
+# Now check if ld is a C compiler.
+LDPREFIX=
+AC_CACHE_CHECK([whether ld is a C compiler],
+ [exec_cv_ld_is_cc],
+ [cat <<_ACEOF > conftest.c
+AC_LANG_PROGRAM(,)
+_ACEOF
+ exec_cv_ld_is_cc=yes
+ $LD -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \
+ || exec_cv_ld_is_cc=no
+ rm -f conftest.c conftest.$OBJEXT])
+
+# And if as is a C compiler.
+AC_CACHE_CHECK([whether as is a C compiler],
+ [exec_cv_as_is_cc],
+ [cat <<_ACEOF > conftest.c
+AC_LANG_PROGRAM(,)
+_ACEOF
+ exec_cv_as_is_cc=yes
+ $AS -c conftest.c -o conftest.$OBJEXT >&AS_MESSAGE_LOG_FD 2>&1 \
+ || exec_cv_as_is_cc=no
+ rm -f conftest.c conftest.$OBJEXT])
+
+# If ld is a C compiler, pass `-nostdlib', `-nostartfiles', and
+# `-static'. Also, set LDPREFIX to -Wl,
+AS_IF([test "x$exec_cv_ld_is_cc" = "xyes"],
+ [LOADERFLAGS="$LOADERFLAGS -nostdlib -nostartfiles -static"
+ LDPREFIX=-Wl,])
+
+# If as is a C compiler, add `-c' to ASFLAGS.
+AS_IF([test "x$exec_cv_as_is_cc" = "xyes"],
+ [ASFLAGS="$ASFLAGS -c"])
+
+AC_DEFUN([exec_CHECK_LINUX_CLONE3],
+[
+AC_CHECK_DECL([__NR_clone3],
+ [AC_DEFINE([CLONE3_SYSCALL], [__NR_clone3])],
+ [], [[
+#include <asm/unistd.h>
+]])
+])
+
+AC_DEFUN([exec_CHECK_MIPS_NABI],
+[
+AC_CACHE_CHECK([whether MIPS NABI calling convention is used],
+ [exec_cv_mips_nabi],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#include <sgidefs.h>
+]], [[
+#ifndef __mips64__
+#if _MIPS_SIM == _ABIO32
+OABI in use.
+#endif /* _MIPS_SIM == _ABIO32 */
+#endif /* !__mips64__ */
+]])], [exec_cv_mips_nabi=yes],
+ [exec_cv_mips_nabi=no])])
+
+dnl mips64 systems use N64 calling convention, a variant of nabi
+dnl calling convention.
+AS_IF([test "x$exec_cv_mips_nabi" != "xno"],
+ [AC_DEFINE([MIPS_NABI], [1],
+ [Define to 1 if MIPS NABI calling convention is being used.])],
+ [OBJS="$OBJS mipsfpu.o"])
+])
+
+# Determine the system type and define appropriate macros.
+exec_loader=
+is_mips=
+OBJS="exec.o trace.o"
+DADDI_BROKEN=no
+
+AS_CASE([$host], [x86_64-*linux*],
+ [AC_CHECK_MEMBER([struct user_regs_struct.rdi],
+ [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+ AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
+ AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
+ AC_DEFINE([SYSCALL_NUM_REG], [orig_rax])
+ AC_DEFINE([SYSCALL_RET_REG], [rax])
+ AC_DEFINE([SYSCALL_ARG_REG], [rdi])
+ AC_DEFINE([SYSCALL_ARG1_REG], [rsi])
+ AC_DEFINE([SYSCALL_ARG2_REG], [rdx])
+ AC_DEFINE([SYSCALL_ARG3_REG], [r10])
+ AC_DEFINE([STACK_POINTER], [rsp])
+ AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+ AC_DEFINE([USER_WORD], [uintptr_t])
+ AC_DEFINE([EXEC_64], [1])
+ AC_DEFINE([ABI_RED_ZONE], [128])
+ AC_DEFINE([EXECUTABLE_BASE], [0x555555554000])
+ AC_DEFINE([INTERPRETER_BASE], [0x600000000000])
+ AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+ AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+ exec_CHECK_LINUX_CLONE3
+ # Make sure the loader doesn't conflict with other position
+ # dependent code.
+ LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x200000000000"
+ exec_loader=loader-x86_64.s],
+ [AC_MSG_ERROR([Missing `rdi' in user_regs_struct])],
+ [[
+#include $user_h
+ ]])], [i[[34567]]86-*linux*],
+ [AC_CHECK_MEMBER([struct user_regs_struct.edi],
+ [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+ AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
+ AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
+ AC_DEFINE([SYSCALL_NUM_REG], [orig_eax])
+ AC_DEFINE([SYSCALL_RET_REG], [eax])
+ AC_DEFINE([SYSCALL_ARG_REG], [ebx])
+ AC_DEFINE([SYSCALL_ARG1_REG], [ecx])
+ AC_DEFINE([SYSCALL_ARG2_REG], [edx])
+ AC_DEFINE([SYSCALL_ARG3_REG], [esi])
+ AC_DEFINE([STACK_POINTER], [esp])
+ AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+ AC_DEFINE([USER_WORD], [uintptr_t])
+ AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
+ AC_DEFINE([INTERPRETER_BASE], [0xaf000000])
+ AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+ AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+ exec_CHECK_LINUX_CLONE3
+ # Make sure the loader doesn't conflict with other position
+ # dependent code.
+ LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0xa0000000"
+ exec_loader=loader-x86.s],
+ [AC_MSG_ERROR([Missing `edi' in user_regs_struct])],
+ [[
+#include $user_h
+ ]])], [aarch64-*linux*],
+ [AC_CHECK_MEMBER([struct user_regs_struct.sp],
+ [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+ AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
+ AC_DEFINE([USER_REGS_STRUCT], [struct user_regs_struct])
+ AC_DEFINE([SYSCALL_NUM_REG], [[regs[8]]])
+ AC_DEFINE([SYSCALL_RET_REG], [[regs[0]]])
+ AC_DEFINE([SYSCALL_ARG_REG], [[regs[0]]])
+ AC_DEFINE([SYSCALL_ARG1_REG], [[regs[1]]])
+ AC_DEFINE([SYSCALL_ARG2_REG], [[regs[2]]])
+ AC_DEFINE([SYSCALL_ARG3_REG], [[regs[3]]])
+ AC_DEFINE([STACK_POINTER], [sp])
+ AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+ AC_DEFINE([USER_WORD], [uintptr_t])
+ AC_DEFINE([EXEC_64], [1])
+ AC_DEFINE([EXECUTABLE_BASE], [0x3000000000])
+ AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
+ AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+ AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ # Note that aarch64 has no `readlink'.
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+ exec_CHECK_LINUX_CLONE3
+ # Make sure the loader doesn't conflict with other position
+ # dependent code. ARM places rather significant restrictions on
+ # virtual addresses for a 64 bit architecture.
+ LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x2000000000"
+ exec_loader=loader-aarch64.s],
+ [AC_MSG_ERROR([Missing `sp' in user_regs_struct])],
+ [[
+#include $user_h
+ ]])], [arm*linux*eabi* | armv7*linux*],
+ [AC_CHECK_MEMBER([struct user_regs.uregs],
+ [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+ AC_DEFINE_UNQUOTED([USER_HEADER], [$user_h])
+ AC_DEFINE([USER_REGS_STRUCT], [struct user_regs])
+ AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]])
+ AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]])
+ AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]])
+ AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]])
+ AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]])
+ AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]])
+ AC_DEFINE([STACK_POINTER], [[uregs[13]]])
+ AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+ AC_DEFINE([USER_WORD], [uintptr_t])
+ AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
+ AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
+ AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+ AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+ exec_CHECK_LINUX_CLONE3
+ LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
+ exec_loader=loader-armeabi.s],
+ [AC_CHECK_MEMBER([struct pt_regs.uregs],
+ [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+ AC_DEFINE_UNQUOTED([USER_HEADER], [<asm/ptrace.h>])
+ AC_DEFINE([USER_REGS_STRUCT], [struct pt_regs])
+ AC_DEFINE([SYSCALL_NUM_REG], [[uregs[7]]])
+ AC_DEFINE([SYSCALL_RET_REG], [[uregs[0]]])
+ AC_DEFINE([SYSCALL_ARG_REG], [[uregs[0]]])
+ AC_DEFINE([SYSCALL_ARG1_REG], [[uregs[1]]])
+ AC_DEFINE([SYSCALL_ARG2_REG], [[uregs[2]]])
+ AC_DEFINE([SYSCALL_ARG3_REG], [[uregs[3]]])
+ AC_DEFINE([STACK_POINTER], [[uregs[13]]])
+ AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+ AC_DEFINE([USER_WORD], [uintptr_t])
+ AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
+ AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
+ AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+ AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+ exec_CHECK_LINUX_CLONE3
+ LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
+ exec_loader=loader-armeabi.s],
+ [AC_MSG_ERROR([Missing `uregs' in user_regs_struct or pt_regs])],
+ [[
+#include <asm/ptrace.h>
+ ]])],
+ [[
+#include $user_h
+ ]])], [mipsel*linux*],
+ [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+ AC_DEFINE([USER_HEADER], ["mipsel-user.h"])
+ AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs])
+ AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0
+ AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0
+ AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0
+ AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1
+ AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2
+ AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3
+ AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp
+ AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+ AC_DEFINE([USER_WORD], [uintptr_t])
+ AC_DEFINE([EXECUTABLE_BASE], [0x0f000000])
+ AC_DEFINE([INTERPRETER_BASE], [0x1f000000])
+ AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+ AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+ AC_CHECK_DECL([_MIPS_SIM], [exec_CHECK_MIPS_NABI],
+ [AC_MSG_ERROR([_MIPS_SIM could not be determined]),
+ [[
+#include <sgidefs.h>
+]]])
+ exec_CHECK_LINUX_CLONE3
+ LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000"
+ is_mips=yes
+ exec_loader=loader-mipsel.s], [mips64el*linux*],
+ [AC_DEFINE([SYSCALL_HEADER], [<asm/unistd.h>])
+ AC_DEFINE([USER_HEADER], ["mipsel-user.h"])
+ AC_DEFINE([USER_REGS_STRUCT], [struct mipsel_regs])
+ AC_DEFINE([SYSCALL_NUM_REG], [[gregs[2]]]) # v0
+ AC_DEFINE([SYSCALL_RET_REG], [[gregs[4]]]) # a0
+ AC_DEFINE([SYSCALL_ARG_REG], [[gregs[4]]]) # a0
+ AC_DEFINE([SYSCALL_ARG1_REG], [[gregs[5]]]) # a1
+ AC_DEFINE([SYSCALL_ARG2_REG], [[gregs[4]]]) # a2
+ AC_DEFINE([SYSCALL_ARG3_REG], [[gregs[5]]]) # a3
+ AC_DEFINE([STACK_POINTER], [[gregs[29]]]) # sp
+ AC_DEFINE([EXEC_SYSCALL], [__NR_execve])
+ AC_DEFINE([USER_WORD], [uintptr_t])
+ AC_DEFINE([EXEC_64], [1])
+ AC_DEFINE([EXECUTABLE_BASE], [0x400000])
+ AC_DEFINE([INTERPRETER_BASE], [0x3f00000000])
+ AC_DEFINE([STACK_GROWS_DOWNWARDS], [1])
+ AC_DEFINE([CLONE_SYSCALL], [__NR_clone])
+ AC_DEFINE([READLINK_SYSCALL], [__NR_readlink])
+ AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat])
+ AC_CACHE_CHECK([whether as understands `daddi'],
+ [exec_cv_as_daddi],
+ [exec_cv_as_daddi=no
+ cat <<_ACEOF >conftest.s
+ .section text
+ .global __start
+__start:
+ li $t0, 0
+ li $t1, 0
+ daddi $t0, $t1, 1
+ daddi $t0, $t1, -1
+ daddi $t0, -1
+ daddi $t0, 1
+
+_ACEOF
+ $AS $ASFLAGS conftest.s -o conftest.$OBJEXT \
+ >&AS_MESSAGE_LOG_FD 2>&1 \
+ && exec_cv_as_daddi=yes
+ rm -f conftest.s conftest.$OBJEXT])
+ AS_IF([test "x$exec_cv_as_daddi" != "xyes"],
+ [DADDI_BROKEN=yes])
+ exec_CHECK_LINUX_CLONE3
+ exec_CHECK_MIPS_NABI
+ LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x3e00000000"
+ is_mips=yes
+ exec_loader=loader-mips64el.s], [*],
+ [AC_MSG_ERROR([Please port libexec to $host])])
+
+AC_SUBST([DADDI_BROKEN])
+
+MIPS_N32=$exec_cv_mips_nabi
+
+AC_ARG_VAR([LOADERFLAGS], [Flags used to link the loader.])
+AC_ARG_VAR([ARFLAGS], [Flags for the archiver.])
+AC_ARG_VAR([ASFLAGS], [Flags for the assembler.])
+
+# Make the assembler optimize for code size. Don't do this on MIPS,
+# as the assembler code manages branch delays manually.
+
+AC_CACHE_CHECK([whether as understands -O],
+ [exec_cv_as_O],
+ [exec_cv_as_O=no
+ cat <<_ACEOF >conftest.s
+ .section text
+ .global _start
+_start:
+
+_ACEOF
+ $AS $ASFLAGS -O conftest.s -o conftest.$OBJEXT \
+ >&AS_MESSAGE_LOG_FD 2>&1 \
+ && exec_cv_as_O=yes
+ rm -f conftest.s conftest.$OBJEXT])
+
+AS_IF([test "$exec_cv_as_O" = "yes" \
+ && test "$is_mips" != "yes"],
+ [ASFLAGS="$ASFLAGS -O"])
+
+# Make the assembler generate debug information.
+
+AC_CACHE_CHECK([whether as understands -g],
+ [exec_cv_as_g],
+ [exec_cv_as_g=no
+ cat <<_ACEOF >conftest.s
+ .section text
+ .global _start
+_start:
+
+_ACEOF
+ $AS $ASFLAGS -g conftest.s -o conftest.$OBJEXT \
+ >&AS_MESSAGE_LOG_FD 2>&1 \
+ && exec_cv_as_g=yes
+ rm -f conftest.s conftest.$OBJEXT])
+AS_IF([test "$exec_cv_as_g" = "yes"], [ASFLAGS="$ASFLAGS -g"])
+
+# Check for the ability to automatically generate dependencies for C
+# source files.
+AUTO_DEPEND=no
+AS_IF([test "x$GCC" = xyes],
+ [AC_CACHE_CHECK([whether gcc understands -MMD -MF],
+ [exec_cv_autodepend],
+ [SAVE_CFLAGS="$CFLAGS"
+ CFLAGS="$CFLAGS -MMD -MF deps.d -MP"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[]])],
+ [exec_cv_autodepend=yes],
+ [exec_cv_autodepend=no])
+ CFLAGS="$SAVE_CFLAGS"
+ test -f deps.d || emacs_cv_autodepend=no
+ rm -rf deps.d])
+ AS_IF([test "x$exec_cv_autodepend" = xyes],
+ [AUTO_DEPEND=yes
+ AS_MKDIR_P([deps])])])
+
+# Now check for some other stuff.
+
+AC_CACHE_CHECK([for 'find' args to delete a file],
+ [exec_cv_find_delete],
+ [AS_IF([touch conftest.tmp && find conftest.tmp -delete 2>/dev/null &&
+ test ! -f conftest.tmp], [exec_cv_find_delete="-delete"],
+ [exec_cv_find_delete="-exec rm -f {} ';'"])])
+FIND_DELETE=$exec_cv_find_delete
+AC_SUBST([FIND_DELETE])
+
+AC_CONFIG_HEADERS([config.h])
+AC_CONFIG_FILES([Makefile config-mips.m4])
+
+AC_SUBST([AUTO_DEPEND])
+AC_SUBST([LOADERFLAGS])
+AC_SUBST([ARFLAGS])
+AC_SUBST([ASFLAGS])
+AC_SUBST([exec_loader])
+AC_SUBST([MIPS_N32])
+AC_SUBST([OBJS])
+
+AC_OUTPUT
diff --git a/exec/deps.mk b/exec/deps.mk
new file mode 100644
index 00000000000..20fcd2dbc5a
--- /dev/null
+++ b/exec/deps.mk
@@ -0,0 +1,21 @@
+### deps.mk
+
+## 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/>.
+
+exec.o: exec.h config.h
+trace.o: exec.h config.h
diff --git a/exec/exec.c b/exec/exec.c
new file mode 100644
index 00000000000..a15386b51bb
--- /dev/null
+++ b/exec/exec.c
@@ -0,0 +1,1202 @@
+/* Program execution for Emacs.
+
+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 <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <sys/ptrace.h>
+#include <sys/param.h>
+#include <sys/mman.h>
+
+#include "exec.h"
+
+#if defined __mips__ && !defined MIPS_NABI
+#include "mipsfpu.h"
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+
+
+
+/* Define replacements for required string functions. */
+
+#if !defined HAVE_STPCPY || !defined HAVE_DECL_STPCPY
+
+/* Copy SRC to DEST, returning the address of the terminating '\0' in
+ DEST. */
+
+static char *
+rpl_stpcpy (char *dest, const char *src)
+{
+ register char *d;
+ register const char *s;
+
+ d = dest;
+ s = src;
+
+ do
+ *d++ = *s;
+ while (*s++ != '\0');
+
+ return d - 1;
+}
+
+#define stpcpy rpl_stpcpy
+#endif /* !defined HAVE_STPCPY || !defined HAVE_DECL_STPCPY */
+
+#if !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY
+
+/* Copy no more than N bytes of SRC to DST, returning a pointer past
+ the last non-NUL byte written into DST. */
+
+static char *
+rpl_stpncpy (char *dest, const char *src, size_t n)
+{
+ char c, *s;
+ size_t n4;
+
+ s = dest;
+
+ if (n >= 4)
+ {
+ n4 = n >> 2;
+
+ for (;;)
+ {
+ c = *src++;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ c = *src++;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ c = *src++;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ c = *src++;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ if (--n4 == 0)
+ goto last_chars;
+ }
+ n -= dest - s;
+ goto zero_fill;
+ }
+
+ last_chars:
+ n &= 3;
+ if (n == 0)
+ return dest;
+
+ for (;;)
+ {
+ c = *src++;
+ --n;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ if (n == 0)
+ return dest;
+ }
+
+ zero_fill:
+ while (n-- > 0)
+ dest[n] = '\0';
+
+ return dest - 1;
+}
+
+#define stpncpy rpl_stpncpy
+#endif /* !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY */
+
+
+
+/* Executable reading functions.
+ These functions extract information from an executable that is
+ about to be loaded.
+
+ `exec_0' takes the name of the program, determines whether or not
+ its format is correct, and if so, returns the list of actions that
+ the loader should perform.
+
+ The actions include:
+
+ - Making the stack executable, if PT_GNU_STACK.
+ - Mapping PT_LOAD sections into the executable with the correct
+ memory protection.
+ - On MIPS, setting the floating point register size.
+ - Transferring control to the interpreter or executable. */
+
+
+/* Check whether or not FD starts with a #!, and return the executable
+ to load if it does. Value is NAME if no interpreter character was
+ found, or the interpreter otherwise. Value is NULL upon an IO
+ error.
+
+ If an additional command line argument is specified, place it in
+ *EXTRA. */
+
+static const char *
+check_interpreter (const char *name, int fd, const char **extra)
+{
+ static char buffer[PATH_MAX], *start;
+ char first[2], *end, *ws;
+ ssize_t rc;
+
+ /* Read the first character. */
+ rc = read (fd, &first, 2);
+
+ if (rc != 2)
+ goto fail;
+
+ if (first[0] != '#' || first[1] != '!')
+ goto nomatch;
+
+ rc = read (fd, buffer, PATH_MAX);
+
+ if (rc < 0)
+ goto fail;
+
+ /* Strip leading whitespace. */
+ start = buffer;
+ while (*start && ((unsigned char) *start) < 128 && isspace (*start))
+ ++start;
+
+ /* Look for a newline character. */
+ end = memchr (start, '\n', rc);
+
+ if (!end)
+ goto fail;
+
+ /* The string containing the interpreter is now in start. NULL
+ terminate it. */
+ *end = '\0';
+
+ /* Now look for any whitespace characters. */
+ ws = strchr (start, ' ');
+
+ /* If there's no whitespace, return the entire start. */
+
+ if (!ws)
+ {
+ if (lseek (fd, 0, SEEK_SET))
+ goto fail;
+
+ return start;
+ }
+
+ /* Otherwise, split the string at the whitespace and return the
+ additional argument. */
+ *ws = '\0';
+
+ if (lseek (fd, 0, SEEK_SET))
+ goto fail;
+
+ *extra = ws + 1;
+ return start;
+
+ nomatch:
+ /* There's no interpreter. */
+ if (lseek (fd, 0, SEEK_SET))
+ goto fail;
+
+ return name;
+
+ fail:
+ errno = ENOEXEC;
+ return NULL;
+}
+
+/* Static area used to store data placed on the loader's stack. */
+static char loader_area[65536];
+
+/* Number of bytes used in that area. */
+static int loader_area_used;
+
+
+
+/* Structure definitions for commands placed in the loader area.
+ Arrange these so that each member is naturally aligned. */
+
+struct exec_open_command
+{
+ /* Word identifying the type of this command. */
+ USER_WORD command;
+
+ /* NULL-terminated file name follows, padded to the size of a user
+ word. */
+};
+
+struct exec_map_command
+{
+ /* Word identifying the type of this command. */
+ USER_WORD command;
+
+ /* Where the file will be mapped. */
+ USER_WORD vm_address;
+
+ /* Offset into the file to map from. */
+ USER_WORD file_offset;
+
+ /* Memory protection for mprotect. */
+ USER_WORD protection;
+
+ /* Number of bytes to be mapped. */
+ USER_WORD length;
+
+ /* Flags for mmap. */
+ USER_WORD flags;
+
+ /* Number of bytes to clear at the end of this mapping. */
+ USER_WORD clear;
+};
+
+struct exec_jump_command
+{
+ /* Word identifying the type of this command. */
+ USER_WORD command;
+
+ /* Address to jump to. */
+ USER_WORD entry;
+
+ /* The value of AT_ENTRY inside the aux vector. */
+ USER_WORD at_entry;
+
+ /* The value of AT_PHENT inside the aux vector. */
+ USER_WORD at_phent;
+
+ /* The value of AT_PHNUM inside the aux vector. */
+ USER_WORD at_phnum;
+
+ /* The value of AT_PHDR inside the aux vector. */
+ USER_WORD at_phdr;
+
+ /* The value of AT_BASE inside the aux vector. */
+ USER_WORD at_base;
+
+#if defined __mips__ && !defined MIPS_NABI
+ /* The FPU mode to apply. */
+ USER_WORD fpu_mode;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+};
+
+
+
+/* Write a command to open the file NAME to the loader area.
+ If ALTERNATE is true, then use the command code 16 instead
+ of 0. Value is 1 upon failure, else 0. */
+
+static int
+write_open_command (const char *name, bool alternate)
+{
+ struct exec_open_command command;
+ size_t size;
+
+ /* First, write the command to open NAME. This is followed by NAME
+ itself, padded to sizeof (USER_WORD) bytes. */
+
+ command.command = alternate ? 16 : 0;
+ if (sizeof loader_area - loader_area_used < sizeof command)
+ return 1;
+ memcpy (loader_area + loader_area_used, &command, sizeof command);
+ loader_area_used += sizeof command;
+
+ /* Calculate the length of NAME. */
+ size = strlen (name) + 1;
+
+ /* Round it up. */
+ size = ((size + (sizeof (USER_WORD) - 1))
+ & ~(sizeof (USER_WORD) - 1));
+
+ if (sizeof loader_area - loader_area_used < size)
+ return 1;
+
+ /* Now copy name to the loader area, filling the padding with NULL
+ bytes. */
+ strncpy (loader_area + loader_area_used, name, size);
+
+ /* Increase loader_area_used. */
+ loader_area_used += size;
+ return 0;
+}
+
+/* Write the commands necessary to map the executable file into memory
+ for the given PT_LOAD program HEADER. Value is 1 upon failure,
+ else 0. If USE_ALTERNATE, use the command code 17 instead of
+ 1.
+
+ Apply the given OFFSET to virtual addresses that will be mapped. */
+
+static int
+write_load_command (program_header *header, bool use_alternate,
+ USER_WORD offset)
+{
+ struct exec_map_command command;
+ struct exec_map_command command1;
+ USER_WORD start, end;
+ bool need_command1;
+ static long pagesize;
+
+ /* First, write the commands necessary to map the specified segment
+ itself.
+
+ This is the area between header->p_vaddr and header->p_filesz,
+ rounded up to the page size. */
+
+#ifndef PAGE_MASK
+ /* This system doesn't define a fixed page size. */
+
+#ifdef HAVE_GETPAGESIZE
+ if (!pagesize)
+ pagesize = getpagesize ();
+#else /* HAVE_GETPAGESIZE */
+ if (!pagesize)
+ pagesize = sysconf (_SC_PAGESIZE);
+
+#define PAGE_MASK (~(pagesize - 1))
+#define PAGE_SIZE (pagesize)
+#endif /* HAVE_GETPAGESIZE */
+#endif /* PAGE_MASK */
+
+ start = header->p_vaddr & PAGE_MASK;
+ end = ((header->p_vaddr + header->p_filesz
+ + PAGE_SIZE)
+ & PAGE_MASK);
+
+ command.command = use_alternate ? 17 : 1;
+ command.vm_address = start;
+ command.file_offset = header->p_offset & PAGE_MASK;
+ command.protection = 0;
+ command.length = end - start;
+ command.clear = 0;
+ command.flags = MAP_PRIVATE | MAP_FIXED;
+
+ /* Apply the memory protection specified in the header. */
+
+ if (header->p_flags & 4) /* PF_R */
+ command.protection |= PROT_READ;
+
+ if (header->p_flags & 2) /* PF_W */
+ command.protection |= PROT_WRITE;
+
+ if (header->p_flags & 1) /* PF_X */
+ command.protection |= PROT_EXEC;
+
+ /* Next, write any command necessary to map pages in the area
+ between p_filesz and p_memsz. */
+ need_command1 = false;
+
+ if (header->p_memsz > header->p_filesz)
+ {
+ /* If there are bytes after end which need to be initialized, do
+ that now. */
+ command.clear = end - header->p_vaddr - header->p_filesz;
+ start = end;
+ end = header->p_vaddr + header->p_memsz + PAGE_SIZE;
+ end &= PAGE_MASK;
+
+ if (end > start)
+ {
+ command1.command = 4;
+ command1.vm_address = start;
+ command1.file_offset = 0;
+ command1.length = end - start;
+ command1.clear = 0;
+ command1.protection = command.protection;
+ command1.flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED;
+ need_command1 = true;
+ }
+ }
+
+ /* Apply the offset to both commands if necessary. */
+
+ if (offset)
+ {
+ if (need_command1)
+ command1.vm_address += offset;
+
+ command.vm_address += offset;
+ }
+
+ /* Write both commands. */
+
+ if (sizeof loader_area - loader_area_used < sizeof command)
+ return 1;
+
+ memcpy (loader_area + loader_area_used, &command,
+ sizeof command);
+ loader_area_used += sizeof command;
+
+ if (!need_command1)
+ return 0;
+
+ if (sizeof loader_area - loader_area_used < sizeof command1)
+ return 1;
+
+ memcpy (loader_area + loader_area_used, &command1,
+ sizeof command1);
+ loader_area_used += sizeof command1;
+
+ return 0;
+}
+
+#if defined __mips__ && !defined MIPS_NABI
+
+/* Static storage used for MIPS ABI flags. */
+static struct mips_elf_abi_flags exec_abi, interpreter_abi;
+
+/* Static storage for interpreter headers. */
+static elf_header exec_interpreter_header;
+
+/* Pointer to the ELF header of this executable's interpreter. */
+static elf_header *interpreter_header;
+
+/* Pointer to any PT_MIPS_ABIFLAGS program header found in the
+ executable itself. */
+static struct mips_elf_abi_flags *exec_abiflags;
+
+/* Pointer to any PT_MIPS_ABIFLAGS program header found in the
+ executable's ELF interpreter. */
+static struct mips_elf_abi_flags *interpreter_abiflags;
+
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+/* Process the specified program HEADER; HEADER is from the ELF
+ interpreter of another executable. FD is the executable file from
+ which it is being read, NAME is its file name, and ELF_HEADER is
+ its header.
+
+ If ELF_HEADER->e_type is ET_DYN, add the base address for position
+ independent interpreter code to virtual addresses.
+
+ Value is 1 upon failure, else 0. */
+
+static int
+process_interpreter_1 (const char *name, int fd,
+ program_header *header,
+ elf_header *elf_header)
+{
+ int rc;
+#if defined __mips__ && !defined MIPS_NABI
+ ssize_t rc1;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+ switch (header->p_type)
+ {
+ default: /* PT_NULL, PT_NOTE, PT_DYNAMIC, PT_INTERP, et cetera */
+ rc = 0;
+ break;
+
+ case 1: /* PT_LOAD */
+ /* This describes a segment in the file that must be loaded.
+ Write the appropriate load command. */
+
+ if (elf_header->e_type == 3) /* ET_DYN */
+ rc = write_load_command (header, true,
+ INTERPRETER_BASE);
+ else
+ rc = write_load_command (header, true, 0);
+
+ break;
+
+#if defined __mips__ && !defined MIPS_NABI
+ case 0x70000003: /* PT_MIPS_ABIFLAGS */
+ /* Record this header for later use. */
+ rc1 = pread (fd, &interpreter_abi, sizeof interpreter_abi,
+ header->p_offset);
+
+ if (rc1 != sizeof interpreter_abi)
+ return 1;
+
+ interpreter_abiflags = &interpreter_abi;
+ rc = 0;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+ }
+
+ return rc;
+}
+
+/* Read the ELF interpreter specified in the given program header from
+ FD, and append the commands necessary to load it to the load area.
+ Then, return the interpreter entry point in *ENTRY.
+
+ Value is 1 upon failure, else 0. */
+
+static int
+process_interpreter (int fd, program_header *prog_header,
+ USER_WORD *entry)
+{
+ char buffer[PATH_MAX + 1];
+ int rc, size, i;
+ elf_header header;
+ program_header program;
+
+ /* Read the interpreter name. */
+ size = MIN (prog_header->p_filesz, PATH_MAX);
+ rc = pread (fd, buffer, size, prog_header->p_offset);
+ if (rc < size)
+ return 1;
+
+ /* Make sure the name is NULL terminated. */
+ buffer[size] = '\0';
+
+ /* Check if the file is executable. This is unfortunately not
+ atomic. */
+
+ if (access (buffer, X_OK))
+ return 1;
+
+ /* Read the interpreter's header much like exec_0.
+
+ However, use special command codes in `process_program_header' if
+ it is position independent. That way, the loader knows it should
+ use the open interpreter instead. */
+
+ fd = open (buffer, O_RDONLY);
+
+ if (fd < 0)
+ return 1;
+
+ rc = read (fd, &header, sizeof header);
+
+ if (rc < sizeof header)
+ goto fail;
+
+#if defined __mips__ && !defined MIPS_NABI
+ /* Record this interpreter's header for later use determining the
+ floating point ABI. */
+ exec_interpreter_header = header;
+ interpreter_header = &exec_interpreter_header;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+ /* Verify that this is indeed an ELF file. */
+
+ if (header.e_ident[0] != 0x7f
+ || header.e_ident[1] != 'E'
+ || header.e_ident[2] != 'L'
+ || header.e_ident[3] != 'F')
+ goto fail;
+
+ /* Now check that the class is correct. */
+#ifdef EXEC_64
+ if (header.e_ident[4] != 2)
+ goto fail;
+#else /* !EXEC_64 */
+ if (header.e_ident[4] != 1)
+ goto fail;
+#endif /* EXEC_64 */
+
+ /* And the endianness. */
+#ifndef WORDS_BIGENDIAN
+ if (header.e_ident[5] != 1)
+ goto fail;
+#else /* WORDS_BIGENDIAN */
+ if (header.e_ident[5] != 2)
+ goto fail;
+#endif /* EXEC_64 */
+
+ /* Check that this is an executable. */
+ if (header.e_type != 2 && header.e_type != 3)
+ goto fail;
+
+ /* Now check that the ELF program header makes sense. */
+ if (header.e_phnum > 0xffff
+ || (header.e_phentsize
+ != sizeof (program_header)))
+ goto fail;
+
+ if (write_open_command (buffer, true))
+ goto fail;
+
+ for (i = 0; i < header.e_phnum; ++i)
+ {
+ rc = read (fd, &program, sizeof program);
+ if (rc < sizeof program)
+ goto fail;
+
+ if (process_interpreter_1 (buffer, fd, &program,
+ &header))
+ goto fail;
+ }
+
+ if (header.e_type == 3) /* ET_DYN */
+ *entry = header.e_entry + INTERPRETER_BASE;
+ else
+ *entry = header.e_entry;
+
+ close (fd);
+ return 0;
+
+ fail:
+ close (fd);
+ return 1;
+}
+
+/* Process the specified program HEADER. FD is the executable file
+ from which it is being read, NAME is its file name, and ELF_HEADER
+ is its header.
+
+ If ELF_HEADER->e_type is ET_DYN, add the base address for position
+ independent code to virtual addresses.
+
+ If OFFSET is non-NULL, and *OFFSET is -1, write the virtual address
+ of HEADER if it describes a PT_LOAD segment.
+
+ If an interpreter is found, set *ENTRY to its entry point.
+
+ Value is 1 upon failure, else 0. */
+
+static int
+process_program_header (const char *name, int fd,
+ program_header *header,
+ elf_header *elf_header,
+ USER_WORD *entry,
+ USER_WORD *offset)
+{
+ int rc;
+#if defined __mips__ && !defined MIPS_NABI
+ ssize_t rc1;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+ switch (header->p_type)
+ {
+ default: /* PT_NULL, PT_NOTE, PT_DYNAMIC, et cetera */
+ rc = 0;
+ break;
+
+ case 1: /* PT_LOAD */
+ /* This describes a segment in the file that must be loaded.
+ Write the appropriate load command. */
+
+ if (elf_header->e_type == 3) /* ET_DYN */
+ {
+ rc = write_load_command (header, false,
+ EXECUTABLE_BASE);
+
+ if (!rc && offset && *offset == (USER_WORD) -1)
+ *offset = EXECUTABLE_BASE + header->p_vaddr;
+ }
+ else
+ {
+ rc = write_load_command (header, false, 0);
+
+ if (!rc && offset && *offset == (USER_WORD) -1)
+ *offset = header->p_vaddr;
+ }
+
+ break;
+
+ case 3: /* PT_INTERP */
+ /* This describes another executable that must be loaded. Open
+ the interpreter and process each of its headers as well. */
+ rc = process_interpreter (fd, header, entry);
+ break;
+
+ case 1685382481: /* PT_GNU_STACK */
+ /* TODO */
+ rc = 0;
+ break;
+
+#if defined __mips__ && !defined MIPS_NABI
+ case 0x70000003: /* PT_MIPS_ABIFLAGS */
+ /* Record this header for later use. */
+ rc1 = pread (fd, &exec_abi, sizeof exec_abi,
+ header->p_offset);
+
+ if (rc1 != sizeof exec_abi)
+ return 1;
+
+ exec_abiflags = &exec_abi;
+ rc = 0;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+ }
+
+ return rc;
+}
+
+/* Prepend one or two extra arguments ARG1 and ARG2 to a pending
+ execve system call. TRACEE is the tracee performing the system
+ call, and REGS are its current user registers. Value is 1 upon
+ failure, else 0. */
+
+static int
+insert_args (struct exec_tracee *tracee, USER_REGS_STRUCT *regs,
+ const char *arg1, const char *arg2)
+{
+ USER_WORD argv, argc, word, new;
+ USER_WORD new1, new2;
+ size_t text_size, effective_size;
+ USER_REGS_STRUCT original;
+
+ /* First, get a pointer to the current argument vector. */
+ argv = regs->SYSCALL_ARG1_REG;
+
+ /* Now figure out how many arguments there are. */
+ argc = 0;
+ while (true)
+ {
+ /* Clear errno. PTRACE_PEEKDATA returns the word read the same
+ way failure indications are returned, so the only way to
+ catch IO errors is by clearing errno before the call to
+ ptrace and checking it afterwards. */
+
+ errno = 0;
+ word = ptrace (PTRACE_PEEKDATA, tracee->pid,
+ (void *) argv, NULL);
+ argv += sizeof (USER_WORD);
+
+ if (errno)
+ return 1;
+
+ if (!word)
+ break;
+
+ ++argc;
+ };
+
+ /* Allocate enough to hold that many arguments, alongside the argc
+ text. */
+
+ text_size = (strlen (arg1) + 1
+ + (arg2 ? strlen (arg2) + 1 : 0));
+
+ /* Round it up to the user word size. */
+ text_size += sizeof (USER_WORD) - 1;
+ text_size &= ~(sizeof (USER_WORD) - 1);
+
+ /* Now allocate the new argv. */
+
+ effective_size = sizeof word * (argc + 2) + text_size;
+
+ if (arg2)
+ effective_size += sizeof word;
+
+ /* Copy regs to original so that user_alloca knows it should append
+ the ABI red zone. */
+
+ memcpy (&original, regs, sizeof *regs);
+ new = user_alloca (tracee, &original, regs,
+ effective_size);
+
+ if (!new)
+ goto fail;
+
+ /* Figure out where argv starts. */
+
+ new2 = new + text_size;
+
+ /* Now write the two strings. */
+
+ new1 = new + strlen (arg1) + 1;
+ if (user_copy (tracee, (const unsigned char *) arg1,
+ new, new1 - new))
+ goto fail;
+
+ if (arg2 && user_copy (tracee, (const unsigned char *) arg2,
+ new1, new2 - new1))
+ goto fail;
+
+ /* Start copying argv back to new2. First, write the one or two new
+ arguments. */
+
+ if (ptrace (PTRACE_POKETEXT, tracee->pid,
+ (void *) new2, (void *) new))
+ goto fail;
+
+ new2 += sizeof new2;
+
+ if (arg2 && ptrace (PTRACE_POKETEXT, tracee->pid,
+ (void *) new2, (void *) new1))
+ goto fail;
+ else if (arg2)
+ new2 += sizeof new2;
+
+ /* Copy the remaining arguments back. */
+
+ argv = regs->SYSCALL_ARG1_REG;
+
+ /* Make sure the trailing NULL is included. */
+ argc += 1;
+
+ while (argc)
+ {
+ /* Read one argument. */
+ word = ptrace (PTRACE_PEEKDATA, tracee->pid,
+ (void *) argv, NULL);
+ argv += sizeof argv;
+ argc--;
+
+ /* Write one argument, then increment new2. */
+
+ if (ptrace (PTRACE_POKETEXT, tracee->pid,
+ (void *) new2, (void *) word))
+ goto fail;
+
+ new2 += sizeof new2;
+ }
+
+ /* Assert that new2 is not out of bounds. */
+ assert (new2 == new + effective_size);
+
+ /* And that it is properly aligned. */
+ assert (!(new2 & (sizeof new2 - 2)));
+
+ /* Now modify the system call argument to point to new +
+ text_size. */
+
+ regs->SYSCALL_ARG1_REG = new + text_size;
+
+#ifdef __aarch64__
+ if (aarch64_set_regs (tracee->pid, regs, false))
+ goto fail;
+#else /* !__aarch64__ */
+ if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs))
+ goto fail;
+#endif /* __aarch64__ */
+
+ /* Success. */
+
+ return 0;
+
+ fail:
+ /* Restore the original stack pointer. */
+#ifdef __aarch64__
+ aarch64_set_regs (tracee->pid, &original, false);
+#else /* !__aarch64__ */
+ ptrace (PTRACE_SETREGS, tracee->pid, NULL, &original);
+#endif /* __aarch64__ */
+ errno = ENOMEM;
+ return 1;
+}
+
+
+
+/* Format PID, an unsigned process identifier, in base 10. Place the
+ result in *IN, and return a pointer to the byte after the
+ result. REM should be NULL. */
+
+static char *
+format_pid (char *in, unsigned int pid)
+{
+ unsigned int digits[32], *fill;
+
+ fill = digits;
+
+ for (; pid != 0; pid = pid / 10)
+ *fill++ = pid % 10;
+
+ /* Insert 0 if the number would otherwise be empty. */
+
+ if (fill == digits)
+ *fill++ = 0;
+
+ while (fill != digits)
+ {
+ --fill;
+ *in++ = '0' + *fill;
+ }
+
+ *in = '\0';
+ return in;
+}
+
+/* Return a sequence of actions required to load the executable under
+ the file NAME for the given TRACEE. First, see if the file starts
+ with #!; in that case, find the program to open and use that
+ instead.
+
+ If REENTRANT is not defined, NAME is actually a buffer of size
+ PATH_MAX + 80. In that case, copy over the file name actually
+ opened.
+
+ Next, read the executable header, and add the necessary memory
+ mappings for each file. Finally, return the action data and its
+ size in *SIZE.
+
+ Finally, use REGS to add the required interpreter arguments to the
+ caller's argv.
+
+ Value is NULL upon failure, with errno set accordingly. */
+
+char *
+exec_0 (char *name, struct exec_tracee *tracee,
+ size_t *size, USER_REGS_STRUCT *regs)
+{
+ int fd, rc, i;
+ elf_header header;
+ const char *interpreter_name, *extra;
+ program_header program;
+ USER_WORD entry, program_entry, offset;
+ USER_WORD header_offset;
+ struct exec_jump_command jump;
+#if defined __mips__ && !defined MIPS_NABI
+ int fpu_mode;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+ char buffer[80], buffer1[PATH_MAX + 80], *rewrite;
+ ssize_t link_size;
+ size_t remaining;
+
+ /* If the process is trying to run /proc/self/exe, make it run
+ itself instead. */
+
+ if (!strcmp (name, "/proc/self/exe") && tracee->exec_file)
+ {
+ strncpy (name, tracee->exec_file, PATH_MAX - 1);
+ name[PATH_MAX] = '\0';
+ }
+ else
+ {
+ /* If name is not absolute, then make it relative to TRACEE's
+ cwd. Use stpcpy, as sprintf is not reentrant. */
+
+ if (name[0] && name[0] != '/')
+ {
+ /* Clear `buffer'. */
+ memset (buffer, 0, sizeof buffer);
+ memset (buffer1, 0, sizeof buffer);
+
+ /* Copy over /proc, the PID, and /cwd/. */
+ rewrite = stpcpy (buffer, "/proc/");
+ rewrite = format_pid (rewrite, tracee->pid);
+ stpcpy (rewrite, "/cwd");
+
+ /* Resolve this symbolic link. */
+
+ link_size = readlink (buffer, buffer1,
+ PATH_MAX + 1);
+
+ if (link_size < 0)
+ return NULL;
+
+ /* Check that the name is a reasonable size. */
+
+ if (link_size > PATH_MAX)
+ {
+ /* The name is too long. */
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ /* Add a directory separator if necessary. */
+
+ if (!link_size || buffer1[link_size - 1] != '/')
+ buffer1[link_size] = '/', link_size++;
+
+ rewrite = buffer1 + link_size;
+ remaining = buffer1 + sizeof buffer1 - rewrite - 1;
+ rewrite = stpncpy (rewrite, name, remaining);
+
+ /* Replace name with buffer1. */
+#ifndef REENTRANT
+ strcpy (name, buffer1);
+#endif /* REENTRANT */
+ }
+ }
+
+ /* Check that the file is accessible and executable. */
+
+ if (access (name, X_OK))
+ return NULL;
+
+ fd = open (name, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ /* Now read the header. */
+
+ extra = NULL;
+ interpreter_name = check_interpreter (name, fd, &extra);
+ if (!interpreter_name)
+ goto fail;
+
+ /* Open the interpreter instead, if necessary. */
+ if (interpreter_name != name)
+ {
+ close (fd);
+ fd = open (interpreter_name, O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ /* Now, rewrite the argument list to include `interpreter_name'
+ and perhaps `extra'. */
+
+ if (insert_args (tracee, regs, interpreter_name,
+ extra))
+ goto fail1;
+ }
+
+ rc = read (fd, &header, sizeof header);
+
+ if (rc < sizeof header)
+ goto fail1;
+
+ /* Verify that this is indeed an ELF file. */
+
+ if (header.e_ident[0] != 0x7f
+ || header.e_ident[1] != 'E'
+ || header.e_ident[2] != 'L'
+ || header.e_ident[3] != 'F')
+ goto fail1;
+
+ /* Now check that the class is correct. */
+#ifdef EXEC_64
+ if (header.e_ident[4] != 2)
+ goto fail1;
+#else /* !EXEC_64 */
+ if (header.e_ident[4] != 1)
+ goto fail1;
+#endif /* EXEC_64 */
+
+ /* And the endianness. */
+#ifndef WORDS_BIGENDIAN
+ if (header.e_ident[5] != 1)
+ goto fail1;
+#else /* WORDS_BIGENDIAN */
+ if (header.e_ident[5] != 2)
+ goto fail1;
+#endif /* EXEC_64 */
+
+ /* Check that this is an executable. */
+ if (header.e_type != 2 && header.e_type != 3)
+ goto fail1;
+
+ /* Now check that the ELF program header makes sense. */
+ if (header.e_phnum > 0xffff
+ || (header.e_phentsize
+ != sizeof (program_header)))
+ goto fail1;
+
+ /* Seek to the first program header and read each one. */
+ rc = lseek (fd, header.e_phoff, SEEK_SET);
+ if (rc < 0)
+ goto fail1;
+ loader_area_used = 0;
+
+ /* Write the command used to open the executable. */
+ if (write_open_command (interpreter_name, false))
+ goto fail1;
+
+ /* Apply base addresses for PIC code. */
+
+ if (header.e_type == 3) /* ET_DYN */
+ offset = EXECUTABLE_BASE;
+ else
+ offset = 0;
+
+ /* entry and program_entry are initially the same, but entry may be
+ set to that of the interpreter if one is present. */
+
+ entry = header.e_entry + offset;
+ program_entry = header.e_entry;
+
+#if defined __mips__ && !defined MIPS_NABI
+ /* Clear MIPS ABI flags. */
+ exec_abiflags = NULL;
+ interpreter_abiflags = NULL;
+ interpreter_header = NULL;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+ /* Set header_offset to -1; `process_program_header' then updates it
+ to that of the first mapping. */
+ header_offset = -1;
+
+ for (i = 0; i < header.e_phnum; ++i)
+ {
+ rc = read (fd, &program, sizeof program);
+ if (rc < sizeof program)
+ goto fail1;
+
+ if (process_program_header (interpreter_name, fd,
+ &program, &header,
+ &entry, &header_offset))
+ goto fail1;
+ }
+
+ /* Write the entry point and program entry. */
+
+ jump.command = 3;
+ jump.entry = entry;
+
+ /* Now calculate values for the aux vector. */
+
+ jump.at_entry = program_entry + offset;
+ jump.at_phent = header.e_phentsize;
+ jump.at_phnum = header.e_phnum;
+ jump.at_base = (entry == header.e_entry + offset
+ ? EXECUTABLE_BASE
+ : INTERPRETER_BASE);
+
+#if defined __mips__ && !defined MIPS_NABI
+ /* Finally, calculate the FPU mode wanted by the executable. */
+
+ if (determine_fpu_mode (&header, interpreter_header,
+ &fpu_mode, exec_abiflags,
+ interpreter_abiflags))
+ /* N.B. that `determine_fpu_mode' sets errno. */
+ goto fail;
+
+ /* If the processor is too new to support FR0 operation, place the
+ executable in floating point emulation mode. */
+
+ if (fpu_mode == FP_FR0 && !cpu_supports_fr0_p ())
+ fpu_mode = FP_FRE;
+
+ jump.fpu_mode = fpu_mode;
+#endif /* defined __mips__ && !defined MIPS_NABI */
+
+ /* The offset used for at_phdr should be that of the first
+ mapping. */
+
+ if (header_offset == (USER_WORD) -1)
+ header_offset = 0;
+
+ jump.at_phdr = header.e_phoff + header_offset;
+
+ if (sizeof loader_area - loader_area_used < sizeof jump)
+ goto fail1;
+
+ memcpy (loader_area + loader_area_used, &jump,
+ sizeof jump);
+ loader_area_used += sizeof jump;
+
+ /* Close the file descriptor and return the number of bytes
+ used. */
+
+ close (fd);
+ *size = loader_area_used;
+
+ /* Make sure the loader area is properly aligned. */
+ assert (!(loader_area_used & (sizeof (USER_WORD) - 1)));
+ return loader_area;
+
+ fail1:
+ errno = ENOEXEC;
+ fail:
+ close (fd);
+ return NULL;
+}
diff --git a/exec/exec.h b/exec/exec.h
new file mode 100644
index 00000000000..8ee74d7ca8b
--- /dev/null
+++ b/exec/exec.h
@@ -0,0 +1,201 @@
+/* Program execution for Emacs.
+
+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/>. */
+
+
+
+#ifndef _EXEC_H_
+#define _EXEC_H_
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#endif /* HAVE_STDINT_H */
+
+#include <sys/types.h>
+
+#include USER_HEADER
+
+/* Define a replacement for `uint64_t' if it's not present in the C
+ library. */
+
+#ifndef UINT64_MAX
+
+typedef struct
+{
+ uint32_t word1;
+ uint32_t word2;
+} xint64_t;
+
+#else /* UINT64_MAX */
+typedef uint64_t xint64_t;
+#endif /* !UINT64_MAX */
+
+
+
+/* 32-bit ELF headers. */
+
+struct elf_header_32
+{
+ unsigned char e_ident[16];
+ uint16_t e_type;
+ uint16_t e_machine;
+ uint32_t e_version;
+ uint32_t e_entry;
+ uint32_t e_phoff;
+ uint32_t e_shoff;
+ uint32_t e_flags;
+ uint16_t e_ehsize;
+ uint16_t e_phentsize;
+ uint16_t e_phnum;
+ uint16_t e_shentsize;
+ uint16_t e_shnum;
+ uint16_t e_shstrndx;
+};
+
+struct program_header_32
+{
+ uint32_t p_type;
+ uint32_t p_offset;
+ uint32_t p_vaddr;
+ uint32_t p_paddr;
+ uint32_t p_filesz;
+ uint32_t p_memsz;
+ uint32_t p_flags;
+ uint32_t p_align;
+};
+
+struct dt_entry_32
+{
+ uint32_t d_tag;
+ uint32_t d_val;
+};
+
+
+
+struct elf_header_64
+{
+ unsigned char e_ident[16];
+ uint16_t e_type;
+ uint16_t e_machine;
+ uint32_t e_version;
+ xint64_t e_entry;
+ xint64_t e_phoff;
+ xint64_t e_shoff;
+ uint32_t e_flags;
+ uint16_t e_ehsize;
+ uint16_t e_phentsize;
+ uint16_t e_phnum;
+ uint16_t e_shentsize;
+ uint16_t e_shnum;
+ uint16_t e_shstrndx;
+};
+
+struct program_header_64
+{
+ uint32_t p_type;
+ uint32_t p_flags;
+ xint64_t p_offset;
+ xint64_t p_vaddr;
+ xint64_t p_paddr;
+ xint64_t p_filesz;
+ xint64_t p_memsz;
+ xint64_t p_align;
+};
+
+struct dt_entry_64
+{
+ xint64_t d_tag;
+ xint64_t d_val;
+};
+
+
+
+/* Define some types to the correct values. */
+
+#ifdef EXEC_64
+typedef struct elf_header_64 elf_header;
+typedef struct program_header_64 program_header;
+typedef struct dt_entry_64 dt_entry;
+#else /* !EXEC_64 */
+typedef struct elf_header_32 elf_header;
+typedef struct program_header_32 program_header;
+typedef struct dt_entry_32 dt_entry;
+#endif /* EXEC_64 */
+
+
+
+/* Defined in trace.c. */
+
+/* Structure describing a process being traced. */
+
+struct exec_tracee
+{
+ /* The next process being traced. */
+ struct exec_tracee *next;
+
+ /* The thread ID of this process. */
+ pid_t pid;
+
+ /* Whether or not the tracee is currently waiting for a system call
+ to complete. */
+ bool waiting_for_syscall : 1;
+
+ /* Whether or not the tracee has been created but is not yet
+ processed by `handle_clone'. */
+ bool new_child : 1;
+
+#ifndef REENTRANT
+ /* Name of the executable being run. */
+ char *exec_file;
+#endif /* !REENTRANT */
+};
+
+
+
+#ifdef __aarch64__
+
+extern int aarch64_get_regs (pid_t, USER_REGS_STRUCT *);
+extern int aarch64_set_regs (pid_t, USER_REGS_STRUCT *, bool);
+
+#endif /* __aarch64__ */
+
+
+
+extern USER_WORD user_alloca (struct exec_tracee *, USER_REGS_STRUCT *,
+ USER_REGS_STRUCT *, USER_WORD);
+extern int user_copy (struct exec_tracee *, const unsigned char *,
+ USER_WORD, USER_WORD);
+extern void exec_init (const char *);
+
+
+
+extern int tracing_execve (const char *, char *const *,
+ char *const *);
+extern int after_fork (pid_t);
+extern pid_t exec_waitpid (pid_t, int *, int);
+
+
+
+/* Defined in exec.c. */
+
+extern char *exec_0 (char *, struct exec_tracee *,
+ size_t *, USER_REGS_STRUCT *);
+
+
+
+#endif /* _EXEC_H_ */
diff --git a/exec/exec1.c b/exec/exec1.c
new file mode 100644
index 00000000000..d77ca8adf54
--- /dev/null
+++ b/exec/exec1.c
@@ -0,0 +1,94 @@
+/* Program execution for Emacs.
+
+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 <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/wait.h>
+
+#include "exec.h"
+
+/* exec1 is a program which takes another program and its arguments,
+ forks, and executes that program, all while tracing it and its
+ children to use the program execution mechanism defined in exec.c.
+
+ This is necessary to bypass security restrictions which prohibit
+ Emacs from loading executables from certain directories, by, in
+ effect, replacing the executable loader in the Linux kernel. */
+
+
+
+int
+main (int argc, char **argv)
+{
+ pid_t pid, pid1;
+ extern char **environ;
+ int wstatus;
+
+ pid1 = getpid ();
+ pid = fork ();
+
+ if (!pid)
+ {
+ /* Set the process group used to the parent. */
+ if (setpgid (0, pid1))
+ perror ("setpgid");
+
+ tracing_execve (argv[2], argv + 2, environ);
+
+ /* An error occured. Exit with failure. */
+ exit (127);
+ }
+ else
+ {
+ /* Provide the file name of the loader. */
+ exec_init (argv[1]);
+
+ if (after_fork (pid))
+ exit (127);
+
+ /* Start waiting for the process to exit. */
+
+ while (true)
+ {
+ pid1 = exec_waitpid (-1, &wstatus, 0);
+
+ /* If the child process exits normally, exit with its status
+ code. If not, raise the signal that caused it to
+ exit. */
+
+ if (pid == pid1)
+ {
+ if (WIFEXITED (wstatus))
+ exit (WEXITSTATUS (wstatus));
+ else /* if WIFSIGNALED (wstatus) */
+ {
+ raise (WTERMSIG (wstatus));
+
+ /* Just in case the signal raised doesn't cause an
+ exit. */
+ exit (127);
+ }
+ }
+
+ /* Otherwise, continue looping. */
+ }
+ }
+}
diff --git a/exec/install-sh b/exec/install-sh
new file mode 100755
index 00000000000..e046efdf0a3
--- /dev/null
+++ b/exec/install-sh
@@ -0,0 +1,541 @@
+#!/usr/bin/sh
+# install - install a program, script, or datafile
+
+scriptversion=2020-11-14.01; # UTC
+
+# This originates from X11R5 (mit/util/scripts/install.sh), which was
+# later released in X11R6 (xc/config/util/install.sh) with the
+# following copyright and license.
+#
+# Copyright (C) 1994 X Consortium
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to
+# deal in the Software without restriction, including without limitation the
+# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+# sell copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
+# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+# Except as contained in this notice, the name of the X Consortium shall not
+# be used in advertising or otherwise to promote the sale, use or other deal-
+# ings in this Software without prior written authorization from the X Consor-
+# tium.
+#
+#
+# FSF changes to this file are in the public domain.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# 'make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch.
+
+tab=' '
+nl='
+'
+IFS=" $tab$nl"
+
+# Set DOITPROG to "echo" to test this script.
+
+doit=${DOITPROG-}
+doit_exec=${doit:-exec}
+
+# Put in absolute file names if you don't have them in your path;
+# or use environment vars.
+
+chgrpprog=${CHGRPPROG-chgrp}
+chmodprog=${CHMODPROG-chmod}
+chownprog=${CHOWNPROG-chown}
+cmpprog=${CMPPROG-cmp}
+cpprog=${CPPROG-cp}
+mkdirprog=${MKDIRPROG-mkdir}
+mvprog=${MVPROG-mv}
+rmprog=${RMPROG-rm}
+stripprog=${STRIPPROG-strip}
+
+posix_mkdir=
+
+# Desired mode of installed file.
+mode=0755
+
+# Create dirs (including intermediate dirs) using mode 755.
+# This is like GNU 'install' as of coreutils 8.32 (2020).
+mkdir_umask=22
+
+backupsuffix=
+chgrpcmd=
+chmodcmd=$chmodprog
+chowncmd=
+mvcmd=$mvprog
+rmcmd="$rmprog -f"
+stripcmd=
+
+src=
+dst=
+dir_arg=
+dst_arg=
+
+copy_on_change=false
+is_target_a_directory=possibly
+
+usage="\
+Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE
+ or: $0 [OPTION]... SRCFILES... DIRECTORY
+ or: $0 [OPTION]... -t DIRECTORY SRCFILES...
+ or: $0 [OPTION]... -d DIRECTORIES...
+
+In the 1st form, copy SRCFILE to DSTFILE.
+In the 2nd and 3rd, copy all SRCFILES to DIRECTORY.
+In the 4th, create DIRECTORIES.
+
+Options:
+ --help display this help and exit.
+ --version display version info and exit.
+
+ -c (ignored)
+ -C install only if different (preserve data modification time)
+ -d create directories instead of installing files.
+ -g GROUP $chgrpprog installed files to GROUP.
+ -m MODE $chmodprog installed files to MODE.
+ -o USER $chownprog installed files to USER.
+ -p pass -p to $cpprog.
+ -s $stripprog installed files.
+ -S SUFFIX attempt to back up existing files, with suffix SUFFIX.
+ -t DIRECTORY install into DIRECTORY.
+ -T report an error if DSTFILE is a directory.
+
+Environment variables override the default commands:
+ CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG
+ RMPROG STRIPPROG
+
+By default, rm is invoked with -f; when overridden with RMPROG,
+it's up to you to specify -f if you want it.
+
+If -S is not specified, no backups are attempted.
+
+Email bug reports to bug-automake@gnu.org.
+Automake home page: https://www.gnu.org/software/automake/
+"
+
+while test $# -ne 0; do
+ case $1 in
+ -c) ;;
+
+ -C) copy_on_change=true;;
+
+ -d) dir_arg=true;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift;;
+
+ --help) echo "$usage"; exit $?;;
+
+ -m) mode=$2
+ case $mode in
+ *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*)
+ echo "$0: invalid mode: $mode" >&2
+ exit 1;;
+ esac
+ shift;;
+
+ -o) chowncmd="$chownprog $2"
+ shift;;
+
+ -p) cpprog="$cpprog -p";;
+
+ -s) stripcmd=$stripprog;;
+
+ -S) backupsuffix="$2"
+ shift;;
+
+ -t)
+ is_target_a_directory=always
+ dst_arg=$2
+ # Protect names problematic for 'test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ shift;;
+
+ -T) is_target_a_directory=never;;
+
+ --version) echo "$0 $scriptversion"; exit $?;;
+
+ --) shift
+ break;;
+
+ -*) echo "$0: invalid option: $1" >&2
+ exit 1;;
+
+ *) break;;
+ esac
+ shift
+done
+
+# We allow the use of options -d and -T together, by making -d
+# take the precedence; this is for compatibility with GNU install.
+
+if test -n "$dir_arg"; then
+ if test -n "$dst_arg"; then
+ echo "$0: target directory not allowed when installing a directory." >&2
+ exit 1
+ fi
+fi
+
+if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then
+ # When -d is used, all remaining arguments are directories to create.
+ # When -t is used, the destination is already specified.
+ # Otherwise, the last argument is the destination. Remove it from $@.
+ for arg
+ do
+ if test -n "$dst_arg"; then
+ # $@ is not empty: it contains at least $arg.
+ set fnord "$@" "$dst_arg"
+ shift # fnord
+ fi
+ shift # arg
+ dst_arg=$arg
+ # Protect names problematic for 'test' and other utilities.
+ case $dst_arg in
+ -* | [=\(\)!]) dst_arg=./$dst_arg;;
+ esac
+ done
+fi
+
+if test $# -eq 0; then
+ if test -z "$dir_arg"; then
+ echo "$0: no input file specified." >&2
+ exit 1
+ fi
+ # It's OK to call 'install-sh -d' without argument.
+ # This can happen when creating conditional directories.
+ exit 0
+fi
+
+if test -z "$dir_arg"; then
+ if test $# -gt 1 || test "$is_target_a_directory" = always; then
+ if test ! -d "$dst_arg"; then
+ echo "$0: $dst_arg: Is not a directory." >&2
+ exit 1
+ fi
+ fi
+fi
+
+if test -z "$dir_arg"; then
+ do_exit='(exit $ret); exit $ret'
+ trap "ret=129; $do_exit" 1
+ trap "ret=130; $do_exit" 2
+ trap "ret=141; $do_exit" 13
+ trap "ret=143; $do_exit" 15
+
+ # Set umask so as not to create temps with too-generous modes.
+ # However, 'strip' requires both read and write access to temps.
+ case $mode in
+ # Optimize common cases.
+ *644) cp_umask=133;;
+ *755) cp_umask=22;;
+
+ *[0-7])
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw='% 200'
+ fi
+ cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;;
+ *)
+ if test -z "$stripcmd"; then
+ u_plus_rw=
+ else
+ u_plus_rw=,u+rw
+ fi
+ cp_umask=$mode$u_plus_rw;;
+ esac
+fi
+
+for src
+do
+ # Protect names problematic for 'test' and other utilities.
+ case $src in
+ -* | [=\(\)!]) src=./$src;;
+ esac
+
+ if test -n "$dir_arg"; then
+ dst=$src
+ dstdir=$dst
+ test -d "$dstdir"
+ dstdir_status=$?
+ # Don't chown directories that already exist.
+ if test $dstdir_status = 0; then
+ chowncmd=""
+ fi
+ else
+
+ # Waiting for this to be detected by the "$cpprog $src $dsttmp" command
+ # might cause directories to be created, which would be especially bad
+ # if $src (and thus $dsttmp) contains '*'.
+ if test ! -f "$src" && test ! -d "$src"; then
+ echo "$0: $src does not exist." >&2
+ exit 1
+ fi
+
+ if test -z "$dst_arg"; then
+ echo "$0: no destination specified." >&2
+ exit 1
+ fi
+ dst=$dst_arg
+
+ # If destination is a directory, append the input filename.
+ if test -d "$dst"; then
+ if test "$is_target_a_directory" = never; then
+ echo "$0: $dst_arg: Is a directory" >&2
+ exit 1
+ fi
+ dstdir=$dst
+ dstbase=`basename "$src"`
+ case $dst in
+ */) dst=$dst$dstbase;;
+ *) dst=$dst/$dstbase;;
+ esac
+ dstdir_status=0
+ else
+ dstdir=`dirname "$dst"`
+ test -d "$dstdir"
+ dstdir_status=$?
+ fi
+ fi
+
+ case $dstdir in
+ */) dstdirslash=$dstdir;;
+ *) dstdirslash=$dstdir/;;
+ esac
+
+ obsolete_mkdir_used=false
+
+ if test $dstdir_status != 0; then
+ case $posix_mkdir in
+ '')
+ # With -d, create the new directory with the user-specified mode.
+ # Otherwise, rely on $mkdir_umask.
+ if test -n "$dir_arg"; then
+ mkdir_mode=-m$mode
+ else
+ mkdir_mode=
+ fi
+
+ posix_mkdir=false
+ # The $RANDOM variable is not portable (e.g., dash). Use it
+ # here however when possible just to lower collision chance.
+ tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$
+
+ trap '
+ ret=$?
+ rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null
+ exit $ret
+ ' 0
+
+ # Because "mkdir -p" follows existing symlinks and we likely work
+ # directly in world-writeable /tmp, make sure that the '$tmpdir'
+ # directory is successfully created first before we actually test
+ # 'mkdir -p'.
+ if (umask $mkdir_umask &&
+ $mkdirprog $mkdir_mode "$tmpdir" &&
+ exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1
+ then
+ if test -z "$dir_arg" || {
+ # Check for POSIX incompatibilities with -m.
+ # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or
+ # other-writable bit of parent directory when it shouldn't.
+ # FreeBSD 6.1 mkdir -m -p sets mode of existing directory.
+ test_tmpdir="$tmpdir/a"
+ ls_ld_tmpdir=`ls -ld "$test_tmpdir"`
+ case $ls_ld_tmpdir in
+ d????-?r-*) different_mode=700;;
+ d????-?--*) different_mode=755;;
+ *) false;;
+ esac &&
+ $mkdirprog -m$different_mode -p -- "$test_tmpdir" && {
+ ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"`
+ test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1"
+ }
+ }
+ then posix_mkdir=:
+ fi
+ rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir"
+ else
+ # Remove any dirs left behind by ancient mkdir implementations.
+ rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null
+ fi
+ trap '' 0;;
+ esac
+
+ if
+ $posix_mkdir && (
+ umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir"
+ )
+ then :
+ else
+
+ # mkdir does not conform to POSIX,
+ # or it failed possibly due to a race condition. Create the
+ # directory the slow way, step by step, checking for races as we go.
+
+ case $dstdir in
+ /*) prefix='/';;
+ [-=\(\)!]*) prefix='./';;
+ *) prefix='';;
+ esac
+
+ oIFS=$IFS
+ IFS=/
+ set -f
+ set fnord $dstdir
+ shift
+ set +f
+ IFS=$oIFS
+
+ prefixes=
+
+ for d
+ do
+ test X"$d" = X && continue
+
+ prefix=$prefix$d
+ if test -d "$prefix"; then
+ prefixes=
+ else
+ if $posix_mkdir; then
+ (umask $mkdir_umask &&
+ $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break
+ # Don't fail if two instances are running concurrently.
+ test -d "$prefix" || exit 1
+ else
+ case $prefix in
+ *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;;
+ *) qprefix=$prefix;;
+ esac
+ prefixes="$prefixes '$qprefix'"
+ fi
+ fi
+ prefix=$prefix/
+ done
+
+ if test -n "$prefixes"; then
+ # Don't fail if two instances are running concurrently.
+ (umask $mkdir_umask &&
+ eval "\$doit_exec \$mkdirprog $prefixes") ||
+ test -d "$dstdir" || exit 1
+ obsolete_mkdir_used=true
+ fi
+ fi
+ fi
+
+ if test -n "$dir_arg"; then
+ { test -z "$chowncmd" || $doit $chowncmd "$dst"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } &&
+ { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false ||
+ test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1
+ else
+
+ # Make a couple of temp file names in the proper directory.
+ dsttmp=${dstdirslash}_inst.$$_
+ rmtmp=${dstdirslash}_rm.$$_
+
+ # Trap to clean up those temp files at exit.
+ trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0
+
+ # Copy the file name to the temp name.
+ (umask $cp_umask &&
+ { test -z "$stripcmd" || {
+ # Create $dsttmp read-write so that cp doesn't create it read-only,
+ # which would cause strip to fail.
+ if test -z "$doit"; then
+ : >"$dsttmp" # No need to fork-exec 'touch'.
+ else
+ $doit touch "$dsttmp"
+ fi
+ }
+ } &&
+ $doit_exec $cpprog "$src" "$dsttmp") &&
+
+ # and set any options; do chmod last to preserve setuid bits.
+ #
+ # If any of these fail, we abort the whole thing. If we want to
+ # ignore errors from any of these, just make sure not to ignore
+ # errors from the above "$doit $cpprog $src $dsttmp" command.
+ #
+ { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } &&
+ { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } &&
+ { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } &&
+ { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } &&
+
+ # If -C, don't bother to copy if it wouldn't change the file.
+ if $copy_on_change &&
+ old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` &&
+ new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` &&
+ set -f &&
+ set X $old && old=:$2:$4:$5:$6 &&
+ set X $new && new=:$2:$4:$5:$6 &&
+ set +f &&
+ test "$old" = "$new" &&
+ $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1
+ then
+ rm -f "$dsttmp"
+ else
+ # If $backupsuffix is set, and the file being installed
+ # already exists, attempt a backup. Don't worry if it fails,
+ # e.g., if mv doesn't support -f.
+ if test -n "$backupsuffix" && test -f "$dst"; then
+ $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null
+ fi
+
+ # Rename the file to the real destination.
+ $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null ||
+
+ # The rename failed, perhaps because mv can't rename something else
+ # to itself, or perhaps because mv is so ancient that it does not
+ # support -f.
+ {
+ # Now remove or move aside any old file at destination location.
+ # We try this two ways since rm can't unlink itself on some
+ # systems and the destination file might be busy for other
+ # reasons. In this case, the final cleanup might fail but the new
+ # file should still install successfully.
+ {
+ test ! -f "$dst" ||
+ $doit $rmcmd "$dst" 2>/dev/null ||
+ { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null &&
+ { $doit $rmcmd "$rmtmp" 2>/dev/null; :; }
+ } ||
+ { echo "$0: cannot unlink or rename $dst" >&2
+ (exit 1); exit 1
+ }
+ } &&
+
+ # Now rename the file to the real destination.
+ $doit $mvcmd "$dsttmp" "$dst"
+ }
+ fi || exit 1
+
+ trap '' 0
+ fi
+done
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "scriptversion="
+# time-stamp-format: "%:y-%02m-%02d.%02H"
+# time-stamp-time-zone: "UTC0"
+# time-stamp-end: "; # UTC"
+# End:
diff --git a/exec/loader-aarch64.s b/exec/loader-aarch64.s
new file mode 100644
index 00000000000..da8ec1f4977
--- /dev/null
+++ b/exec/loader-aarch64.s
@@ -0,0 +1,187 @@
+// 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/>.
+
+// Notice that aarch64 requires that sp be aligned to 16 bytes while
+// accessing memory from sp, so x20 is used to chase down the load
+// area.
+
+ .section .text
+ .global _start
+_start:
+ //mov x8, 101 // SYS_nanosleep
+ //adr x0, timespec // req
+ //mov x1, #0 // rem
+ //svc #0 // syscall
+ mov x20, sp // x20 = sp
+ ldr x10, [x20] // x10 = original SP
+ add x20, x20, #16 // x20 = start of load area
+ mov x28, #-1 // x28 = secondary fd
+.next_action:
+ ldr x11, [x20] // action number
+ and x12, x11, #-17 // actual action number
+ cbz x12, .open_file // open file?
+ cmp x12, #3 // jump?
+ beq .rest_of_exec
+ cmp x12, #4 // anonymous mmap?
+ beq .do_mmap_anon
+.do_mmap:
+ ldr x0, [x20, 8] // vm_address
+ ldr x1, [x20, 32] // length
+ ldr x2, [x20, 24] // protection
+ ldr x3, [x20, 40] // flags
+ tst x11, #16 // primary fd?
+ mov x4, x29 // primary fd
+ beq .do_mmap_1
+ mov x4, x28 // secondary fd
+.do_mmap_1:
+ mov x8, #222 // SYS_mmap
+ ldr x5, [x20, 16] // file_offset
+ svc #0 // syscall
+ ldr x9, [x20, 8] // length
+ cmp x0, x9 // mmap result
+ bne .perror // print error
+ ldr x3, [x20, 48] // clear
+ add x1, x1, x0 // x1 = vm_address + end
+ sub x3, x1, x3 // x3 = x1 - clear
+ mov x0, #0 // x0 = 0
+.fill64:
+ sub x2, x1, x3 // x2 = x1 - x3
+ cmp x2, #63 // x2 >= 64?
+ ble .fillb // start filling bytes
+ stp x0, x0, [x3] // x3[0] = 0, x3[1] = 0
+ stp x0, x0, [x3, 16] // x3[2] = 0, x3[3] = 0
+ stp x0, x0, [x3, 32] // x3[4] = 0, x3[5] = 0
+ stp x0, x0, [x3, 48] // x3[6] = 0, x3[7] = 0
+ add x3, x3, #64 // x3 += 8
+ b .fill64
+.fillb:
+ cmp x1, x3 // x1 == x3?
+ beq .continue // done
+ strb w0, [x3], #1 // ((char *) x3)++ = 0
+ b .fillb
+.continue:
+ add x20, x20, #56 // next action
+ b .next_action
+.do_mmap_anon:
+ ldr x0, [x20, 8] // vm_address
+ ldr x1, [x20, 32] // length
+ ldr x2, [x20, 24] // protection
+ ldr x3, [x20, 40] // flags
+ mov x4, #-1 // fd
+ b .do_mmap_1
+.open_file:
+ mov x8, #56 // SYS_openat
+ mov x0, #-100 // AT_FDCWD
+ add x1, x20, #8 // file name
+ mov x2, #0 // O_RDONLY
+ mov x3, #0 // mode
+ svc #0 // syscall
+ cmp x0, #-1 // rc < 0?
+ ble .perror
+ mov x19, x1 // x19 == x1
+.nextc:
+ ldrb w2, [x1], #1 // b = *x1++
+ cmp w2, #47 // dir separator?
+ bne .nextc1 // not dir separator
+ mov x19, x1 // x19 = char past separator
+.nextc1:
+ cbnz w2, .nextc // b?
+ add x1, x1, #7 // round up x1
+ and x20, x1, #-8 // mask for round, set x20
+ tst x11, #16 // primary fd?
+ bne .secondary // secondary fd
+ mov x29, x0 // primary fd
+ mov x8, #167 // SYS_prctl
+ mov x0, #15 // PR_SET_NAME
+ mov x1, x19 // basename
+ mov x2, #0 // arg2
+ mov x3, #0 // arg3
+ mov x4, #0 // arg4
+ mov x5, #0 // arg5
+ svc #0 // syscall
+ b .next_action // next action
+.secondary:
+ mov x28, x0 // secondary fd
+ b .next_action // next action.
+.perror:
+ mov x8, #93 // SYS_exit
+ mvn x0, x0 // x1 = ~x0
+ add x0, x0, 1 // x1 += 1
+ svc #0 // exit
+.rest_of_exec:
+ mov x7, x20 // x7 = x20
+ mov x20, x10 // x20 = x10
+ ldr x9, [x20] // argc
+ add x9, x9, #2 // x9 += 2
+ lsl x9, x9, #3 // argc * 8
+ add x20, x20, x9 // now past argv
+.skipenv:
+ ldr x9, [x20], #8 // x9 = *envp++
+ cbnz x9, .skipenv // x9?
+.one_auxv:
+ ldr x9, [x20], #16 // x9 = *sp, sp += 2
+ cbz x9, .cleanup // !x9?
+ cmp x9, #3 // is AT_PHDR?
+ beq .replace_phdr // replace
+ cmp x9, #4 // is AT_PHENT?
+ beq .replace_phent // replace
+ cmp x9, #5 // is AT_PHNUM?
+ beq .replace_phnum // replace
+ cmp x9, #9 // is AT_ENTRY?
+ beq .replace_entry // replace
+ cmp x9, #7 // is AT_BASE?
+ beq .replace_base // replace
+ b .one_auxv // next auxv
+.replace_phdr:
+ ldr x9, [x7, 40] // at_phdr
+ str x9, [x20, -8] // store value
+ b .one_auxv
+.replace_phent:
+ ldr x9, [x7, 24] // at_phent
+ str x9, [x20, -8] // store value
+ b .one_auxv
+.replace_phnum:
+ ldr x9, [x7, 32] // at_phnum
+ str x9, [x20, -8] // store value
+ b .one_auxv
+.replace_entry:
+ ldr x9, [x7, 16] // at_entry
+ str x9, [x20, -8] // store value
+ b .one_auxv
+.replace_base:
+ ldr x9, [x7, 48] // at_base
+ str x9, [x20, -8] // store value
+ b .one_auxv
+.cleanup:
+ cmp x28, #-1 // is secondary fd set?
+ bne .cleanup1 // not set
+ mov x8, #57 // SYS_close
+ mov x0, x28 // secondary fd
+ svc #0 // syscall
+.cleanup1:
+ mov x8, #57 // SYS_close
+ mov x0, x29 // primary fd
+ svc #0 // syscall
+.enter:
+ mov sp, x10 // restore original SP
+ mov x0, #0 // clear rtld_fini
+ ldr x1, [x7, 8] // branch to code
+ br x1
+
+timespec:
+ .quad 10
+ .quad 10
diff --git a/exec/loader-armeabi.s b/exec/loader-armeabi.s
new file mode 100644
index 00000000000..32b2a5268d6
--- /dev/null
+++ b/exec/loader-armeabi.s
@@ -0,0 +1,204 @@
+@ 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/>.
+
+ .section .text
+ .global _start
+_start:
+ @mov r7, #162 @ SYS_nanosleep
+ @adr r0, timespec @ req
+ @mov r1, #0 @ rem
+ @swi #0 @ syscall
+ mov r8, sp @ r8 = sp
+ ldr r9, [r8], #8 @ r9 = original sp, r8 += 8
+ mov r14, #-1 @ r14 = secondary fd
+.next_action:
+ ldr r11, [r8] @ r11 = action number
+ and r12, r11, #-17 @ actual action number
+ cmp r12, #0 @ open file?
+ beq .open_file @ open file.
+ cmp r12, #3 @ jump?
+ beq .rest_of_exec @ jump to code.
+ cmp r12, #4 @ anonymous mmap?
+ beq .do_mmap_anon @ anonymous mmap.
+.do_mmap:
+ add r6, r8, #4 @ r6 = r8 + 4
+ ldm r6!, {r0, r5} @ vm_address, file_offset
+ ldm r6!, {r1, r2} @ protection, length
+ mov r3, r1 @ swap
+ lsr r5, #12 @ divide file offset by page size
+ mov r1, r2 @ swap
+ mov r2, r3 @ swap
+ ldm r6!, {r3, r12} @ flags, clear
+ tst r11, #16 @ primary fd?
+ mov r4, r10 @ primary fd
+ beq .do_mmap_1
+ mov r4, r14 @ secondary fd
+.do_mmap_1:
+ mov r7, #192 @ SYS_mmap2
+ swi #0 @ syscall
+ ldr r2, [r8, #4] @ vm_address
+ cmp r2, r0 @ rc == vm_address?
+ bne .perror
+ add r0, r1, r2 @ r0 = length + vm_address
+ sub r3, r0, r12 @ r3 = r0 - clear
+ mov r1, #0 @ r1 = 0
+.align:
+ cmp r0, r3 @ r0 == r3?
+ beq .continue @ continue
+ tst r3, #3 @ r3 & 3?
+ bne .fill32 @ fill aligned
+ strb r1, [r3], #1 @ fill byte
+ b .align @ align again
+.fill32:
+ sub r2, r0, r3 @ r2 = r0 - r3
+ cmp r2, #31 @ r2 >= 32?
+ ble .fillb @ start filling bytes
+ str r1, [r3], #4 @ *r3++ = 0
+ str r1, [r3], #4 @ *r3++ = 0
+ str r1, [r3], #4 @ *r3++ = 0
+ str r1, [r3], #4 @ *r3++ = 0
+ str r1, [r3], #4 @ *r3++ = 0
+ str r1, [r3], #4 @ *r3++ = 0
+ str r1, [r3], #4 @ *r3++ = 0
+ str r1, [r3], #4 @ *r3++ = 0
+ b .fill32
+.fillb:
+ cmp r0, r3 @ r0 == r3
+ beq .continue @ done
+ strb r1, [r3], #1 @ ((char *) r3)++ = 0
+ b .fillb
+.continue:
+ add r8, r8, #28 @ next action
+ b .next_action
+.do_mmap_anon:
+ add r6, r8, #4 @ r6 = r8 + 4
+ ldm r6!, {r0, r5} @ vm_address, file_offset
+ ldm r6!, {r1, r2} @ protection, length
+ mov r3, r1 @ swap
+ lsr r5, #12 @ divide file offset by page size
+ mov r1, r2 @ swap
+ mov r2, r3 @ swap
+ ldm r6!, {r3, r12} @ flags, clear
+ mov r4, #-1 @ fd
+ b .do_mmap_1
+.open_file:
+ mov r7, #5 @ SYS_open
+ add r0, r8, #4 @ file name
+ mov r1, #0 @ O_RDONLY
+ mov r2, #0 @ mode
+ swi #0 @ syscall
+ cmp r0, #-1 @ r0 <= -1?
+ ble .perror
+ add r8, r8, #4 @ r8 = start of string
+ mov r1, r8 @ r1 = r8
+.nextc:
+ ldrb r2, [r8], #1 @ b = *r0++
+ cmp r2, #47 @ dir separator?
+ bne .nextc1 @ not dir separator
+ mov r1, r8 @ r1 = char past separator
+.nextc1:
+ cmp r2, #0 @ b?
+ bne .nextc @ next character
+ add r8, r8, #3 @ round up r8
+ and r8, r8, #-4 @ mask for round, set r8
+ tst r11, #16 @ primary fd?
+ bne .secondary @ secondary fd
+ mov r10, r0 @ primary fd
+ mov r7, #172 @ SYS_prctl
+ mov r0, #15 @ PR_SET_NAME, r1 = name
+ mov r2, #0 @ arg2
+ mov r3, #0 @ arg3
+ mov r4, #0 @ arg4
+ mov r5, #0 @ arg5
+ swi #0 @ syscall
+ b .next_action @ next action
+.secondary:
+ mov r14, r0 @ secondary fd
+ b .next_action @ next action
+.perror:
+ mov r7, #1 @ SYS_exit
+ mvn r0, r0 @ r0 = ~r0
+ add r0, r0, #1 @ r0 += 1
+ swi #0
+.rest_of_exec:
+ mov r7, r9 @ r7 = original SP
+ ldr r6, [r7] @ argc
+ add r6, r6, #2 @ argc + 2
+ lsl r6, r6, #2 @ argc *= 4
+ add r7, r7, r6 @ now past argv
+.skipenv:
+ ldr r6, [r7], #4 @ r6 = *r7++
+ cmp r6, #0 @ r6?
+ bne .skipenv @ r6?
+.one_auxv:
+ ldr r6, [r7], #8 @ r6 = *r7, r7 += 2
+ cmp r6, #0 @ !r6?
+ beq .cleanup @ r6?
+ cmp r6, #3 @ is AT_PHDR?
+ beq .replace_phdr @ replace
+ cmp r6, #4 @ is AT_PHENT?
+ beq .replace_phent @ replace
+ cmp r6, #5 @ is AT_PHNUM?
+ beq .replace_phnum @ replace
+ cmp r6, #9 @ is AT_ENTRY?
+ beq .replace_entry @ replace
+ cmp r6, #7 @ is AT_BASE?
+ beq .replace_base @ replace
+ b .one_auxv @ next auxv
+.replace_phdr:
+ ldr r6, [r8, #20] @ at_phdr
+ str r6, [r7, #-4] @ store value
+ b .one_auxv
+.replace_phent:
+ ldr r6, [r8, #12] @ at_phent
+ str r6, [r7, #-4] @ store value
+ b .one_auxv
+.replace_phnum:
+ ldr r6, [r8, #16] @ at_phnum
+ str r6, [r7, #-4] @ store value
+ b .one_auxv
+.replace_entry:
+ ldr r6, [r8, #8] @ at_entry
+ str r6, [r7, #-4] @ store value
+ b .one_auxv
+.replace_base:
+ ldr r6, [r8, #24] @ at_base
+ str r6, [r7, #-4] @ store value
+ b .one_auxv
+.cleanup:
+ cmp r14, #-1 @ secondary fd set?
+ bne .cleanup1 @ not set
+ mov r7, #6 @ SYS_close
+ mov r0, r14 @ secondary fd
+ swi #0 @ syscall
+.cleanup1:
+ mov r7, #6 @ SYS_close
+ mov r0, r10 @ primary fd
+ swi #0 @ syscall
+.enter:
+ mov sp, r9 @ restore original SP
+ mov r0, #0 @ clear rtld_fini
+ ldr r1, [r8, #4] @ branch to code
+ bx r1
+
+timespec:
+ .long 10
+ .long 10
+
+@ Local Variables:
+@ asm-comment-char: 64
+@ End:
diff --git a/exec/loader-mips64el.s b/exec/loader-mips64el.s
new file mode 100644
index 00000000000..00a2765a9b6
--- /dev/null
+++ b/exec/loader-mips64el.s
@@ -0,0 +1,234 @@
+# 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-mips.m4')
+
+ .set noreorder # delay slots managed by hand
+ .set noat # no assembler macros
+ .section .text
+ .global __start
+__start:
+dnl li $v0, 5034 # SYS_nanosleep
+dnl dla $a0, .timespec # rqtp
+dnl li $a1, 0 # rmtp
+dnl syscall # syscall
+ ld $s2, ($sp) # original stack pointer
+ DADDI3( $s0, $sp, 16) # start of load area
+ DADDI2( $sp, -16) # primary fd, secondary fd
+ li $t0, -1 # secondary fd
+ sd $t0, 8($sp) # initialize secondary fd
+.next_action:
+ ld $s1, ($s0) # action number
+ andi $t0, $s1, 15 # t0 = action number & 15
+ beqz $t0, .open_file # open file?
+ nop # delay slot
+ DADDI2( $t0, -3) # t0 -= 3
+ beqz $t0, .rest_of_exec # jump to code
+ nop # delay slot
+ li $t1, 1
+ beq $t0, $t1, .do_mmap_anon # anonymous mmap?
+ nop # delay slot
+.do_mmap:
+ ld $t0, 8($s0) # vm address
+ ld $t1, 16($s0) # file_offset
+ ld $t2, 24($s0) # protection
+ ld $t3, 32($s0) # length
+ ld $v0, 40($s0) # flags
+ ld $v1, ($sp) # primary fd
+ andi $s3, $s1, 16 # s1 & 16?
+ beqz $s3, .do_mmap_1 # secondary fd?
+ nop # delay slot
+ ld $v1, 8($sp) # secondary fd
+.do_mmap_1:
+ move $a0, $t0 # syscall arg
+ move $a1, $t3 # syscall arg
+ move $a2, $t2 # syscall arg
+ move $a3, $v0 # syscall arg
+ move $a4, $v1 # syscall arg
+ move $a5, $t1 # syscall arg
+ li $v0, 5009 # SYS_mmap
+ syscall # syscall
+ bne $a3, $zero, .perror # perror?
+ nop # delay slot
+ ld $t1, 48($s0) # clear
+ dadd $t0, $a0, $a1 # t0 = end of mapping
+ dsub $t1, $t0, $t1 # t1 = t0 - clear
+.align:
+ beq $t0, $t1, .continue # already finished
+ nop # delay slot
+ andi $t2, $t1, 7 # t1 & 7?
+ bnez $t2, .filld # start filling longs
+ nop # delay slot
+.filld:
+ dsub $t2, $t0, $t1 # t2 = t0 - t1
+ sltiu $t2, $t2, 64 # t2 < 64?
+ bne $t2, $zero, .fillb # fill bytes
+ nop # delay slot
+ sd $zero, ($t1) # zero doubleword
+ DADDI2( $t1, 8) # next doubleword
+ sd $zero, ($t1) # zero doubleword
+ DADDI2( $t1, 8) # next doubleword
+ sd $zero, ($t1) # zero doubleword
+ DADDI2( $t1, 8) # next doubleword
+ sd $zero, ($t1) # zero doubleword
+ DADDI2( $t1, 8) # next doubleword
+ sd $zero, ($t1) # zero doubleword
+ DADDI2( $t1, 8) # next doubleword
+ sd $zero, ($t1) # zero doubleword
+ DADDI2( $t1, 8) # next doubleword
+ sd $zero, ($t1) # zero doubleword
+ DADDI2( $t1, 8) # next doubleword
+ sd $zero, ($t1) # zero doubleword
+ DADDI2( $t1, 8) # next doubleword
+ j .filld # fill either doubleword or byte
+ nop # delay slot
+.fillb:
+ beq $t0, $t1, .continue # already finished?
+ nop # delay slot
+ sb $zero, ($t1) # clear byte
+ DADDI2( $t1, 1) # t1++
+.continue:
+ DADDI2( $s0, 56) # s0 = next action
+ j .next_action # next action
+ nop # delay slot
+.do_mmap_anon:
+ ld $t0, 8($s0) # vm address
+ ld $t1, 16($s0) # file_offset
+ ld $t2, 24($s0) # protection
+ ld $t3, 32($s0) # length
+ ld $v0, 40($s0) # flags
+ li $v1, -1 # fd
+ j .do_mmap_1 # do mmap
+ nop # branch delay slot
+.open_file:
+ li $v0, 5002 # SYS_open
+ DADDI3( $a0, $s0, 8) # start of name
+ move $a1, $zero # flags = O_RDONLY
+ move $a2, $zero # mode = 0
+ syscall # syscall
+ bne $a3, $zero, .perror # perror
+ nop # delay slot
+ DADDI2( $s0, 8) # start of string
+ move $t3, $s0 # t3 = s0
+.nextc:
+ lb $t0, ($s0) # load byte
+ DADDI2( $s0, 1) # s0++
+ li $t1, 47 # directory separator `/'
+ bne $t0, $t1, .nextc1 # is separator char?
+ nop # delay slot
+ move $t3, $s0 # t3 = char past separator
+.nextc1:
+ bnez $t0, .nextc # next character?
+ nop # delay slot
+ DADDI2( $s0, 7) # adjust for round
+ li $t2, -8 # t2 = -8
+ and $s0, $s0, $t2 # mask for round
+ andi $t0, $s1, 16 # t1 = s1 & 16
+ move $t1, $sp # address of primary fd
+ beqz $t0, .primary # primary fd?
+ nop # delay slot
+ DADDI2( $t1, 8) # address of secondary fd
+ sd $v0, ($t1) # store fd
+ j .next_action # next action
+ nop # delay slot
+.primary:
+ sd $v0, ($t1) # store fd
+ li $v0, 5153 # SYS_prctl
+ li $a0, 15 # PR_SET_NAME
+ move $a1, $t3 # char past separator
+ move $a2, $zero # a2
+ move $a3, $zero # a3
+ move $a4, $zero # a4
+ move $a5, $zero # a5
+ syscall # syscall
+ j .next_action # next action
+ nop # delay slot
+.perror:
+ move $a0, $v0 # errno
+ li $v0, 5058 # SYS_exit
+ syscall # syscall
+.rest_of_exec:
+ move $s1, $s2 # original SP
+ ld $t0, ($s1) # argc
+ dsll $t0, $t0, 3 # argc *= 3
+ DADDI2( $t0, 16) # argc += 16
+ dadd $s1, $s1, $t0 # s1 = start of envp
+.skipenv:
+ ld $t0, ($s1) # t0 = *s1
+ DADDI2( $s1, 8) # s1++
+ bne $t0, $zero, .skipenv # skip again
+ nop # delay slot
+ dla $t3, .auxvtab # address of auxv table
+.one_auxv:
+ ld $t0, ($s1) # t0 = auxv type
+ li $t1, 10 # t1 = 10
+ beqz $t0, .finish # is AT_IGNORE?
+ nop # delay slot
+ sltu $t1, $t0, $t1 # t1 = t0 < num offsets
+ beqz $t1, .next # next auxv
+ nop # delay slot
+ dsll $t1, $t0, 2 # t1 = t0 * 4
+ dadd $t1, $t3, $t1 # t1 = .auxvtab + t1
+ lw $t2, ($t1) # t2 = *t1
+ beqz $t2, .next # skip auxv
+ nop # delay slot
+ dadd $t2, $s0, $t2 # t2 = s0 + t2
+ ld $t2, ($t2) # t2 = *t2
+ sd $t2, 8($s1) # set auxv value
+.next:
+ DADDI2( $s1, 16) # next auxv
+ j .one_auxv # next auxv
+ nop # delay slot
+.finish:
+ ld $t0, 8($sp) # secondary fd
+ li $t1, -1 # t1 = -1
+ ld $s1, ($sp) # s1 = primary fd
+ li $v0, 5003 # SYS_close
+ beq $t0, $t2, .finish1 # secondary fd set?
+ nop # delay slot
+ move $a0, $t0 # secondary fd
+ syscall # syscall
+ li $v0, 5003 # SYS_close
+.finish1:
+ move $a0, $s1 # primary fd
+ syscall # syscall
+.jump:
+ move $v0, $zero # rtld_fini
+ ld $t0, 8($s0) # entry
+ move $sp, $s2 # restore stack pointer, delay slot
+ jr $t0 # enter
+ nop # delay slot
+
+.auxvtab:
+ .long 0 # 0
+ .long 0 # 1
+ .long 0 # 2
+ .long 40 # 3 AT_PHDR
+ .long 24 # 4 AT_PHENT
+ .long 32 # 5 AT_PHNUM
+ .long 0 # 6
+ .long 48 # 7 AT_BASE
+ .long 0 # 8
+ .long 16 # 9 AT_ENTRY
+
+.timespec:
+ .quad 10
+ .quad 10
+
+# Local Variables:
+# asm-comment-char: 35
+# End:
diff --git a/exec/loader-mipsel.s b/exec/loader-mipsel.s
new file mode 100644
index 00000000000..baba3f05a94
--- /dev/null
+++ b/exec/loader-mipsel.s
@@ -0,0 +1,236 @@
+# 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-mips.m4')
+
+# Make sure not to use t4 through t7, in order to maintain portability
+# with N32 ABI systems.
+
+ .set noreorder # delay slots managed by hand
+ .section .text
+ .global __start
+__start:
+dnl li $v0, SYSCALL_nanosleep # SYS_nanosleep
+dnl la $a0, .timespec # rqtp
+dnl li $a1, 0 # rmtp
+dnl syscall # syscall
+ lw $s6, ($sp) # original stack pointer
+ addi $s0, $sp, 8 # start of load area
+ addi $sp, -8 # primary fd, secondary fd
+ li $t0, -1 # secondary fd
+ sw $t0, 4($sp) # initialize secondary fd
+.next_action:
+ lw $s2, ($s0) # action number
+ nop # delay slot
+ andi $t0, $s2, 15 # t0 = s2 & 15
+ beqz $t0, .open_file # open file?
+ li $t1, 3 # t1 = 3, delay slot
+ beq $t0, $t1, .rest_of_exec # jump to code
+ li $t1, 4 # t1 = 4, delay slot
+ beq $t0, $t1, .do_mmap_anon # anonymous mmap
+.do_mmap:
+ lw $a0, 4($s0) # vm_address, delay slot
+ lw $v1, 8($s0) # file_offset
+ lw $a2, 12($s0) # protection
+ lw $a1, 16($s0) # length
+ lw $a3, 20($s0) # flags
+ lw $v0, ($sp) # primary fd
+ andi $t1, $s2, 16 # t1 = s2 & 16
+ beqz $t1, .do_mmap_1 # secondary fd?
+ nop # delay slot
+ lw $v0, 4($sp) # secondary fd
+ nop # delay slot
+.do_mmap_1:
+SYSCALL(`$v0',`$v1',`$zero',`$zero') # syscall args
+ li $v0, SYSCALL_mmap # SYS_mmap
+ syscall # syscall
+ bne $a3, $zero, .perror # perror
+RESTORE() # delay slot, restore sp
+ lw $s5, 24($s0) # clear
+ add $t0, $a0, $a1 # t0 = length + vm_address, delay slot
+ sub $t1, $t0, $s5 # t1 = t0 - clear
+.align:
+ beq $t0, $t1, .continue # already finished?
+ nop # delay slot
+ andi $t2, $t1, 3 # t1 & 3?
+ bnez $t2, .fillw # start filling longs
+ nop # delay slot
+ sb $zero, ($t1) # clear byte
+ addi $t1, $t1, 1 # t1++
+ j .align # continue
+ nop # delay slot
+.fillw:
+ sub $t2, $t0, $t1 # t2 = t0 - t1
+ sltiu $t2, $t2, 32 # r2 < 32?
+ bne $t2, $zero, .fillb # fill bytes
+ nop # delay slot
+ sw $zero, ($t1) # zero word
+ addi $t1, $t1, 4 # next word
+ sw $zero, ($t1) # zero word
+ addi $t1, $t1, 4 # next word
+ sw $zero, ($t1) # zero word
+ addi $t1, $t1, 4 # next word
+ sw $zero, ($t1) # zero word
+ addi $t1, $t1, 4 # next word
+ sw $zero, ($t1) # zero word
+ addi $t1, $t1, 4 # next word
+ sw $zero, ($t1) # zero word
+ addi $t1, $t1, 4 # next word
+ sw $zero, ($t1) # zero word
+ addi $t1, $t1, 4 # next word
+ sw $zero, ($t1) # zero word
+ addi $t1, $t1, 4 # next word
+ j .fillw # fill either word or byte
+ nop # delay slot
+.fillb:
+ beq $t0, $t1, .continue # already finished?
+ nop # delay slot
+ sb $zero, ($t1) # clear byte
+ addi $t1, $t1, 1 # t1++
+.continue:
+ addi $s0, $s0, 28 # s0 = next action
+ j .next_action # next action
+ nop # delay slot
+.do_mmap_anon:
+ lw $v1, 8($s0) # file_offset
+ lw $a2, 12($s0) # protection
+ lw $a1, 16($s0) # length
+ lw $a3, 20($s0) # flags
+ li $t4, -1 # fd
+ j .do_mmap_1 # do mmap
+ nop # delay slot
+.open_file:
+ li $v0, SYSCALL_open # SYS_open
+ addi $a0, $s0, 4 # start of name
+ move $a1, $zero # flags = O_RDONLY
+ move $a2, $zero # mode = 0
+ syscall # syscall
+ bne $a3, $zero, .perror # perror
+ addi $s0, $s0, 4 # start of string, delay slot
+ move $t3, $s0 # t3 = char past separator
+.nextc:
+ lb $t0, ($s0) # load byte
+ addi $s0, $s0, 1 # s0++
+ li $t1, 47 # directory separator `/'
+ bne $t0, $t1, .nextc1 # is separator char?
+ nop # delay slot
+ move $t3, $s0 # t3 = char past separator
+.nextc1:
+ bnez $t0, .nextc # next character?
+ nop # delay slot
+ addi $s0, $s0, 3 # adjust for round
+ li $t2, -4 # t2 = -4
+ and $s0, $s0, $t2 # mask for round
+ andi $t0, $s2, 16 # t1 = s2 & 16
+ beqz $t0, .primary # primary fd?
+ move $t0, $sp # address of primary fd, delay slot
+ addi $t0, $t0, 4 # address of secondary fd
+ j .next_action # next action
+.primary:
+ sw $v0, ($t0) # store fd, delay slot
+ li $v0, SYSCALL_prctl # SYS_prctl
+ li $a0, 15 # PR_SET_NAME
+ move $a1, $t3 # name
+ move $a2, $zero # arg1
+ move $a3, $zero # arg2
+SYSCALL(`$a2',`$a2',`$a2',`$a2') # syscall args
+ syscall # syscall
+RESTORE() # restore sp
+ j .next_action # next action
+ nop # delay slot
+.perror:
+ move $a0, $v0 # errno
+ li $v0, SYSCALL_exit # SYS_exit
+ syscall # syscall
+.rest_of_exec:
+ move $s1, $s6 # s1 = original SP
+ lw $t0, ($s1) # argc
+ nop # delay slot
+ sll $t0, $t0, 2 # argc *= 4
+ addi $t0, $t0, 8 # argc += 8
+ add $s1, $s1, $t0 # s1 = start of envp
+.skipenv:
+ lw $t0, ($s1) # t0 = *s1
+ addi $s1, $s1, 4 # s1++
+ bne $t0, $zero, .skipenv # skip again
+ nop # delay slot
+ la $s2, .auxvtab # address of auxv table
+.one_auxv:
+ lw $t0, ($s1) # t0 = auxv type
+ li $t1, 10 # t1 = 10, delay slot
+ beqz $t0, .finish # is AT_IGNORE?
+ sltu $t1, $t0, $t1 # t1 = t0 < num offsets, delay slot
+ beq $t1, $zero, .next # next auxv
+ sll $t1, $t0, 2 # t1 = t0 * 4, delay slot
+ add $t1, $s2, $t1 # t1 = .auxvtab + t1
+ lw $t2, ($t1) # t2 = *t1
+ nop # delay slot
+ beqz $t2, .next # skip auxv
+ add $t2, $s0, $t2 # t2 = s0 + t2
+ lw $t2, ($t2) # t2 = *t2
+ nop # delay slot
+ sw $t2, 4($s1) # set auxv value
+.next:
+ addi $s1, $s1, 8 # next auxv
+ j .one_auxv # next auxv
+ nop # delay slot
+.finish:
+ lw $t0, 4($sp) # secondary fd
+ lw $s1, ($sp) # primary fd, delay slot, preserved
+ li $t2, -1 # immediate -1
+ beq $t0, $t2, .finish1 # secondary fd set?
+ li $v0, SYSCALL_close # SYS_close, delay slot
+ move $a0, $t0 # fd
+ syscall # syscall
+ li $v0, SYSCALL_close # SYS_close
+.finish1:
+ move $a0, $s1 # primary fd
+ syscall # syscall
+ li $v0, SYSCALL_prctl # SYS_prctl
+ li $a0, 45 # PR_SET_FP_MODE
+ lw $a1, 28($s0) # fpu_mode
+ move $a2, $zero # arg3
+ move $a3, $zero # arg4
+SYSCALL(`$a2',`$a2',`$a2',`$a2') # syscall args
+ syscall # syscall
+RESTORE() # restore sp
+.jump:
+ move $v0, $zero # rtld_fini
+ lw $t0, 4($s0) # entry
+ move $sp, $s6 # restore stack pointer, delay slot
+ jr $t0 # enter
+ nop # delay slot
+
+.auxvtab:
+ .long 0 # 0
+ .long 0 # 1
+ .long 0 # 2
+ .long 20 # 3 AT_PHDR
+ .long 12 # 4 AT_PHENT
+ .long 16 # 5 AT_PHNUM
+ .long 0 # 6
+ .long 24 # 7 AT_BASE
+ .long 0 # 8
+ .long 8 # 9 AT_ENTRY
+
+.timespec:
+ .long 10
+ .long 10
+
+# Local Variables:
+# asm-comment-char: 35
+# End:
diff --git a/exec/loader-x86.s b/exec/loader-x86.s
new file mode 100644
index 00000000000..6329e7f33b1
--- /dev/null
+++ b/exec/loader-x86.s
@@ -0,0 +1,203 @@
+define(`CC', `
+dnl')
+
+CC Copyright (C) 2023 Free Software Foundation, Inc.
+CC
+CC This file is part of GNU Emacs.
+CC
+CC GNU Emacs is free software: you can redistribute it and/or modify
+CC it under the terms of the GNU General Public License as published
+CC by the Free Software Foundation, either version 3 of the License,
+CC or (at your option) any later version.
+CC
+CC GNU Emacs is distributed in the hope that it will be useful, but
+CC WITHOUT ANY WARRANTY; without even the implied warranty of
+CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+CC General Public License for more details.
+CC
+CC You should have received a copy of the GNU General Public License
+CC along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+ .section .text
+ .global _start
+_start:
+dnl movl $162, %eax CC SYS_nanosleep
+dnl leal timespec, %ebx
+dnl xorl %ecx, %ecx
+dnl int $0x80
+ leal 8(%esp), %ebp CC ebp = start of load area
+ subl $8, %esp CC (%esp) = primary fd, 4(%esp) = secondary fd
+ movl $-1, 4(%esp)
+.next_action:
+ movl (%ebp), %edx CC edx = action number
+ andl $-17, %edx
+ cmpl $0, %edx CC open file?
+ je .open_file
+ cmpl $3, %edx CC jump?
+ je .rest_of_exec
+ cmpl $4, %edx CC anonymous mmap?
+ je .do_mmap_anon
+.do_mmap:
+ subl $24, %esp
+ movl $90, %eax CC SYS_old_mmap
+ movl %esp, %ebx
+ movl 4(%ebp), %ecx CC address
+ movl %ecx, (%esp)
+ movl 16(%ebp), %ecx CC length
+ movl %ecx, 4(%esp)
+ movl 12(%ebp), %ecx CC protection
+ movl %ecx, 8(%esp)
+ movl 20(%ebp), %ecx CC flags
+ movl %ecx, 12(%esp)
+ testl $16, (%ebp) CC primary?
+ movl 28(%esp), %ecx
+ cmovzl 24(%esp), %ecx
+ movl %ecx, 16(%esp) CC fd
+ movl 8(%ebp), %ecx CC offset
+ movl %ecx, 20(%esp)
+.do_mmap_1:
+ int $0x80
+ addl $24, %esp CC restore esp
+ cmpl $-1, %eax CC mmap failed?
+ je .perror
+ movl 24(%ebp), %ecx CC clear
+ testl %ecx, %ecx
+ jz .continue
+ movl 4(%ebp), %esi CC start of mapping
+ addl 16(%ebp), %esi CC end of mapping
+ subl %ecx, %esi CC start of clear area
+.again:
+ testl %ecx, %ecx
+ jz .continue
+ subl $1, %ecx
+ movb $0, (%esi, %ecx, 1)
+ jmp .again
+.continue:
+ leal 28(%ebp), %ebp
+ jmp .next_action
+.do_mmap_anon:
+ subl $24, %esp
+ movl $90, %eax CC SYS_old_mmap
+ movl %esp, %ebx
+ movl 4(%ebp), %ecx CC address
+ movl %ecx, (%esp)
+ movl 16(%ebp), %ecx CC length
+ movl %ecx, 4(%esp)
+ movl 12(%ebp), %ecx CC protection
+ movl %ecx, 8(%esp)
+ movl 20(%ebp), %ecx CC flags
+ movl %ecx, 12(%esp)
+ movl $-1, 16(%esp) CC fd
+ movl 8(%ebp), %ecx CC offset
+ movl %ecx, 20(%esp)
+ jmp .do_mmap_1
+.open_file:
+ movl $5, %eax CC SYS_open
+ leal 4(%ebp), %ebx CC ebx = %esp + 8
+ pushl %ebx
+ xorl %ecx, %ecx CC flags = O_RDONLY
+ xorl %edx, %edx CC mode = 0
+ int $0x80
+ cmpl $-1, %eax CC open failed?
+ jle .perror
+ movl %ebp, %esi CC (esi) = original action number
+ popl %ebp CC ebp = start of string
+ movl %ebp, %ecx CC char past separator
+ decl %ebp
+.nextc:
+ incl %ebp
+ movb (%ebp), %dl CC dl = *ebp
+ cmpb $47, %dl CC dl == '\?'?
+ jne .nextc1
+ leal 1(%ebp), %ecx CC ecx = char past separator
+.nextc1:
+ cmpb $0, %dl CC dl == 0?
+ jne .nextc
+ addl $4, %ebp CC adjust past ebp prior to rounding
+ andl $-4, %ebp CC round ebp up to the next long
+ testl $16, (%esi) CC original action number & 16?
+ jz .primary
+ movl %eax, 4(%esp) CC secondary fd = eax
+ jmp .next_action
+.primary:
+ pushl %ebp
+ xorl %esi, %esi CC arg3
+ movl %eax, 4(%esp) CC primary fd = eax
+ xorl %edx, %edx CC arg2
+ movl $15, %ebx CC PR_SET_NAME, arg1 = ecx
+ xorl %edi, %edi CC arg4
+ movl $172, %eax CC SYS_prctl
+ xorl %ebp, %ebp CC arg5
+ int $0x80 CC syscall
+ popl %ebp
+ jmp .next_action
+.perror:
+ movl %eax, %ebx
+ negl %ebx
+ movl $1, %eax
+ int $0x80
+.rest_of_exec:
+ movl 8(%esp), %ecx CC ecx = original stack pointer
+ movl (%ecx), %esi CC esi = argc
+ leal 8(%ecx, %esi, 4), %ecx CC ecx = start of environ
+.skip_environ:
+ movl (%ecx), %esi CC envp[N]
+ addl $4, %ecx
+ testl %esi, %esi CC envp[n] ?
+ jnz .skip_environ CC otherwise, esi is now at the start of auxv
+.one_auxv:
+ movl (%ecx), %esi CC auxv type
+ leal 8(%ecx), %ecx CC skip to next auxv
+ testl %esi, %esi CC is 0?
+ jz .cleanup
+ cmpl $3, %esi CC is AT_PHDR
+ je .replace_phdr
+ cmpl $4, %esi CC is AT_PHENT?
+ je .replace_phent
+ cmpl $5, %esi CC is AT_PHNUM?
+ je .replace_phnum
+ cmpl $9, %esi CC is AT_ENTRY?
+ je .replace_entry
+ cmpl $7, %esi CC is AT_BASE
+ je .replace_base
+ jmp .one_auxv
+.replace_phdr:
+ movl 20(%ebp), %esi
+ movl %esi, -4(%ecx)
+ jmp .one_auxv
+.replace_phent:
+ movl 12(%ebp), %esi
+ movl %esi, -4(%ecx)
+ jmp .one_auxv
+.replace_phnum:
+ movl 16(%ebp), %esi
+ movl %esi, -4(%ecx)
+ jmp .one_auxv
+.replace_entry:
+ movl 8(%ebp), %esi
+ movl %esi, -4(%ecx)
+ jmp .one_auxv
+.replace_base:
+ movl 24(%ebp), %esi
+ movl %esi, -4(%ecx)
+ jmp .one_auxv
+.cleanup:
+ movl $6, %eax CC SYS_close
+ cmpl $-1, 4(%esp) CC see if interpreter fd is set
+ je .cleanup_1
+ movl 4(%esp), %ebx
+ int $0x80
+ movl $6, %eax CC SYS_close
+.cleanup_1:
+ movl (%esp), %ebx
+ int $0x80
+.enter:
+ pushl $0
+ popfl CC restore floating point state
+ movl 8(%esp), %esp CC restore initial stack pointer
+ xorl %edx, %edx CC clear rtld_fini
+ jmpl *4(%ebp) CC entry
+
+timespec:
+ .long 10
+ .long 10
diff --git a/exec/loader-x86_64.s b/exec/loader-x86_64.s
new file mode 100644
index 00000000000..acba609b202
--- /dev/null
+++ b/exec/loader-x86_64.s
@@ -0,0 +1,195 @@
+define(`CC', `
+dnl')
+
+CC Copyright (C) 2023 Free Software Foundation, Inc.
+CC
+CC This file is part of GNU Emacs.
+CC
+CC GNU Emacs is free software: you can redistribute it and/or modify
+CC it under the terms of the GNU General Public License as published
+CC by the Free Software Foundation, either version 3 of the License,
+CC or (at your option) any later version.
+CC
+CC GNU Emacs is distributed in the hope that it will be useful, but
+CC WITHOUT ANY WARRANTY; without even the implied warranty of
+CC MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+CC General Public License for more details.
+CC
+CC You should have received a copy of the GNU General Public License
+CC along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+ .section .text
+ .global _start
+_start:
+dnl movq $35, %rax CC SYS_nanosleep
+dnl leaq timespec(%rip), %rdi
+dnl xorq %rsi, %rsi
+dnl syscall
+ popq %r13 CC original SP
+ popq %r15 CC size of load area.
+ movq $-1, %r12 CC r12 is the interpreter fd
+.next_action:
+ movq (%rsp), %r14 CC action number
+ movq %r14, %r15 CC original action number
+ andq $-17, %r14
+ cmpq $0, %r14 CC open file?
+ je .open_file
+ cmpq $3, %r14 CC jump?
+ je .rest_of_exec
+ cmpq $4, %r14 CC anonymous mmap?
+ je .do_mmap_anon
+.do_mmap:
+ movq $9, %rax CC SYS_mmap
+ movq 8(%rsp), %rdi CC address
+ movq 16(%rsp), %r9 CC offset
+ movq 24(%rsp), %rdx CC protection
+ movq 32(%rsp), %rsi CC length
+ movq 40(%rsp), %r10 CC flags
+ CC set r8 to the primary fd unless r15 & 16
+ testq $16, %r15
+ movq %r12, %r8
+ cmovzq %rbx, %r8
+.do_mmap_1:
+ syscall
+ cmpq $-1, %rax CC mmap failed
+ je .perror
+ movq 48(%rsp), %r9 CC clear
+ testq %r9, %r9
+ jz .continue
+ movq 8(%rsp), %r10 CC start of mapping
+ addq 32(%rsp), %r10 CC end of mapping
+ subq %r9, %r10 CC start of clear area
+.again:
+ testq %r9, %r9
+ jz .continue
+ subq $1, %r9
+ movb $0, (%r10, %r9, 1)
+ jmp .again
+.continue:
+ leaq 56(%rsp), %rsp
+ jmp .next_action
+.do_mmap_anon:
+ movq $9, %rax CC SYS_mmap
+ movq 8(%rsp), %rdi CC address
+ movq 16(%rsp), %r9 CC offset
+ movq 24(%rsp), %rdx CC protection
+ movq 32(%rsp), %rsi CC length
+ movq 40(%rsp), %r10 CC flags
+ movq $-1, %r8 CC -1
+ jmp .do_mmap_1
+.open_file:
+ movq $2, %rax CC SYS_open
+ leaq 8(%rsp), %rdi CC rdi = %rsp + 8
+ xorq %rsi, %rsi CC flags = O_RDONLY
+ xorq %rdx, %rdx CC mode = 0
+ syscall
+ cmpq $-1, %rax CC open failed
+ jle .perror
+ movq %rdi, %rsp CC rsp = start of string
+ subq $1, %rsp
+ movq %rsp, %r14 CC r14 = start of string
+.nextc:
+ addq $1, %rsp
+ movb (%rsp), %dil CC rdi = *rsp
+ cmpb $47, %dil CC *rsp == '/'?
+ jne .nextc1
+ movq %rsp, %r14 CC r14 = rsp
+ addq $1, %r14 CC r14 = char past separator
+.nextc1:
+ cmpb $0, %dil CC *rsp == 0?
+ jne .nextc
+ addq $8, %rsp CC adjust past rsp prior to rounding
+ andq $-8, %rsp CC round rsp up to the next quad
+ testq $16, %r15 CC r15 & 16?
+ jz .primary
+ movq %rax, %r12 CC otherwise, move fd to r12
+ jmp .next_action
+.primary:
+ movq %rax, %rbx CC if not, move fd to rbx
+ movq $157, %rax CC SYS_prctl
+ movq $15, %rdi CC PR_SET_NAME
+ movq %r14, %rsi CC arg1
+ xorq %rdx, %rdx CC arg2
+ xorq %r10, %r10 CC arg3
+ xorq %r8, %r8 CC arg4
+ xorq %r9, %r9 CC arg5
+ syscall
+ jmp .next_action
+.perror:
+ movq %rax, %r12 CC error code
+ negq %r12
+ movq $1, %rax CC SYS_write
+ movq $1, %rdi CC stdout
+ leaq error(%rip), %rsi CC buffer
+ movq $23, %rdx CC count
+ syscall
+ movq $60, %rax CC SYS_exit
+ movq %r12, %rdi CC code
+ syscall
+.rest_of_exec: CC rsp now points to six quads:
+ movq %rsp, %r8 CC now, they are r8
+ movq %r13, %rsp CC restore SP
+ popq %r10 CC argc
+ leaq 8(%rsp,%r10,8), %rsp CC now at start of environ
+.skip_environ:
+ popq %r10 CC envp[N]
+ testq %r10, %r10 CC envp[n]?
+ jnz .skip_environ CC otherwise, rsp is now at the start of auxv
+.one_auxv:
+ popq %rcx CC auxv type
+ addq $8, %rsp CC skip value
+ testq %rcx, %rcx CC is 0?
+ jz .cleanup
+ cmpq $3, %rcx CC is AT_PHDR?
+ je .replace_phdr
+ cmpq $4, %rcx CC is AT_PHENT?
+ je .replace_phent
+ cmpq $5, %rcx CC is AT_PHNUM?
+ je .replace_phnum
+ cmpq $9, %rcx CC is AT_ENTRY?
+ je .replace_entry
+ cmpq $7, %rcx CC is AT_BASE?
+ je .replace_base
+ jmp .one_auxv
+.replace_phdr:
+ movq 40(%r8), %r9
+ movq %r9, -8(%rsp) CC set at_phdr
+ jmp .one_auxv
+.replace_phent:
+ movq 24(%r8), %r9
+ movq %r9, -8(%rsp) CC set at_phent
+ jmp .one_auxv
+.replace_phnum:
+ movq 32(%r8), %r9
+ movq %r9, -8(%rsp) CC set at_phnum
+ jmp .one_auxv
+.replace_entry:
+ movq 16(%r8), %r9
+ movq %r9, -8(%rsp) CC set at_entry
+ jmp .one_auxv
+.replace_base:
+ movq 48(%r8), %r9
+ movq %r9, -8(%rsp) CC set at_base
+ jmp .one_auxv
+.cleanup:
+ movq $3, %rax CC SYS_close
+ cmpq $-1, %r12 CC see if interpreter fd is set
+ je .cleanup_1
+ movq %r12, %rdi
+ syscall
+ movq $3, %rax CC SYS_close
+.cleanup_1:
+ movq %rbx, %rdi
+ syscall
+.enter:
+ pushq $0
+ popfq CC clear FP state
+ movq %r13, %rsp CC restore SP
+ xorq %rdx, %rdx CC clear rtld_fini
+ jmpq *8(%r8) CC entry
+
+error:
+ .ascii "_start: internal error."
+timespec:
+ .quad 10
+ .quad 10
diff --git a/exec/mipsel-user.h b/exec/mipsel-user.h
new file mode 100644
index 00000000000..dc3f98eb4e7
--- /dev/null
+++ b/exec/mipsel-user.h
@@ -0,0 +1,43 @@
+/* Program execution for Emacs.
+
+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/>. */
+
+
+
+#ifndef _MIPSEL_USER_H_
+#define _MIPSEL_USER_H_
+
+#include <sys/user.h>
+
+#ifndef ELF_NGREG
+#define ELF_NGREG 45
+#endif /* ELF_NGREG */
+
+
+
+/* This file defines a structure containing user mode general purpose
+ registers on 32-bit mipsel systems. */
+
+struct mipsel_regs
+{
+ /* General purpose registers. */
+ uint64_t gregs[ELF_NGREG];
+};
+
+#endif /* _MIPSEL_USER_H_ */
+
diff --git a/exec/mipsfpu.c b/exec/mipsfpu.c
new file mode 100644
index 00000000000..f5fa5720804
--- /dev/null
+++ b/exec/mipsfpu.c
@@ -0,0 +1,289 @@
+/* Program execution for Emacs.
+
+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 <errno.h>
+
+#include "mipsfpu.h"
+
+
+
+/* OABI MIPS systems support several different modes of execution.
+ Each mode differs in the size and utilization of the hardware
+ floating-point registers.
+
+ Linux normally sets the floating point mode to one appropriate for
+ execution, taking into account the floating point modes of the
+ interpreter and executable binaries. However, this logic is
+ forsaken when the `execve' system call is overwritten.
+
+ Thus, the correct floating point mode must be determined and set
+ within the loader binary. */
+
+
+
+/* Various constants used throughout this code. */
+
+#define MIPS_ABI_FP_ANY 0 /* FP ABI doesn't matter */
+#define MIPS_ABI_FP_DOUBLE 1 /* -mdouble-float */
+#define MIPS_ABI_FP_SINGLE 2 /* -msingle-float */
+#define MIPS_ABI_FP_SOFT 3 /* -msoft-float */
+#define MIPS_ABI_FP_OLD_64 4 /* -mips32r2 -mfp64 */
+#define MIPS_ABI_FP_XX 5 /* -mfpxx */
+#define MIPS_ABI_FP_64 6 /* -mips32r2 -mfp64 */
+#define MIPS_ABI_FP_64A 7 /* -mips32r2 -mfp64 -mno-odd-spreg */
+
+#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */
+#define EF_MIPS_PIC 2 /* Contains PIC code. */
+#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */
+#define EF_MIPS_XGOT 8
+#define EF_MIPS_64BIT_WHIRL 16
+#define EF_MIPS_ABI2 32
+#define EF_MIPS_ABI_ON32 64
+#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */
+#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */
+#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */
+
+
+
+/* Structure describing the requirements of a single floating-point
+ ABI. */
+
+struct mode_description
+{
+ /* Whether or not the ABI only executes single precision
+ instructions, and can operate in both 32-bit or 64-bit floating
+ point mode. */
+ bool single;
+
+ /* Whether or not the ABI performs floating point operations in
+ software, using integer registers. */
+ bool soft;
+
+ /* Whether or not the ABI requires the use of 64-bit floating point
+ registers. */
+ bool fr1;
+
+ /* Whether or not the ABI requires the use of 64-bit floating point
+ registers on NABI systems, and 32-bit ones on OABI systems. */
+ bool frdefault;
+
+ /* Whether or not this ABI requires single precision floating point
+ emulation. */
+ bool fre;
+};
+
+static struct mode_description fpu_reqs[] =
+ {
+ [MIPS_ABI_FP_ANY] = { true, true, true, true, true, },
+ [MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true, },
+ [MIPS_ABI_FP_SINGLE] = { true, false, false, false, false, },
+ [MIPS_ABI_FP_SOFT] = { false, true, false, false, false, },
+ [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false, },
+ [MIPS_ABI_FP_XX] = { false, false, true, true, true, },
+ [MIPS_ABI_FP_64] = { false, false, true, false, false, },
+ [MIPS_ABI_FP_64A] = { false, false, true, false, true, },
+ };
+
+
+
+/* Return whether or not the given floating-point ABI is valid. */
+
+static bool
+valid_abi_p (int abi)
+{
+ switch (abi)
+ {
+ case MIPS_ABI_FP_ANY:
+ case MIPS_ABI_FP_DOUBLE:
+ case MIPS_ABI_FP_SINGLE:
+ case MIPS_ABI_FP_SOFT:
+ case MIPS_ABI_FP_OLD_64:
+ case MIPS_ABI_FP_XX:
+ case MIPS_ABI_FP_64:
+ case MIPS_ABI_FP_64A:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* Return the floating point mode appropriate for the specified
+ floating point ABI. */
+
+static int
+fp_mode_for_abi (int abi)
+{
+ struct mode_description *desc;
+
+ desc = &fpu_reqs[abi];
+
+ if (desc->fre)
+ return FP_FRE;
+ else if (desc->fr1)
+ return FP_FR1;
+
+ return FP_FR0;
+}
+
+/* Determine whether or not the CPU is capable of operating in FR0
+ floating point mode. */
+
+bool
+cpu_supports_fr0_p (void)
+{
+#if defined __mips_isa_rev && __mips_isa_rev >= 6
+ return true;
+#else /* !defined __mips_isa_rev | mips_isa_rev < 6 */
+ return false;
+#endif /* defined __mips_isa_rev && mips_isa_rev >= 6 */
+}
+
+/* Determine the FPU mode for the executable whose ELF header is
+ HEADER. If INTERPRETER is non-NULL, also take an interpreter whose
+ header is INTERPRETER into account.
+
+ ABIFLAGS should be HEADER's corresponding PT_MIPS_ABIFLAGS program
+ header, and ABIFLAGS1 should be that of INTERPRETER, if set. Both
+ fields may be NULL if no PT_MIPS_ABIFLAGS header is present; in
+ that case, use HEADER->e_flags to determine the ABI instead.
+
+ Return the FPU mode in *MODE. Value is 0 upon success, 1
+ otherwise, with errno set. */
+
+int
+determine_fpu_mode (elf_header *header, elf_header *interpreter,
+ int *mode, struct mips_elf_abi_flags *abiflags,
+ struct mips_elf_abi_flags *abiflags1)
+{
+ int exec_abi, interpreter_abi;
+ struct mode_description *exec_desc, *interpreter_desc, common;
+
+ /* Figure out the executable's floating point ABI. First, consult
+ header->e_flags, and use the old 64-bit floating point ABI if it
+ is specified. */
+
+ exec_abi = MIPS_ABI_FP_ANY;
+
+ /* First, check HEADER->e_flags. */
+
+ if (header->e_flags & EF_MIPS_FP64)
+ exec_abi = MIPS_ABI_FP_OLD_64;
+
+ /* Next, use ABIFLAGS if it exists. */
+
+ if (abiflags && valid_abi_p (abiflags->fp_abi))
+ exec_abi = abiflags->fp_abi;
+ else if (abiflags)
+ {
+ errno = ENOEXEC;
+ return 1;
+ }
+
+ /* Now determine that of the interpreter. */
+
+ interpreter_abi = MIPS_ABI_FP_ANY;
+
+ if (interpreter)
+ {
+ if (interpreter->e_flags & EF_MIPS_FP64)
+ interpreter_abi = MIPS_ABI_FP_OLD_64;
+
+ if (abiflags1 && valid_abi_p (abiflags->fp_abi))
+ interpreter_abi = abiflags->fp_abi;
+ else if (abiflags1)
+ {
+ errno = ELIBBAD;
+ return 1;
+ }
+ }
+
+ /* If no interpreter flag is set, just return that of the
+ executable. */
+
+ if (!interpreter)
+ {
+ *mode = fp_mode_for_abi (exec_abi);
+ return 0;
+ }
+
+ /* Otherwise, compare both ABIs and try to find one which will run
+ both kinds of code.
+
+ First, see if there's an easy way out: both ABIs are identical,
+ or one ABI is MIPS_ABI_FP_ANY. */
+
+ if (exec_abi == interpreter_abi)
+ {
+ *mode = fp_mode_for_abi (exec_abi);
+ return 0;
+ }
+ else if (exec_abi == MIPS_ABI_FP_ANY)
+ {
+ *mode = fp_mode_for_abi (interpreter_abi);
+ return 0;
+ }
+ else if (interpreter_abi == MIPS_ABI_FP_ANY)
+ {
+ *mode = fp_mode_for_abi (exec_abi);
+ return 0;
+ }
+
+ /* If that doesn't work, compare various characteristics of both
+ ABIs and select an appropriate floating point mode. */
+
+ exec_desc = &fpu_reqs[exec_abi];
+ interpreter_desc = &fpu_reqs[interpreter_abi];
+
+ /* Merge both sets of requirements. */
+ common.single = exec_desc->single && interpreter_desc->single;
+ common.soft = exec_desc->soft && interpreter_desc->soft;
+ common.fr1 = exec_desc->fr1 && interpreter_desc->fr1;
+ common.frdefault = exec_desc->frdefault && interpreter_desc->frdefault;
+ common.fre = exec_desc->fre && interpreter_desc->fre;
+
+ /* Default to a mode capable of running code expecting 32-bit
+ registers. */
+
+ if (!(header->e_flags & EF_MIPS_ABI2))
+ *mode = FP_FR0;
+ else
+ /* But in this case, use FR1. */
+ *mode = FP_FR1;
+
+ if (common.fre && !common.frdefault && !common.fr1)
+ /* Floating point emulation mode is required. */
+ *mode = FP_FRE;
+ else if ((common.fr1 && common.frdefault)
+ || (common.single && !common.frdefault)
+ || common.fr1)
+ /* 64-bit mode is required. */
+ *mode = FP_FR1;
+ else if (!common.fre && !common.frdefault
+ && !common.fr1 && !common.single
+ && !common.soft)
+ {
+ /* The floating point modes specified are incompatible. */
+ errno = ELIBBAD;
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/exec/mipsfpu.h b/exec/mipsfpu.h
new file mode 100644
index 00000000000..2315db59e93
--- /dev/null
+++ b/exec/mipsfpu.h
@@ -0,0 +1,82 @@
+/* Program execution for Emacs.
+
+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/>. */
+
+
+
+#ifndef _MIPSFPU_H_
+#define _MIPSFPU_H_
+
+#include "exec.h"
+
+struct mips_elf_abi_flags
+{
+ /* Version of flags structure. */
+ uint16_t version;
+
+ /* The level of the ISA: 1-5, 32, 64. */
+ uint8_t isa_level;
+
+ /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */
+ uint8_t isa_rev;
+
+ /* The size of general purpose registers. */
+ uint8_t gpr_size;
+
+ /* The size of co-processor 1 registers. */
+ uint8_t cpr1_size;
+
+ /* The size of co-processor 2 registers. */
+ uint8_t cpr2_size;
+
+ /* The floating-point ABI. */
+ uint8_t fp_abi;
+
+ /* Mask of processor-specific extensions. */
+ uint32_t isa_ext;
+
+ /* Mask of ASEs used. */
+ uint32_t ases;
+
+ /* Mask of general flags. */
+ uint32_t flags1;
+
+ /* Mask of general flags. */
+ uint32_t flags2;
+};
+
+
+
+/* Floating point modes. */
+
+#define FP_FR0 0
+#define FP_FR1 1
+#define FP_FRE 3
+
+
+
+/* Defined in mipsfpu.c. */
+
+extern bool cpu_supports_fr0_p (void);
+extern int determine_fpu_mode (elf_header *, elf_header *,
+ int *, struct mips_elf_abi_flags *,
+ struct mips_elf_abi_flags *);
+
+
+
+#endif /* _MIPSFPU_H_ */
diff --git a/exec/test.c b/exec/test.c
new file mode 100644
index 00000000000..fa2a848837c
--- /dev/null
+++ b/exec/test.c
@@ -0,0 +1,105 @@
+/* Program execution for Emacs.
+
+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 <signal.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+
+#include <sys/wait.h>
+
+#include "exec.h"
+
+
+
+static void
+print_usage (void)
+{
+ fprintf (stderr, "test loader-name program [args...]\n"
+ "Run the given program using the specified loader.\n");
+}
+
+
+
+extern char **environ;
+
+/* This program uses libexec to wrap the execution of a child
+ process. */
+
+int
+main (int argc, char **argv)
+{
+ pid_t pid, child;
+ int sig;
+ sigset_t sigset;
+
+ /* Check that there are a sufficient number of arguments. */
+
+ if (argc < 3)
+ {
+ print_usage ();
+ return 1;
+ }
+
+ exec_init (argv[1]);
+
+ /* Block SIGCHLD to avoid reentrant modification of the child
+ process list. */
+
+ sigemptyset (&sigset);
+ sigaddset (&sigset, SIGCHLD);
+ sigprocmask (SIG_BLOCK, &sigset, NULL);
+
+ if (!(pid = fork ()))
+ {
+ tracing_execve (argv[2], argv + 2, environ);
+ fprintf (stderr, "tracing_execve: %s\n",
+ strerror (errno));
+ exit (1);
+ }
+ else if (after_fork (pid))
+ {
+ fprintf (stderr, "after_fork: %s\n",
+ strerror (errno));
+ exit (1);
+ }
+
+ /* Now start waiting for child processes to exit. */
+
+ while (true)
+ {
+ child = exec_waitpid (-1, &sig, 0);
+
+ /* If pid is -1, a system call has been handled. */
+
+ if (child == -1)
+ continue;
+
+ /* If the main process exits, then exit as well. */
+
+ if (child == pid && !WIFSTOPPED (sig))
+ return (WIFEXITED (sig)
+ ? WEXITSTATUS (sig)
+ : WTERMSIG (sig));
+ }
+}
diff --git a/exec/trace.c b/exec/trace.c
new file mode 100644
index 00000000000..974df1dd5e1
--- /dev/null
+++ b/exec/trace.c
@@ -0,0 +1,1420 @@
+/* Program execution for Emacs.
+
+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 <sys/ptrace.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <limits.h>
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "exec.h"
+
+#include SYSCALL_HEADER
+#include USER_HEADER
+
+#ifdef __aarch64__
+#include <sys/uio.h> /* for struct iovec */
+#include <linux/elf.h> /* for NT_* */
+#endif /* __aarch64__ */
+
+#ifdef HAVE_SYS_UIO_H
+#include <sys/uio.h> /* for process_vm_readv */
+#endif /* HAVE_SYS_UIO_H */
+
+#ifndef SYS_SECCOMP
+#define SYS_SECCOMP 1
+#endif /* SYS_SECCOMP */
+
+#ifndef PTRACE_GETEVENTMSG
+#define PTRACE_GETEVENTMSG 0x4201
+#endif /* PTRACE_GETEVENTMSG */
+
+
+
+/* Program tracing functions.
+
+ The main entry point is the function `tracing_execve', which traces
+ the thread and calls exec. Each time that thread calls `clone',
+ the new child is traced as well.
+
+ Instead of calling `waitpid', call `exec_waitpid' instead. */
+
+
+
+/* Number of tracees children are allowed to create. */
+#define MAX_TRACEES 4096
+
+#ifdef __aarch64__
+
+/* Place PID's registers into *REGS. Return 1 upon failure, else
+ 0. */
+
+int
+aarch64_get_regs (pid_t pid, USER_REGS_STRUCT *regs)
+{
+ struct iovec iov;
+
+ iov.iov_base = regs;
+ iov.iov_len = sizeof *regs;
+
+ return (ptrace (PTRACE_GETREGSET, pid, NT_PRSTATUS,
+ &iov) != 0);
+}
+
+/* Set PID's registers to *REGS. If SYSCALL_P, also update the
+ current system call number to the `x8' register.
+
+ Value is 1 upon failure, else 0. */
+
+int
+aarch64_set_regs (pid_t pid, USER_REGS_STRUCT *regs,
+ bool syscall_p)
+{
+ struct iovec iov;
+ USER_WORD callno;
+ long rc;
+
+ /* Write the user registers. */
+
+ iov.iov_base = regs;
+ iov.iov_len = sizeof *regs;
+
+ rc = ptrace (PTRACE_SETREGSET, pid, NT_PRSTATUS,
+ &iov);
+ if (rc < 0)
+ return 1;
+
+ /* Now, write the system call number if necessary. */
+
+ if (syscall_p)
+ {
+ callno = regs->regs[8];
+ iov.iov_base = &callno;
+ iov.iov_len = sizeof callno;
+
+ return (ptrace (PTRACE_SETREGSET, pid, NT_ARM_SYSTEM_CALL,
+ &iov) != 0);
+ }
+
+ return 0;
+}
+
+#endif /* __aarch64__ */
+
+
+
+/* List of all processes which are being traced. */
+static struct exec_tracee *tracing_processes;
+
+
+
+/* Read N bytes from TRACEE's memory, starting at the specified user
+ ADDRESS. Return its contents in BUFFER.
+
+ If there are unreadable pages within ADDRESS + N, the contents of
+ BUFFER after the first such page becomes undefined. */
+
+static void
+read_memory (struct exec_tracee *tracee, char *buffer,
+ USER_WORD n, USER_WORD address)
+{
+ USER_WORD word, n_words, n_bytes, i;
+ long rc;
+#ifdef HAVE_PROCESS_VM
+ struct iovec iov, remote;
+
+ /* If `process_vm_readv' is available, use it instead. */
+
+ iov.iov_base = buffer;
+ iov.iov_len = n;
+ remote.iov_base = (void *) address;
+ remote.iov_len = n;
+
+ /* Return immediately if successful. As long as some bytes were
+ read, consider the read to have been a success. */
+
+ if (n <= SSIZE_MAX
+ && ((size_t) process_vm_readv (tracee->pid, &iov, 1,
+ &remote, 1, 0) != -1))
+ return;
+
+#endif /* HAVE_PROCESS_VM */
+
+ /* First, read entire words from the tracee. */
+ n_words = n & ~(sizeof (USER_WORD) - 1);
+
+ /* Next, determine the number of bytes to read from the last
+ word. */
+ n_bytes = n & (sizeof (USER_WORD) - 1);
+
+ /* Start reading words. */
+ i = 0;
+ while (n_words)
+ {
+ rc = ptrace (PTRACE_PEEKTEXT, tracee->pid,
+ (void *) address + i, NULL);
+ word = rc;
+ memcpy (buffer, &word, sizeof word);
+ buffer += sizeof word;
+ i += sizeof word;
+ n_words -= sizeof word;
+ }
+
+ /* Now, read the remaining bytes. */
+ assert (n_bytes < sizeof (word));
+
+ if (n_bytes)
+ {
+ rc = ptrace (PTRACE_PEEKTEXT, tracee->pid,
+ (void *) address + i, NULL);
+ word = rc;
+
+ /* Copy only n_bytes to the caller. */
+ memcpy (buffer, &word, n_bytes);
+ }
+}
+
+/* Allocate N bytes of memory from TRACEE's stack. Return the address
+ of that memory upon success, else 0.
+
+ Place the updated user-mode registers of TRACEE in *NEW_REGS, which
+ should initially contain the current stack pointer of TRACEE.
+
+ REGS should contain the user mode registers of TRACEE prior to the
+ system call starting; it is not updated to reflect any changes. */
+
+USER_WORD
+user_alloca (struct exec_tracee *tracee, USER_REGS_STRUCT *regs,
+ USER_REGS_STRUCT *new_regs, USER_WORD n)
+{
+ USER_WORD sp, old_sp;
+
+ /* Get the current stack pointer. */
+ old_sp = sp = new_regs->STACK_POINTER;
+
+#if RED_ZONE_SIZE
+ /* Some ABI rules specify a ``red zone'' around the stack pointer
+ that is reserved for compiler optimizations. */
+
+#ifdef STACK_GROWS_DOWNWARDS
+ if (sp == regs->STACK_POINTER)
+ sp -= RED_ZONE_SIZE;
+#else /* !STACK_GROWS_DOWNWARDS */
+ if (sp == regs->STACK_POINTER)
+ sp += RED_ZONE_SIZE;
+#endif /* STACK_GROWS_DOWNWARDS */
+#endif /* RED_ZONE_SIZE */
+
+ /* Now take N off the stack. */
+
+#ifdef STACK_GROWS_DOWNWARDS
+ sp = sp - n;
+
+ /* Check for overflow. */
+
+ if (sp > new_regs->STACK_POINTER)
+ return 0;
+#else /* !STACK_GROWS_DOWNWARDS */
+ sp = sp + n;
+
+ /* Check for overflow. */
+
+ if (sp < new_regs->STACK_POINTER)
+ return 0;
+#endif /* STACK_GROWS_DOWNWARDS */
+
+ /* Set the stack pointer. */
+ new_regs->STACK_POINTER = sp;
+
+#ifdef __aarch64__
+ if (aarch64_set_regs (tracee->pid, new_regs, false))
+ goto fail;
+#else /* !__aarch64__ */
+ if (ptrace (PTRACE_SETREGS, tracee->pid, NULL,
+ new_regs))
+ goto fail;
+#endif /* __aarch64__ */
+
+ /* Now return the start of the new area. */
+#ifdef STACK_GROWS_DOWNWARDS
+ return sp;
+#else /* !STACK_GROWS_DOWNWARDS */
+ return sp - n;
+#endif /* STACK_GROWS_DOWNWARDS */
+
+ fail:
+ /* Restore the old stack pointer. */
+ new_regs->STACK_POINTER = old_sp;
+ return 0;
+}
+
+/* Copy N bytes to ADDRESS in TRACEE's address space from BUFFER.
+ Value is 0 upon success, else 1. */
+
+int
+user_copy (struct exec_tracee *tracee, const unsigned char *buffer,
+ USER_WORD address, USER_WORD n)
+{
+ USER_WORD start, end, word;
+ unsigned char *bytes;
+#ifdef HAVE_PROCESS_VM
+ struct iovec iov, remote;
+
+ /* Try to use `process_vm_writev' if possible, but fall back to
+ ptrace if something bad happens. */
+
+ iov.iov_base = (void *) buffer;
+ iov.iov_len = n;
+ remote.iov_base = (void *) address;
+ remote.iov_len = n;
+
+ if (n <= SSIZE_MAX
+ && ((size_t) process_vm_writev (tracee->pid, &iov, 1,
+ &remote, 1, 0) == n))
+ return 0;
+#endif /* HAVE_PROCESS_VM */
+
+ /* Calculate the start and end positions for the write. */
+
+ start = address;
+ end = address + n;
+
+ /* Write from start to the last word. */
+
+ while (start < end)
+ {
+ if (start + sizeof word <= end)
+ {
+ /* Write a word by itself and increment start. */
+ memcpy (&word, buffer, sizeof word);
+ buffer += sizeof word;
+
+ if (ptrace (PTRACE_POKEDATA, tracee->pid,
+ (void *) start, (void *) word))
+ return 1;
+
+ start += sizeof word;
+ }
+ else
+ {
+ /* Only end - start bytes should be written.
+ Read the word at start from tracee->pid, then write
+ it back with changes. */
+
+ word = ptrace (PTRACE_PEEKDATA, tracee->pid,
+ (void *) start, NULL);
+ bytes = (unsigned char *) &word;
+ memcpy (bytes, buffer, end - start);
+
+ if (ptrace (PTRACE_POKEDATA, tracee->pid,
+ (void *) start, (void *) word))
+ return 1;
+
+ /* Writing was successful. */
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+
+
+/* Chain of free exec_tracee structures. */
+static struct exec_tracee *free_tracees;
+
+/* Remove the specified TRACEE from the chain of all processes being
+ traced. */
+
+static void
+remove_tracee (struct exec_tracee *tracee)
+{
+ struct exec_tracee **last;
+
+ last = &tracing_processes;
+ while (*last)
+ {
+ if (*last == tracee)
+ {
+ *last = tracee->next;
+
+ /* Link the tracee onto the list of free tracees. */
+ tracee->next = free_tracees;
+
+#ifndef REENTRANT
+ /* Free the exec file, if any. */
+ free (tracee->exec_file);
+ tracee->exec_file = NULL;
+#endif /* REENTRANT */
+
+ free_tracees = tracee;
+
+ return;
+ }
+ else
+ last = &(*last)->next;
+ }
+}
+
+
+
+/* Child process tracing. */
+
+/* Array of `struct exec_tracees' that they are allocated from. */
+static struct exec_tracee static_tracees[MAX_TRACEES];
+
+/* Number of tracees currently allocated. */
+static int tracees;
+
+/* Return the `struct exec_tracee' corresponding to the specified
+ PROCESS. */
+
+static struct exec_tracee *
+find_tracee (pid_t process)
+{
+ struct exec_tracee *tracee;
+
+ for (tracee = tracing_processes; tracee; tracee = tracee->next)
+ {
+ if (tracee->pid == process)
+ return tracee;
+ }
+
+ return NULL;
+}
+
+/* Prepare to handle the completion of a `clone' system call.
+
+ If the new clone is not yet being traced, create a new tracee for
+ PARENT's child, copying over its current command line. Then, set
+ `new_child' in the new tracee. Otherwise, continue it until the
+ next syscall. */
+
+static void
+handle_clone_prepare (struct exec_tracee *parent)
+{
+#ifndef REENTRANT
+ long rc;
+ unsigned long pid;
+ struct exec_tracee *tracee;
+
+ rc = ptrace (PTRACE_GETEVENTMSG, parent->pid, NULL,
+ &pid);
+ if (rc)
+ return;
+
+ /* See if the tracee already exists. */
+ tracee = find_tracee (pid);
+
+ if (tracee)
+ {
+ /* Continue the tracee. Record its command line, as that has
+ not yet been done. */
+
+ assert (tracee->new_child);
+ tracee->new_child = false;
+ tracee->exec_file = NULL;
+ ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0);
+
+ if (parent->exec_file)
+ tracee->exec_file = strdup (parent->exec_file);
+ return;
+ }
+
+ if (free_tracees)
+ {
+ tracee = free_tracees;
+ free_tracees = free_tracees->next;
+ }
+ else if (tracees < MAX_TRACEES)
+ {
+ tracee = &static_tracees[tracees];
+ tracees++;
+ }
+ else
+ return;
+
+ tracee->pid = pid;
+ tracee->next = tracing_processes;
+ tracee->waiting_for_syscall = false;
+ tracee->new_child = true;
+ tracee->exec_file = NULL;
+ tracing_processes = tracee;
+
+ /* Copy over the command line. */
+
+ if (parent->exec_file)
+ tracee->exec_file = strdup (parent->exec_file);
+#endif /* REENTRANT */
+}
+
+/* Handle the completion of a `clone' or `clone3' system call,
+ resulting in the creation of the process PID. If TRACEE is NULL,
+ allocate a new tracee structure from a static area for the
+ processes's pid, then set TRACEE->new_child to true and await the
+ parent's corresponding ptrace event to arrive; otherwise, just
+ clear TRACEE->new_child.
+
+ Value is 0 upon success, 2 if TRACEE should remain suspended until
+ the parent's ptrace-stop, and 1 otherwise. */
+
+static int
+handle_clone (struct exec_tracee *tracee, pid_t pid)
+{
+ long rc;
+ int flags, value;
+
+ /* Now allocate a new tracee, either from static_tracees or the free
+ list, if no tracee was supplied. */
+
+ value = 0;
+
+ if (!tracee)
+ {
+ if (free_tracees)
+ {
+ tracee = free_tracees;
+ free_tracees = free_tracees->next;
+ }
+ else if (tracees < MAX_TRACEES)
+ {
+ tracee = &static_tracees[tracees];
+ tracees++;
+ }
+ else
+ return 1;
+
+ tracee->pid = pid;
+ tracee->next = tracing_processes;
+ tracee->waiting_for_syscall = false;
+#ifndef REENTRANT
+ tracee->exec_file = NULL;
+#endif /* REENTRANT */
+ tracing_processes = tracee;
+ tracee->new_child = true;
+
+ /* Wait for the ptrace-stop to happen in the parent. */
+ value = 2;
+ }
+ else
+ /* Clear the flag saying that this is a newly created child
+ process. */
+ tracee->new_child = false;
+
+ /* Apply required options to the child, so that the kernel
+ automatically traces children and makes it easy to differentiate
+ between system call traps and other kinds of traps. */
+
+ flags = PTRACE_O_TRACECLONE;
+ flags |= PTRACE_O_TRACEVFORK;
+ flags |= PTRACE_O_TRACEFORK;
+ flags |= PTRACE_O_TRACESYSGOOD;
+ flags |= PTRACE_O_TRACEEXIT;
+
+ rc = ptrace (PTRACE_SETOPTIONS, pid, 0, flags);
+
+ if (rc)
+ goto bail;
+
+ if (value != 2)
+ {
+ /* The new tracee is currently stopped. Continue it until the next
+ system call. */
+
+ rc = ptrace (PTRACE_SYSCALL, pid, 0, 0);
+
+ if (rc)
+ goto bail;
+ }
+
+ return value;
+
+ bail:
+ remove_tracee (tracee);
+ return 1;
+}
+
+
+
+/* NOTICE: none of these functions should ever call `malloc' or
+ another async signal unsafe function. */
+
+/* File name of the loader binary. */
+static const char *loader_name;
+
+
+
+/* Return whether or not the trap signal described by SIGNAL is
+ generated by a system call being attempted by a tracee. */
+
+static bool
+syscall_trap_p (siginfo_t *signal)
+{
+ /* SIGTRAP delivered by the kernel means this is a system call
+ stop. */
+ return (signal->si_code == SIGTRAP
+ || signal->si_code == (SIGTRAP | SI_KERNEL));
+}
+
+/* Check if the wait status STATUS indicates a system call trap.
+ TRACEE is the process whose stop STATUS describes. If TRACEE exits
+ while this information is being determined, return -1; if STATUS
+ indicates some other kind of stop, return 1 after continuing
+ TRACEE. Value is 0 otherwise. */
+
+static int
+check_signal (struct exec_tracee *tracee, int status)
+{
+ siginfo_t siginfo;
+
+ switch ((status & 0xfff00) >> 8)
+ {
+ case SIGTRAP:
+ /* Now, use PTRACE_GETSIGINFO to determine whether or not the
+ signal was delivered in response to a system call. */
+
+ if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0, &siginfo))
+ return -1;
+
+ if (!syscall_trap_p (&siginfo))
+ {
+ if (siginfo.si_code < 0)
+ /* SIGTRAP delivered from userspace. Pass it on. */
+ ptrace (PTRACE_SYSCALL, tracee->pid, 0, SIGTRAP);
+ else
+ ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0);
+
+ return 1;
+ }
+
+ case SIGTRAP | 0x80: /* SIGTRAP | 0x80 specifically refers to
+ system call traps. */
+ break;
+
+#ifdef SIGSYS
+ case SIGSYS:
+ if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0, &siginfo))
+ return -1;
+
+ /* Continue the process until the next syscall, but don't
+ pass through the signal if an emulated syscall led to
+ it. */
+#ifdef HAVE_SIGINFO_T_SI_SYSCALL
+#ifndef __arm__
+ ptrace (PTRACE_SYSCALL, tracee->pid,
+ 0, ((siginfo.si_code == SYS_SECCOMP
+ && siginfo.si_syscall == -1)
+ ? 0 : status));
+#else /* __arm__ */
+ ptrace (PTRACE_SYSCALL, tracee->pid,
+ 0, ((siginfo.si_code == SYS_SECCOMP
+ && siginfo.si_syscall == 222)
+ ? 0 : status));
+#endif /* !__arm__ */
+#else /* !HAVE_SIGINFO_T_SI_SYSCALL */
+ /* Drop this signal, since what caused it is unknown. */
+ ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0);
+#endif /* HAVE_SIGINFO_T_SI_SYSCALL */
+ return 1;
+#endif /* SIGSYS */
+
+ default:
+ /* Continue the process until the next syscall. */
+ ptrace (PTRACE_SYSCALL, tracee->pid, 0, status);
+ return 1;
+ }
+
+ return 0;
+}
+
+
+
+/* Handle an `exec' system call from the given TRACEE. REGS are the
+ tracee's current user-mode registers.
+
+ Rewrite the system call arguments to use the loader binary. Then,
+ continue the system call until the loader is loaded. Write the
+ information necessary to load the original executable into the
+ loader's stack.
+
+ Value is 0 upon success, 1 upon a generic failure before the loader
+ is loaded, 2 if the process has stopped, and 3 if something failed,
+ but it is too late to handle it.
+
+ Set errno appropriately upon returning a generic failure. */
+
+static int
+handle_exec (struct exec_tracee *tracee, USER_REGS_STRUCT *regs)
+{
+ char buffer[PATH_MAX + 80], *area;
+ USER_REGS_STRUCT original;
+ size_t size, loader_size;
+ USER_WORD loader, size1, sp;
+ int rc, wstatus;
+ siginfo_t siginfo;
+
+ /* Save the old stack pointer. */
+ sp = regs->STACK_POINTER;
+
+ /* Read the file name. */
+ read_memory (tracee, buffer, PATH_MAX,
+ regs->SYSCALL_ARG_REG);
+
+ /* Make sure BUFFER is NULL terminated. */
+
+ if (!memchr (buffer, '\0', PATH_MAX))
+ {
+ errno = ENAMETOOLONG;
+ return 1;
+ }
+
+ /* Copy over the registers as they originally were. */
+ memcpy (&original, regs, sizeof *regs);
+
+ /* Figure out what the loader needs to do. */
+ again1:
+ area = exec_0 (buffer, tracee, &size, regs);
+
+ if (!area)
+ {
+ /* Handle SIGINTR errors caused by IO. */
+ if (errno == EINTR)
+ goto again1;
+
+ return 1;
+ }
+
+ /* Rewrite the first argument to point to the loader. */
+
+ loader_size = strlen (loader_name) + 1;
+ loader = user_alloca (tracee, &original, regs,
+ loader_size);
+
+ if (!loader)
+ {
+ errno = ENOMEM;
+ return 1;
+ }
+
+ if (user_copy (tracee, (unsigned char *) loader_name,
+ loader, loader_size))
+ {
+ errno = EIO;
+ return 1;
+ }
+
+ regs->SYSCALL_ARG_REG = loader;
+
+#ifdef __aarch64__
+
+ if (aarch64_set_regs (tracee->pid, regs, false))
+ {
+ errno = EIO;
+ return 1;
+ }
+
+#else /* !__aarch64__ */
+
+ if (ptrace (PTRACE_SETREGS, tracee->pid, NULL,
+ regs))
+ {
+ errno = EIO;
+ return 1;
+ }
+
+#endif /* __aarch64__ */
+
+ /* Continue the system call until loader starts. */
+
+ if (ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL))
+ {
+ errno = EIO;
+ return 1;
+ }
+
+#ifndef REENTRANT
+ /* Now that the loader has started, record the value to use for
+ /proc/self/exe. Don't give up just because strdup fails.
+
+ Note that exec_0 copies the absolute file name into buffer. */
+
+ if (tracee->exec_file)
+ free (tracee->exec_file);
+ tracee->exec_file = strdup (buffer);
+#endif /* REENTRANT */
+
+ again:
+ rc = waitpid (tracee->pid, &wstatus, __WALL);
+ if (rc == -1 && errno == EINTR)
+ goto again;
+
+ if (rc < 0)
+ return 1;
+
+ if (!WIFSTOPPED (wstatus))
+ /* The process has been killed in response to a signal.
+ In this case, simply return 2. */
+ return 2;
+ else
+ {
+ /* Then, check if STATUS is not a syscall-stop, and try again if
+ it isn't. */
+ rc = check_signal (tracee, wstatus);
+
+ if (rc == -1)
+ return 2;
+ else if (rc)
+ goto again;
+
+ /* Retrieve the signal information and determine whether or not
+ the system call has completed. */
+
+ if (ptrace (PTRACE_GETSIGINFO, tracee->pid, 0,
+ &siginfo))
+ return 3;
+
+ if (!syscall_trap_p (&siginfo))
+ {
+ /* Continue. */
+ if (ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0))
+ return 3;
+
+ goto again;
+ }
+ }
+
+#ifdef __aarch64__
+
+ if (aarch64_get_regs (tracee->pid, &original))
+ return 3;
+
+#else /* !__aarch64__ */
+
+ /* The system call has now completed. Get the registers again. */
+
+ if (ptrace (PTRACE_GETREGS, tracee->pid, NULL,
+ &original))
+ return 3;
+
+#endif /* __aarch64__ */
+
+ *regs = original;
+
+ /* Upon failure, wait for the next system call and return
+ success. */
+
+ if (original.SYSCALL_RET_REG)
+ {
+ /* Restore the original stack pointer. */
+ regs->STACK_POINTER = sp;
+
+#ifdef __aarch64__
+ aarch64_set_regs (tracee->pid, regs, false);
+#else /* !__aarch64__ */
+ ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs);
+#endif /* __aarch64__ */
+
+ goto exec_failure;
+ }
+
+ /* Write the loader area to the stack, followed by its size and the
+ original stack pointer. */
+
+ loader = user_alloca (tracee, &original, regs,
+ size + sizeof loader * 2);
+ if (!loader)
+ return 3;
+
+ size1 = size;
+
+#ifndef STACK_GROWS_DOWNWARDS
+
+ NOT_IMPLEMENTED;
+
+#else /* STACK_GROWS_DOWNWARDS */
+
+ if (user_copy (tracee, (unsigned char *) area,
+ loader + sizeof size1 * 2, size)
+ || user_copy (tracee, (unsigned char *) &size1,
+ loader + sizeof size1, sizeof size1))
+ return 3;
+
+ size1 = original.STACK_POINTER;
+
+ if (user_copy (tracee, (unsigned char *) &size1,
+ loader, sizeof size1))
+ return 3;
+
+#endif /* STACK_GROWS_DOWNWARDS */
+
+ /* Continue. */
+ if (ptrace (PTRACE_SYSCALL, tracee->pid, 0, 0))
+ return 3;
+
+ return 0;
+
+ exec_failure:
+ return 3;
+}
+
+/* Handle a `readlink' or `readlinkat' system call.
+
+ CALLNO is the system call number, and REGS are the current user
+ registers of the TRACEE.
+
+ If the first argument of a `readlinkat' system call is AT_FDCWD,
+ and the file name specified in either a `readlink' or `readlinkat'
+ system call is `/proc/self/exe', write the name of the executable
+ being run into the buffer specified in the system call.
+
+ Return the number of bytes written to the tracee's buffer in
+ *RESULT.
+
+ Value is 0 upon success. Value is 1 upon failure, and 2 if the
+ system call has been emulated. */
+
+static int
+handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs,
+ struct exec_tracee *tracee, USER_WORD *result)
+{
+#ifdef REENTRANT
+ /* readlinkat cannot be handled specially when the library is built
+ to be reentrant, as the file name information cannot be
+ recorded. */
+ return 0;
+#else /* !REENTRANT */
+
+ char buffer[PATH_MAX + 1];
+ USER_WORD address, return_buffer, size;
+ size_t length;
+
+ /* Read the file name. */
+
+#ifdef READLINK_SYSCALL
+ if (callno == READLINK_SYSCALL)
+ {
+ address = regs->SYSCALL_ARG_REG;
+ return_buffer = regs->SYSCALL_ARG1_REG;
+ size = regs->SYSCALL_ARG2_REG;
+ }
+ else
+#endif /* READLINK_SYSCALL */
+ {
+ address = regs->SYSCALL_ARG1_REG;
+ return_buffer = regs->SYSCALL_ARG2_REG;
+ size = regs->SYSCALL_ARG3_REG;
+ }
+
+ read_memory (tracee, buffer, PATH_MAX, address);
+
+ /* Make sure BUFFER is NULL terminated. */
+
+ if (!memchr (buffer, '\0', PATH_MAX))
+ {
+ errno = ENAMETOOLONG;
+ return 1;
+ }
+
+ /* Now check if the caller is looking for /proc/self/exe.
+
+ dirfd can be ignored, as for now only absolute file names are
+ handled. FIXME. */
+
+ if (strcmp (buffer, "/proc/self/exe") || !tracee->exec_file)
+ return 0;
+
+ /* Copy over tracee->exec_file. Truncate it to PATH_MAX, length, or
+ size, whichever is less. */
+
+ length = strlen (tracee->exec_file);
+ length = MIN (size, MIN (PATH_MAX, length));
+ strncpy (buffer, tracee->exec_file, length);
+
+ if (user_copy (tracee, (unsigned char *) buffer,
+ return_buffer, length))
+ {
+ errno = EIO;
+ return 1;
+ }
+
+ *result = length;
+ return 2;
+#endif /* REENTRANT */
+}
+
+/* Process the system call at which TRACEE is stopped. If the system
+ call is not known or not exec, send TRACEE on its way. Otherwise,
+ rewrite it to load the loader and perform an appropriate action. */
+
+static void
+process_system_call (struct exec_tracee *tracee)
+{
+ USER_REGS_STRUCT regs;
+ int rc, wstatus, save_errno;
+ USER_WORD callno, sp;
+ USER_WORD result;
+ bool reporting_error;
+
+#ifdef __aarch64__
+ rc = aarch64_get_regs (tracee->pid, &regs);
+#else /* !__aarch64__ */
+ rc = ptrace (PTRACE_GETREGS, tracee->pid, NULL,
+ &regs);
+#endif /* __aarch64__ */
+
+ /* TODO: what to do if this fails? */
+ if (rc < 0)
+ return;
+
+ /* Save the stack pointer. */
+ sp = regs.STACK_POINTER;
+
+ /* Now dispatch based on the system call. */
+ callno = regs.SYSCALL_NUM_REG;
+ switch (callno)
+ {
+ case EXEC_SYSCALL:
+
+ /* exec system calls should be handled synchronously. */
+ assert (!tracee->waiting_for_syscall);
+ rc = handle_exec (tracee, &regs);
+
+ switch (rc)
+ {
+ case 3:
+ /* It's too late to do anything about this error,. */
+ break;
+
+ case 2:
+ /* The process has gone away. */
+ remove_tracee (tracee);
+ break;
+
+ case 1:
+ /* An error has occured; errno is set to the error. */
+ goto report_syscall_error;
+ }
+
+ break;
+
+#ifdef READLINK_SYSCALL
+ case READLINK_SYSCALL:
+#endif /* READLINK_SYSCALL */
+ case READLINKAT_SYSCALL:
+
+ /* Handle this readlinkat system call. */
+ rc = handle_readlinkat (callno, &regs, tracee,
+ &result);
+
+ /* rc means the same as in `handle_exec'. */
+
+ if (rc == 1)
+ goto report_syscall_error;
+ else if (rc == 2)
+ goto emulate_syscall;
+
+ /* Fallthrough. */
+
+ default:
+ /* Don't wait for the system call to finish; instead, the system
+ will DTRT upon the next call to PTRACE_SYSCALL after the
+ syscall-trap signal is delivered. */
+
+ rc = ptrace (PTRACE_SYSCALL, tracee->pid,
+ NULL, NULL);
+ if (rc < 0)
+ return;
+
+ tracee->waiting_for_syscall = !tracee->waiting_for_syscall;
+ }
+
+ return;
+
+ report_syscall_error:
+ reporting_error = true;
+ goto common;
+
+ emulate_syscall:
+ reporting_error = false;
+ common:
+
+ /* Reporting an error or emulating a system call works by setting
+ the system call number to -1, letting it continue, and then
+ substituting errno for ENOSYS in the case of an error.
+
+ Make sure that the stack pointer is restored to its original
+ position upon exit, or bad things can happen. */
+
+ /* First, save errno; system calls below will clobber it. */
+ save_errno = errno;
+
+ regs.SYSCALL_NUM_REG = -1;
+ regs.STACK_POINTER = sp;
+
+#ifdef __aarch64__
+ if (aarch64_set_regs (tracee->pid, &regs, true))
+ return;
+#else /* !__aarch64__ */
+
+#ifdef __arm__
+ /* On ARM systems, a special request is used to update the system
+ call number as known to the kernel. In addition, the system call
+ number must be valid, so use `tuxcall'. Hopefully, nobody will
+ run this on a kernel with Tux. */
+
+ if (ptrace (PTRACE_SET_SYSCALL, tracee->pid, NULL, 222))
+ return;
+#endif /* __arm__ */
+
+ if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, &regs))
+ return;
+#endif /* __aarch64__ */
+
+ /* Do this invalid system call. */
+ if (ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL))
+ return;
+
+ again1:
+ rc = waitpid (tracee->pid, &wstatus, __WALL);
+ if (rc == -1 && errno == EINTR)
+ goto again1;
+
+ /* Return if waitpid fails. */
+
+ if (rc == -1)
+ return;
+
+ /* If the process received a signal, see if the signal is SIGSYS and
+ from seccomp. If so, discard it. */
+
+ if (WIFSTOPPED (wstatus))
+ {
+ rc = check_signal (tracee, wstatus);
+
+ if (rc == -1)
+ return;
+ else if (rc)
+ goto again1;
+ }
+
+ if (!WIFSTOPPED (wstatus))
+ /* The process has been killed in response to a signal. In this
+ case, simply unlink the tracee and return. */
+ remove_tracee (tracee);
+ else if (reporting_error)
+ {
+#ifdef __mips__
+ /* MIPS systems place errno in v0 and set a3 to 1. */
+ regs.gregs[2] = save_errno;
+ regs.gregs[7] = 1;
+#else /* !__mips__ */
+ regs.SYSCALL_RET_REG = -save_errno;
+#endif /* __mips__ */
+
+ /* Report errno. */
+#ifdef __aarch64__
+ aarch64_set_regs (tracee->pid, &regs, false);
+#else /* !__aarch64__ */
+ ptrace (PTRACE_SETREGS, tracee->pid, NULL, &regs);
+#endif /* __aarch64__ */
+
+ /* Now wait for the next system call to happen. */
+ ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL);
+ }
+ else
+ {
+ /* No error is being reported. Return the result in the
+ appropriate registers. */
+
+#ifdef __mips__
+ /* MIPS systems place errno in v0 and set a3 to 1. */
+ regs.gregs[2] = result;
+ regs.gregs[7] = 0;
+#else /* !__mips__ */
+ regs.SYSCALL_RET_REG = result;
+#endif /* __mips__ */
+
+ /* Report errno. */
+#ifdef __aarch64__
+ aarch64_set_regs (tracee->pid, &regs, false);
+#else /* !__aarch64__ */
+ ptrace (PTRACE_SETREGS, tracee->pid, NULL, &regs);
+#endif /* __aarch64__ */
+
+ /* Now wait for the next system call to happen. */
+ ptrace (PTRACE_SYSCALL, tracee->pid, NULL, NULL);
+ }
+}
+
+
+
+/* Like `execve', but asks the parent to begin tracing this thread.
+ Fail if tracing is unsuccessful. */
+
+int
+tracing_execve (const char *file, char *const *argv,
+ char *const *envp)
+{
+ int rc;
+
+ /* Start tracing self. */
+ rc = ptrace (PTRACE_TRACEME, 0, NULL, NULL);
+ if (rc)
+ return rc;
+
+ /* Notify the parent to enter signal-delivery-stop. */
+ raise (SIGSTOP);
+ return execve (file, argv, envp);
+}
+
+/* Wait for PID to trace itself, and make a record of that process.
+ Value is 1 or 2 upon failure, 0 otherwise. Make sure that SIGCHLD
+ is blocked around calls to this function.
+
+ If failure occurs because PID exited, value is 2; upon any other
+ kind of failure, value is 1. */
+
+int
+after_fork (pid_t pid)
+{
+ int wstatus, rc, flags;
+ struct exec_tracee *tracee;
+
+ /* First, wait for something to happen to PID. */
+ again:
+ rc = waitpid (pid, &wstatus, __WALL);
+ if (rc != pid && errno == EINTR)
+ goto again;
+
+ if (rc != pid)
+ return 1;
+
+ /* If the child exited (or in general wasn't traced), return 2. */
+
+ if (!WIFSTOPPED (wstatus))
+ return 2;
+
+ /* Apply required options to the child, so that the kernel
+ automatically traces children and makes it easy to differentiate
+ between system call traps and other kinds of traps. */
+
+ flags = PTRACE_O_TRACECLONE;
+ flags |= PTRACE_O_TRACEVFORK;
+ flags |= PTRACE_O_TRACEFORK;
+ flags |= PTRACE_O_TRACESYSGOOD;
+ flags |= PTRACE_O_TRACEEXIT;
+
+ rc = ptrace (PTRACE_SETOPTIONS, pid, 0, flags);
+
+ if (rc)
+ {
+ /* If the kernel can't trace child processes upon creation and
+ exit, then it can't work reliably. */
+ ptrace (PTRACE_DETACH, pid, 0, 0);
+ return 1;
+ }
+
+ /* Request that the child stop upon the next system call. */
+ rc = ptrace (PTRACE_SYSCALL, pid, 0, 0);
+ if (rc)
+ return 1;
+
+ /* Enter the child in `tracing_processes'. */
+
+ if (free_tracees)
+ {
+ tracee = free_tracees;
+ free_tracees = free_tracees->next;
+ }
+ else
+ tracee = malloc (sizeof *tracee);
+
+ if (!tracee)
+ return 1;
+
+ tracee->pid = pid;
+ tracee->next = tracing_processes;
+ tracee->waiting_for_syscall = false;
+ tracee->new_child = false;
+#ifndef REENTRANT
+ tracee->exec_file = NULL;
+#endif /* REENTRANT */
+ tracing_processes = tracee;
+ return 0;
+}
+
+/* Wait for a child process to exit, like `waitpid'. However, if a
+ child stops to perform a system call, send it on its way and return
+ -1. OPTIONS must not contain WUNTRACED. */
+
+pid_t
+exec_waitpid (pid_t pid, int *wstatus, int options)
+{
+ int status;
+ struct exec_tracee *tracee;
+ siginfo_t siginfo;
+
+ pid = waitpid (pid, &status, options | __WALL);
+ if (pid < 0)
+ return pid;
+
+ /* Copy status into *WSTATUS if specified. */
+ if (wstatus)
+ *wstatus = status;
+
+ /* WIFSTOPPED (status) means that the process has been stopped in
+ response to a system call. Find its tracee and process the
+ system call. */
+
+ if (WIFSTOPPED (status))
+ {
+ tracee = find_tracee (pid);
+
+ if (!tracee || tracee->new_child)
+ {
+ if (WSTOPSIG (status) == SIGSTOP)
+ /* A new process has been created and stopped. Record
+ it now. */
+ handle_clone (tracee, pid);
+
+ return -1;
+ }
+
+ /* Now extract the stop signal, including ptrace event bits. */
+ status &= 0xfff00;
+ status = status >> 8;
+
+ switch (status)
+ {
+ case SIGTRAP:
+ /* Now, use PTRACE_GETSIGINFO to determine whether or not the
+ signal was delivered in response to a system call. */
+
+ if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo))
+ return -1;
+
+ if (!syscall_trap_p (&siginfo))
+ {
+ if (siginfo.si_code < 0)
+ /* SIGTRAP delivered from userspace. Pass it on. */
+ ptrace (PTRACE_SYSCALL, pid, 0, SIGTRAP);
+ else
+ ptrace (PTRACE_SYSCALL, pid, 0, 0);
+
+ return -1;
+ }
+
+ case SIGTRAP | 0x80: /* SIGTRAP | 0x80 specifically refers to
+ system call traps. */
+ /* Otherwise, process the system call and continue waiting. */
+ process_system_call (tracee);
+ return -1;
+
+ case SIGTRAP | (PTRACE_EVENT_EXIT << 8):
+ /* The tracee has exited. Make it finish correctly. */
+ ptrace (PTRACE_SYSCALL, pid, 0, 0);
+ remove_tracee (tracee);
+ return -1;
+
+ case SIGTRAP | (PTRACE_EVENT_FORK << 8):
+ case SIGTRAP | (PTRACE_EVENT_VFORK << 8):
+ case SIGTRAP | (PTRACE_EVENT_CLONE << 8):
+
+ /* Both PTRACE_EVENT_CLONE and SIGSTOP must arrive before a
+ process is continued. Otherwise, its parent's cmdline
+ cannot be obtained and propagated.
+
+ If the PID of the new process is currently not being
+ traced, create a new tracee. Set `new_child' to true,
+ and copy over the old command line in preparation for a
+ SIGSTOP signal being delivered to it.
+
+ Otherwise, start the tracee running until the next
+ syscall. */
+
+ handle_clone_prepare (tracee);
+
+ /* These events are handled by tracing SIGSTOP signals sent
+ to unknown tracees. Make sure not to pass through
+ status, as there's no signal really being delivered. */
+ ptrace (PTRACE_SYSCALL, pid, 0, 0);
+ return -1;
+
+#ifdef SIGSYS
+ case SIGSYS:
+ if (ptrace (PTRACE_GETSIGINFO, pid, 0, &siginfo))
+ return -1;
+
+ /* Continue the process until the next syscall, but don't
+ pass through the signal if an emulated syscall led to
+ it. */
+#ifdef HAVE_SIGINFO_T_SI_SYSCALL
+#ifndef __arm__
+ ptrace (PTRACE_SYSCALL, pid, 0, ((siginfo.si_code == SYS_SECCOMP
+ && siginfo.si_syscall == -1)
+ ? 0 : status));
+#else /* __arm__ */
+ ptrace (PTRACE_SYSCALL, pid, 0, ((siginfo.si_code == SYS_SECCOMP
+ && siginfo.si_syscall == 222)
+ ? 0 : status));
+#endif /* !__arm__ */
+#else /* !HAVE_SIGINFO_T_SI_SYSCALL */
+ /* Drop this signal, since what caused it is unknown. */
+ ptrace (PTRACE_SYSCALL, pid, 0, 0);
+#endif /* HAVE_SIGINFO_T_SI_SYSCALL */
+ return -1;
+#endif /* SIGSYS */
+
+ default:
+ /* Continue the process until the next syscall. */
+ ptrace (PTRACE_SYSCALL, pid, 0, status);
+ return -1;
+ }
+ }
+ else
+ {
+ /* The process has exited. Unlink the associated tracee. */
+ tracee = find_tracee (pid);
+
+ if (tracee)
+ remove_tracee (tracee);
+
+ return pid;
+ }
+}
+
+
+
+/* Initialize the exec library. LOADER should be the file name of the
+ loader binary; it is not copied. */
+
+void
+exec_init (const char *loader)
+{
+ loader_name = loader;
+}
diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in
new file mode 100644
index 00000000000..f7f834e7582
--- /dev/null
+++ b/java/AndroidManifest.xml.in
@@ -0,0 +1,201 @@
+<!-- @configure_input@
+
+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/>. -->
+
+<!-- targetSandboxVersion must be 1. Otherwise, fascist security
+ restrictions prevent Emacs from making HTTP connections. -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.gnu.emacs"
+ android:targetSandboxVersion="1"
+ android:installLocation="auto"
+ android:requestLegacyExternalStorage="true"
+ android:versionCode="@emacs_major_version@"
+ android:versionName="@version@">
+
+ <!-- Paste in every permission in existence so Emacs can do
+ anything. -->
+
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+ <uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.SET_WALLPAPER" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.RECEIVE_MMS"/>
+ <uses-permission android:name="android.permission.WRITE_SMS"/>
+ <uses-permission android:name="android.permission.READ_SMS"/>
+ <uses-permission android:name="android.permission.NFC" />
+ <uses-permission android:name="android.permission.TRANSMIT_IR" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
+ <uses-permission android:name="android.permission.WAKE_LOCK"/>
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+ <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <!-- This is required on Android 11 or later to access /sdcard. -->
+
+ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
+
+ <uses-sdk android:minSdkVersion="@ANDROID_MIN_SDK@"
+ android:targetSdkVersion="33"/>
+
+ <application android:name="org.gnu.emacs.EmacsApplication"
+ android:label="Emacs"
+ android:icon="@drawable/emacs"
+ android:hardwareAccelerated="true"
+ android:supportsRtl="true"
+ android:theme="@style/EmacsStyle"
+ android:debuggable="@ANDROID_DEBUGGABLE@"
+ @ANDROID_SHARED_USER_ID@
+ android:extractNativeLibs="true">
+
+ <activity android:name="org.gnu.emacs.EmacsActivity"
+ android:launchMode="singleInstance"
+ android:windowSoftInputMode="adjustResize"
+ android:exported="true"
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity android:name="org.gnu.emacs.EmacsOpenActivity"
+ android:taskAffinity="open.dialog"
+ android:excludeFromRecents="true"
+ android:exported="true">
+
+ <!-- Allow Emacs to open all kinds of files known to Android. -->
+
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW"/>
+ <action android:name="android.intent.action.EDIT"/>
+ <action android:name="android.intent.action.PICK"/>
+
+ <category android:name="android.intent.category.DEFAULT"/>
+
+ <data android:mimeType="image/aces"/>
+ <data android:mimeType="image/avci"/>
+ <data android:mimeType="image/avcs"/>
+ <data android:mimeType="image/avif"/>
+ <data android:mimeType="image/bmp"/>
+ <data android:mimeType="image/cgm"/>
+ <data android:mimeType="image/dicom-rle"/>
+ <data android:mimeType="image/dpx"/>
+ <data android:mimeType="image/emf"/>
+ <data android:mimeType="image/example"/>
+ <data android:mimeType="image/fits"/>
+ <data android:mimeType="image/g3fax"/>
+ <data android:mimeType="image/heic"/>
+ <data android:mimeType="image/heic-sequence"/>
+ <data android:mimeType="image/heif"/>
+ <data android:mimeType="image/heif-sequence"/>
+ <data android:mimeType="image/hej2k"/>
+ <data android:mimeType="image/hsj2"/>
+ <data android:mimeType="image/jls"/>
+ <data android:mimeType="image/jp2"/>
+ <data android:mimeType="image/jph"/>
+ <data android:mimeType="image/jphc"/>
+ <data android:mimeType="image/jpm"/>
+ <data android:mimeType="image/jpx"/>
+ <data android:mimeType="image/jxr"/>
+ <data android:mimeType="image/jxrA"/>
+ <data android:mimeType="image/jxrS"/>
+ <data android:mimeType="image/jxs"/>
+ <data android:mimeType="image/jxsc"/>
+ <data android:mimeType="image/jxsi"/>
+ <data android:mimeType="image/jxss"/>
+ <data android:mimeType="image/ktx"/>
+ <data android:mimeType="image/ktx2"/>
+ <data android:mimeType="image/naplps"/>
+ <data android:mimeType="image/png"/>
+ <data android:mimeType="image/prs.btif"/>
+ <data android:mimeType="image/prs.pti"/>
+ <data android:mimeType="image/pwg-raster"/>
+ <data android:mimeType="image/svg+xml"/>
+ <data android:mimeType="image/t38"/>
+ <data android:mimeType="image/tiff"/>
+ <data android:mimeType="image/tiff-fx"/>
+ <data android:mimeType="image/xpm"/>
+ <data android:mimeType="text/*"/>
+ <data android:mimeType="application/*xml"/>
+ <data android:mimeType="application/atom+xml"/>
+ <data android:mimeType="application/dxf"/>
+ <data android:mimeType="application/ecmascript"/>
+ <data android:mimeType="application/javascript"/>
+ <data android:mimeType="application/json"/>
+ <data android:mimeType="application/*log*"/>
+ <data android:mimeType="application/octet-stream"/>
+ <data android:mimeType="application/soap+xm"/>
+ <data android:mimeType="application/x-caramel"/>
+ <data android:mimeType="application/x-klaunch"/>
+ <data android:mimeType="application/x-latex"/>
+ <data android:mimeType="application/x-sh"/>
+ <data android:mimeType="application/x-tcl"/>
+ <data android:mimeType="application/x-tex*"/>
+ <data android:mimeType="application/x-troff*"/>
+ <data android:mimeType="application/xhtml+xml"/>
+ <data android:mimeType="application/xml*"/>
+ <data android:mimeType="application/zip"/>
+ <data android:mimeType="application/x-zip-compressed"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:name="org.gnu.emacs.EmacsMultitaskActivity"
+ android:windowSoftInputMode="adjustResize"
+ android:exported="true"
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"/>
+
+ <activity android:autoRemoveFromRecents="true"
+ android:label="Emacs options"
+ android:exported="true"
+ android:name=".EmacsPreferencesActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.APPLICATION_PREFERENCES" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <provider android:name="org.gnu.emacs.EmacsDocumentsProvider"
+ android:authorities="org.gnu.emacs"
+ android:exported="true"
+ android:grantUriPermissions="true"
+ android:permission="android.permission.MANAGE_DOCUMENTS"
+ android:enabled="@bool/isAtLeastKitKat">
+ <intent-filter>
+ <action
+ android:name="android.content.action.DOCUMENTS_PROVIDER"/>
+ </intent-filter>
+ </provider>
+
+ <service android:name="org.gnu.emacs.EmacsService"
+ android:directBootAware="false"
+ android:enabled="true"
+ android:exported="false"
+ android:label="GNU Emacs service"/>
+ </application>
+</manifest>
diff --git a/java/INSTALL b/java/INSTALL
new file mode 100644
index 00000000000..63b99272004
--- /dev/null
+++ b/java/INSTALL
@@ -0,0 +1,968 @@
+Installation instructions for Android
+Copyright (C) 2023 Free Software Foundation, Inc.
+See the end of the file for license conditions.
+
+Please read the entirety of this file before attempting to build Emacs
+as an application package which can run on Android devices.
+
+When building from the source repository, make sure to read
+INSTALL.REPO as well.
+
+
+
+Android is an unusual operating system in that program binaries cannot
+be produced on computers running Android themselves. Instead, they
+must be built on some other computer using a set of tools known as the
+``Android SDK'' (Software Development Kit) and the ``Android NDK''
+(Native Development Kit.) Appropriate versions of both must be
+obtained to build GNU Emacs; after being built, the generated binaries
+will work on almost all Android devices. This document does not
+elaborate on how both sets of tools can be obtained. However, for
+your freedom's sake, you should use the Android SDK provided by the
+Debian project.
+
+In addition to the Android SDK and Android NDK, Emacs also requires
+the Java compiler from OpenJDK 1.7.0 to be installed on your system,
+along with a working `m4' macro processor. Building on GNU systems is
+all that is officially supported. We are told that Mac OS works too,
+and other Unix systems will likely work as well, but MS Windows and
+Cygwin will not.
+
+Once all of those tools are obtained, you may invoke the `configure'
+script like so:
+
+ ./configure --with-android=/path/to/android.jar \
+ ANDROID_CC=/path/to/android/ndk/cc \
+ SDK_BUILD_TOOLS=/path/to/sdk/build/tools
+
+Replacing the paths in the command line above with:
+
+ - the path to the `android.jar' headers which come with the Android
+ SDK. They must correspond to Android version 13 (API level 33) or
+ later.
+
+ - the path to the C compiler in the Android NDK, for the kind of CPU
+ you are building Emacs to run on.
+
+ - the path to the directory in the Android SDK containing binaries
+ such as `aapt', `apksigner', and `d8'. These are used to build
+ the application package.
+
+Where the type of CPU can either be `armeabi', `armv7*', `i686',
+`x86_64', `mips', or `mips64'.
+
+After the configuration process completes, you may run:
+
+ make all
+
+Once `make' finishes, there should be a file in the `java' directory
+named along the lines of:
+
+ emacs-<version>-<api-version>-<abi>.apk
+
+where <api-version> is the oldest version of Android that the package
+will run on, and <abi> is the type of Android machine the package was
+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.
+
+Even older versions of the Android SDK do not require the extra
+`-isystem' directives.
+
+Emacs is known to run on Android 2.2 (API version 8) or later, with
+the NDK r10b or later. We wanted to make Emacs work on even older
+versions of Android, but they are missing the required JNI graphics
+library that allows Emacs to display text from C code.
+
+Due to an extremely nasty bug in the Android 2.2 system, the generated
+Emacs package cannot be compressed in builds for Android 2.2. As a
+result, the Emacs package will be approximately 100 megabytes larger
+than a compressed package for a newer version of Android.
+
+
+BUILDING C++ DEPENDENCIES
+
+With a new version of the NDK, dependencies containing C++ code should
+build without any futher configuration. However, older versions
+require that you use the ``make_standalone_toolchain.py'' script in
+the NDK distribution to create a ``standalone toolchain'', and use
+that instead, in order for C++ headers to be found.
+
+See https://developer.android.com/ndk/guides/standalone_toolchain for
+more details; when a ``standalone toolchain'' is specified, the
+configure script will try to determine the location of the C++
+compiler based on the C compiler specified. If that automatic
+detection does not work, you can specify a C++ compiler yourself, like
+so:
+
+ ./configure --with-ndk-cxx=/path/to/toolchain/bin/i686-linux-android-g++
+
+Some versions of the NDK have a bug, where GCC fails to locate
+``stddef.h'' after being copied to a standalone toolchain. To work
+around this problem (which normally exhibits itself when building C++
+code), add:
+
+ -isystem /path/to/toolchain/include/c++/4.9.x
+
+to ANDROID_CFLAGS.
+
+
+DEBUG AND RELEASE BUILDS
+
+Android makes a distinction between ``debug'' and ``release'' builds
+of applications. With ``release'' builds, the system will apply
+stronger optimizations to the application at the cost of being unable
+to debug them with the steps in etc/DEBUG.
+
+Emacs is built as a debuggable package by default, but:
+
+ ./configure --without-android-debug
+
+will create a release build of Emacs instead. This may be useful when
+running Emacs on resource constrained machines.
+
+If you are building an Emacs package for redistribution, we urge you
+to provide both debug and release versions.
+
+
+BUILDING WITH A SHARED USER ID
+
+Sometimes it may be desirable to build Emacs so that it is able to
+access executables from another program. To achieve this, that other
+program must have a ``shared user ID'', and be signed with the same
+signing key used to sign Emacs (normally `emacs.keystore'.)
+
+Once you have both that signing key and its ``shared user ID'', you
+can give it to configure:
+
+ ./configure --with-shared-user-id=MY.SHARED.USER.ID
+
+Don't do this if you already have Emacs installed with a different
+shared user ID, as the system does not allow programs to change their
+user IDs after being installed.
+
+
+BUILDING WITH THIRD PARTY LIBRARIES
+
+The Android NDK does not support the usual ways of locating third
+party libraries, especially not via `pkg-config'. Instead, it uses
+its own system called `ndk-build'. The one exception to this rule is
+zlib, which is considered a part of the Android OS itself and is
+available on all devices running Android.
+
+Android also requires that each application include its own
+dependencies, as the system makes no guarantee about the existence of
+any particular library.
+
+Emacs is not built with the `ndk-build' system. Instead, it is built
+with Autoconf and Make.
+
+However, it supports building and including dependencies which use the
+similarly Make-based `ndk-build' system.
+
+To use dependencies built through `ndk-build', you must specify a list
+of directories within which Emacs will search for ``Android.mk''
+files, like so:
+
+ ./configure "--with-ndk-path=directory1 directory2"
+
+If `configure' complains about not being able to find
+``libc++_shared.so'', then you must locate that file in your copy of
+the NDK, and specify it like so:
+
+ ./configure --with-ndk-cxx-shared=/path/to/sysroot/libc++_shared.so
+
+Emacs will then read the ``Android.mk'' file in each directory, and
+automatically build and use those modules.
+
+When building for Intel systems, some ``ndk-build'' modules require
+the Netwide Assembler, usually installed under ``nasm'', to be present
+on the system that is building Emacs.
+
+Google, Inc. has adapted many common Emacs dependencies to use the
+`ndk-build' system. Here is a non-exhaustive list of what is known to
+work, along with what has to be patched to make them work:
+
+ libpng - https://android.googlesource.com/platform/external/libpng
+ libwebp - https://android.googlesource.com/platform/external/webp
+ (You must apply the patch at the end of this file for the resulting
+ binary to work on armv7 devices.)
+ giflib - https://android.googlesource.com/platform/external/giflib
+ (You must add LOCAL_EXPORT_CFLAGS := -I$(LOCAL_PATH) before
+ its Android.mk includes $(BUILD_STATIC_LIBRARY))
+ libjpeg-turbo - https://android.googlesource.com/platform/external/libjpeg-turbo
+ (You must add LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH) before
+ its Android.mk includes $(BUILD_SHARED_LIBRARY))
+ libxml2 - https://android.googlesource.com/platform/external/libxml2/
+ (You must also place the dependency icu4c in ``--with-ndk-path'',
+ and apply the patch at the end of this file.)
+ icu4c - https://android.googlesource.com/platform/external/icu/
+ (You must apply the patch at the end of this file.)
+ sqlite3 - https://android.googlesource.com/platform/external/sqlite/
+ (You must apply the patch at the end of this file, and add the `dist'
+ directory to ``--with-ndk-path''.)
+ libselinux - https://android.googlesource.com/platform/external/libselinux
+ (You must apply the patches at the end of the file, and obtain
+ the following three dependencies.)
+ libpackagelistparser
+ https://android.googlesource.com/platform/system/core/+/refs/heads/nougat-mr1-dev/libpackagelistparser/
+ libpcre - https://android.googlesource.com/platform/external/pcre
+ libcrypto - https://android.googlesource.com/platform/external/boringssl
+ (You must apply the patch at the end of this file when building for
+ ARM systems.)
+
+Many of these dependencies have been migrated over to the
+``Android.bp'' build system now used to build Android itself.
+However, the old ``Android.mk'' Makefiles are still present in older
+branches, and can be easily adapte to newer versions.
+
+In addition, some Emacs dependencies provide `ndk-build' support
+themselves:
+
+ libjansson - https://github.com/akheron/jansson
+ (You must add LOCAL_EXPORT_INCLUDES := $(LOCAL_C_INCLUDES) before
+ its Android.mk includes $(BUILD_SHARED_LIBRARY), then copy
+ android/jansson_config.h to android/jansson_private_config.h.)
+
+Emacs developers have ported the following dependencies to ARM Android
+systems:
+
+ gnutls, gmp - https://sourceforge.net/projects/android-ports-for-gnu-emacs
+ (Please see the section GNUTLS near the end of this file.)
+ libtiff - https://sourceforge.net/projects/android-ports-for-gnu-emacs
+ (Extract and point ``--with-ndk-path'' to tiff-4.5.0-emacs.tar.gz.)
+ tree-sitter - https://sourceforge.net/projects/android-ports-for-gnu-emacs
+ (Please see the section TREE-SITTER near the end of this file.)
+ harfbuzz - https://sourceforge.net/projects/android-ports-for-gnu-emacs
+ (Please see the section HARFBUZZ near the end of this file.)
+
+And other developers have ported the following dependencies to Android
+systems:
+
+ ImageMagick, lcms2 - https://github.com/MolotovCherry/Android-ImageMagick7
+ (Please see the section IMAGEMAGICK near the end of this file.)
+
+We anticipate that most untested non-trivial ndk-build dependencies
+will need adjustments in Emacs to work, as the Emacs build system
+which emulates ndk-build is in an extremely early state.
+
+
+GNUTLS
+
+Modified copies of GnuTLS and its dependencies (such as libgmp,
+libtasn1, p11-kit) which can be built with the ndk-build system can be
+found at https://sourceforge.net/projects/android-ports-for-gnu-emacs.
+
+They have only been tested on arm64 Android systems running Android
+5.0 or later, and armv7l systems running Android 13 or later, so your
+mileage may vary, especially if you are trying to build Emacs for
+another kind of machine.
+
+To build Emacs with GnuTLS, you must unpack each of the following tar
+archives in that site:
+
+ gmp-6.2.1-emacs.tgz
+ gnutls-3.7.8-emacs.tar.gz
+ libtasn1-4.19.0-emacs.tar.gz
+ p11-kit-0.24.1-emacs.tar.gz
+ nettle-3.8-emacs.tar.gz
+
+and add the resulting folders to ``--with-ndk-path''. Note that you
+should not try to build these packages separately using any
+`configure' script or Makefiles inside.
+
+
+TREE-SITTER
+
+A copy of tree-sitter modified to build with the ndk-build system can
+also be found that URL. To build Emacs with tree-sitter, you must
+unpack the following tar archive in that site:
+
+ tree-sitter-0.20.7-emacs.tar.gz
+
+and add the resulting folder to ``--with-ndk-build''.
+
+
+HARFBUZZ
+
+A copy of HarfBuzz modified to build with the ndk-build system can
+also be found at that URL. To build Emacs with HarfBuzz, you must
+unpack the following tar archive in that site:
+
+ harfbuzz-7.1.0-emacs.tar.gz
+
+and add the resulting folder to ``--with-ndk-build''.
+
+
+IMAGEMAGICK
+
+There is a third party port of ImageMagick to Android. Unfortunately,
+the port also uses its own patched versions of libpng, libjpeg,
+libtiff and libwebp, which conflict with those used by Emacs. Its
+Makefiles were also written for MS Windows, so you must also apply the
+patch at the end of this file.
+
+
+
+PATCH FOR LIBXML2
+
+This patch must be applied to the Android.mk in Google's version of
+libxml2 before it can be built for Emacs. In addition, you must also
+revert the commit `edb5870767fed8712a9b77ef34097209b61ab2db'.
+
+diff --git a/Android.mk b/Android.mk
+index 07c7b372..24f67e49 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -80,6 +80,7 @@ LOCAL_SHARED_LIBRARIES := libicuuc
+ LOCAL_MODULE:= libxml2
+ LOCAL_CLANG := true
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
++LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)
+ include $(BUILD_SHARED_LIBRARY)
+
+ # For the host
+@@ -94,3 +95,5 @@ LOCAL_MODULE := libxml2
+ LOCAL_CLANG := true
+ LOCAL_ADDITIONAL_DEPENDENCIES += $(LOCAL_PATH)/Android.mk
+ include $(BUILD_HOST_STATIC_LIBRARY)
++
++$(call import-module,libicuuc)
+
+PATCH FOR ICU
+
+This patch must be applied to icu4j/Android.mk in Google's version of
+icu before it can be built for Emacs.
+
+diff --git a/icu4j/Android.mk b/icu4j/Android.mk
+index d1ab3d5..69eff81 100644
+--- a/icu4j/Android.mk
++++ b/icu4j/Android.mk
+@@ -69,7 +69,7 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
+ # Path to the ICU4C data files in the Android device file system:
+ icu4c_data := /system/usr/icu
+ icu4j_config_root := $(LOCAL_PATH)/main/classes/core/src
+-include external/icu/icu4j/adjust_icudt_path.mk
++include $(LOCAL_PATH)/adjust_icudt_path.mk
+
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(icu4j_src_files)
+
+diff --git a/icu4c/source/common/Android.mk b/icu4c/source/common/Android.mk
+index 8e5f757..44bb130 100644
+--- a/icu4c/source/common/Android.mk
++++ b/icu4c/source/common/Android.mk
+@@ -231,7 +231,7 @@ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES += $(src_files)
+ LOCAL_C_INCLUDES += $(c_includes) $(optional_android_logging_includes)
+ LOCAL_CFLAGS += $(local_cflags) -DPIC -fPIC
+-LOCAL_SHARED_LIBRARIES += libdl $(optional_android_logging_libraries)
++LOCAL_SHARED_LIBRARIES += libdl libstdc++ $(optional_android_logging_libraries)
+ LOCAL_MODULE_TAGS := optional
+ LOCAL_MODULE := libicuuc
+ LOCAL_RTTI_FLAG := -frtti
+
+PATCH FOR SQLITE3
+
+diff --git a/dist/Android.mk b/dist/Android.mk
+index bf277d2..36734d9 100644
+--- a/dist/Android.mk
++++ b/dist/Android.mk
+@@ -141,6 +141,7 @@ include $(BUILD_HOST_EXECUTABLE)
+ include $(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(common_src_files)
+ LOCAL_CFLAGS += $(minimal_sqlite_flags)
++LOCAL_EXPORT_C_INCLUDES += $(LOCAL_PATH)
+ LOCAL_MODULE:= libsqlite_static_minimal
+ LOCAL_SDK_VERSION := 23
+ include $(BUILD_STATIC_LIBRARY)
+
+diff --git a/dist/sqlite3.c b/dist/sqlite3.c
+index b0536a4..8fa1ee9 100644
+--- a/dist/sqlite3.c
++++ b/dist/sqlite3.c
+@@ -26474,7 +26474,7 @@ SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
+ */
+ #if !defined(HAVE_POSIX_FALLOCATE) \
+ && (_XOPEN_SOURCE >= 600 || _POSIX_C_SOURCE >= 200112L)
+-# define HAVE_POSIX_FALLOCATE 1
++/* # define HAVE_POSIX_FALLOCATE 1 */
+ #endif
+
+ /*
+
+PATCH FOR WEBP
+
+diff --git a/Android.mk b/Android.mk
+index c7bcb0f5..d4da1704 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -28,9 +28,10 @@ ifneq ($(findstring armeabi-v7a, $(TARGET_ARCH_ABI)),)
+ # Setting LOCAL_ARM_NEON will enable -mfpu=neon which may cause illegal
+ # instructions to be generated for armv7a code. Instead target the neon code
+ # specifically.
+- NEON := c.neon
+- USE_CPUFEATURES := yes
+- WEBP_CFLAGS += -DHAVE_CPU_FEATURES_H
++ # NEON := c.neon
++ # USE_CPUFEATURES := yes
++ # WEBP_CFLAGS += -DHAVE_CPU_FEATURES_H
++ NEON := c
+ else
+ NEON := c
+ endif
+
+PATCHES FOR SELINUX
+
+diff --git a/Android.mk b/Android.mk
+index 659232e..1e64fd6 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -116,3 +116,7 @@ LOCAL_STATIC_LIBRARIES := libselinux
+ LOCAL_WHOLE_STATIC_LIBRARIES := libpcre
+ LOCAL_C_INCLUDES := external/pcre
+ include $(BUILD_HOST_EXECUTABLE)
++
++$(call import-module,libpcre)
++$(call import-module,libpackagelistparser)
++$(call import-module,libcrypto)
+
+diff --git a/src/android.c b/src/android.c
+index 5206a9f..b351ffc 100644
+--- a/src/android.c
++++ b/src/android.c
+@@ -21,8 +21,7 @@
+ #include <selinux/label.h>
+ #include <selinux/avc.h>
+ #include <openssl/sha.h>
+-#include <private/android_filesystem_config.h>
+-#include <log/log.h>
++#include <android/log.h>
+ #include "policy.h"
+ #include "callbacks.h"
+ #include "selinux_internal.h"
+@@ -686,6 +685,7 @@ static int seapp_context_lookup(enum seapp_kind kind,
+ seinfo = parsedseinfo;
+ }
+
++#if 0
+ userid = uid / AID_USER;
+ isOwner = (userid == 0);
+ appid = uid % AID_USER;
+@@ -702,9 +702,13 @@ static int seapp_context_lookup(enum seapp_kind kind,
+ username = "_app";
+ appid -= AID_APP;
+ } else {
++#endif
+ username = "_isolated";
++ appid = 0;
++#if 0
+ appid -= AID_ISOLATED_START;
+ }
++#endif
+
+ if (appid >= CAT_MAPPING_MAX_ID || userid >= CAT_MAPPING_MAX_ID)
+ goto err;
+@@ -1662,8 +1666,10 @@ int selinux_log_callback(int type, const char *fmt, ...)
+
+ va_start(ap, fmt);
+ if (vasprintf(&strp, fmt, ap) != -1) {
++#if 0
+ LOG_PRI(priority, "SELinux", "%s", strp);
+ LOG_EVENT_STRING(AUDITD_LOG_TAG, strp);
++#endif
+ free(strp);
+ }
+ va_end(ap);
+
+PATCH FOR BORINGSSL
+
+diff --git a/Android.mk b/Android.mk
+index 3e3ef2a..277d4a9 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -27,7 +27,9 @@ LOCAL_MODULE := libcrypto
+ LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/src/include
+ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk $(LOCAL_PATH)/crypto-sources.mk
+ LOCAL_CFLAGS += -fvisibility=hidden -DBORINGSSL_SHARED_LIBRARY -DBORINGSSL_IMPLEMENTATION -DOPENSSL_SMALL -Wno-unused-parameter
++LOCAL_CFLAGS_arm = -DOPENSSL_STATIC_ARMCAP -DOPENSSL_NO_ASM
+ LOCAL_SDK_VERSION := 9
++LOCAL_LDFLAGS = --no-undefined
+ # sha256-armv4.S does not compile with clang.
+ LOCAL_CLANG_ASFLAGS_arm += -no-integrated-as
+ LOCAL_CLANG_ASFLAGS_arm64 += -march=armv8-a+crypto
+diff --git a/sources.mk b/sources.mk
+index e82f3d5..be3a3c4 100644
+--- a/sources.mk
++++ b/sources.mk
+@@ -337,20 +337,20 @@ linux_aarch64_sources := \
+ linux-aarch64/crypto/sha/sha256-armv8.S\
+ linux-aarch64/crypto/sha/sha512-armv8.S\
+
+-linux_arm_sources := \
+- linux-arm/crypto/aes/aes-armv4.S\
+- linux-arm/crypto/aes/aesv8-armx32.S\
+- linux-arm/crypto/aes/bsaes-armv7.S\
+- linux-arm/crypto/bn/armv4-mont.S\
+- linux-arm/crypto/modes/ghash-armv4.S\
+- linux-arm/crypto/modes/ghashv8-armx32.S\
+- linux-arm/crypto/sha/sha1-armv4-large.S\
+- linux-arm/crypto/sha/sha256-armv4.S\
+- linux-arm/crypto/sha/sha512-armv4.S\
+- src/crypto/chacha/chacha_vec_arm.S\
+- src/crypto/cpu-arm-asm.S\
+- src/crypto/curve25519/asm/x25519-asm-arm.S\
+- src/crypto/poly1305/poly1305_arm_asm.S\
++# linux_arm_sources := \
++# linux-arm/crypto/aes/aes-armv4.S\
++# linux-arm/crypto/aes/aesv8-armx32.S\
++# linux-arm/crypto/aes/bsaes-armv7.S\
++# linux-arm/crypto/bn/armv4-mont.S\
++# linux-arm/crypto/modes/ghash-armv4.S\
++# linux-arm/crypto/modes/ghashv8-armx32.S\
++# linux-arm/crypto/sha/sha1-armv4-large.S\
++# linux-arm/crypto/sha/sha256-armv4.S\
++# linux-arm/crypto/sha/sha512-armv4.S\
++# src/crypto/chacha/chacha_vec_arm.S\
++# src/crypto/cpu-arm-asm.S\
++# src/crypto/curve25519/asm/x25519-asm-arm.S\
++# src/crypto/poly1305/poly1305_arm_asm.S\
+
+ linux_x86_sources := \
+ linux-x86/crypto/aes/aes-586.S\
+
+PATCH FOR IMAGEMAGICK
+
+diff --git a/Android.mk b/Android.mk
+index 5ab6699..4441417 100644
+--- a/Android.mk
++++ b/Android.mk
+@@ -52,6 +52,20 @@ LZMA_LIB_PATH := $(LOCAL_PATH)/xz-5.2.4
+ BZLIB_LIB_PATH := $(LOCAL_PATH)/bzip-1.0.8
+ LCMS_LIB_PATH := $(LOCAL_PATH)/liblcms2-2.9
+
++LIBBZ2_ENABLED := true
++LIBFFTW_ENABLED := true
++LIBFREETYPE2_ENABLED := true
++LIBJPEG_TURBO_ENABLED := true
++LIBLZMA_ENABLED := true
++LIBOPENJPEG_ENABLED := true
++LIBPNG_ENABLED := true
++LIBTIFF_ENABLED := true
++LIBWEBP_ENABLED := true
++LIBXML2_ENABLED := true
++LIBZLIB_ENABLED := true
++LIBLCMS2_ENABLED := true
++BUILD_MAGICKWAND := true
++
+ #-------------------------------------------------------------
+ # Include all modules
+ #-------------------------------------------------------------
+@@ -68,6 +82,9 @@ include $(MAKE_PATH)/libjpeg-turbo.mk
+ # libopenjpeg
+ include $(MAKE_PATH)/libopenjpeg.mk
+
++# libwebp
++include $(MAKE_PATH)/libwebp.mk
++
+ # libtiff
+ include $(MAKE_PATH)/libtiff.mk
+
+@@ -77,9 +94,6 @@ include $(MAKE_PATH)/libpng.mk
+ # libfreetype2
+ include $(MAKE_PATH)/libfreetype2.mk
+
+-# libwebp
+-include $(MAKE_PATH)/libwebp.mk
+-
+ # libfftw
+ include $(MAKE_PATH)/libfftw.mk
+
+diff --git a/libjpeg-turbo-2.0.2/jconfig.h b/libjpeg-turbo-2.0.2/jconfig.h
+index 47d14c9..5c6f8ee 100644
+--- a/libjpeg-turbo-2.0.2/jconfig.h
++++ b/libjpeg-turbo-2.0.2/jconfig.h
+@@ -1,57 +1,43 @@
+-/* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS */
++/* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS */
+ #ifndef JPEG_LIB_VERSION
+ #define JPEG_LIB_VERSION 62
+ #endif
+-
+ #ifndef LIBJPEG_TURBO_VERSION
+ #define LIBJPEG_TURBO_VERSION 2.0.2
+ #endif
+-
+ #ifndef LIBJPEG_TURBO_VERSION_NUMBER
+ #define LIBJPEG_TURBO_VERSION_NUMBER 202
+ #endif
+-
+ #ifndef C_ARITH_CODING_SUPPORTED
+ #define C_ARITH_CODING_SUPPORTED
+ #endif
+-
+ #ifndef D_ARITH_CODING_SUPPORTED
+ #define D_ARITH_CODING_SUPPORTED
+ #endif
+-
+ #ifndef MEM_SRCDST_SUPPORTED
+ #define MEM_SRCDST_SUPPORTED
+ #endif
+-
+ #ifndef WITH_SIMD
+ #define WITH_SIMD
+ #endif
+-
+ #ifndef BITS_IN_JSAMPLE
+ #define BITS_IN_JSAMPLE 8
+ #endif
+-
+ #ifndef HAVE_LOCALE_H
+ #define HAVE_LOCALE_H
+ #endif
+-
+ #ifndef HAVE_STDDEF_H
+ #define HAVE_STDDEF_H
+ #endif
+-
+ #ifndef HAVE_STDLIB_H
+ #define HAVE_STDLIB_H
+ #endif
+-
+ #ifndef NEED_SYS_TYPES_H
+ #define NEED_SYS_TYPES_H
+ #endif
+-
+ #ifndef HAVE_UNSIGNED_CHAR
+ #define HAVE_UNSIGNED_CHAR
+ #endif
+-
+ #ifndef HAVE_UNSIGNED_SHORT
+ #define HAVE_UNSIGNED_SHORT
+ #endif
+-
+diff --git a/libxml2-2.9.9/encoding.c b/libxml2-2.9.9/encoding.c
+index a3aaf10..60f165b 100644
+--- a/libxml2-2.9.9/encoding.c
++++ b/libxml2-2.9.9/encoding.c
+@@ -2394,7 +2394,6 @@ xmlCharEncOutput(xmlOutputBufferPtr output, int init)
+ {
+ int ret;
+ size_t written;
+- size_t writtentot = 0;
+ size_t toconv;
+ int c_in;
+ int c_out;
+@@ -2451,7 +2450,6 @@ retry:
+ xmlBufContent(in), &c_in);
+ xmlBufShrink(in, c_in);
+ xmlBufAddLen(out, c_out);
+- writtentot += c_out;
+ if (ret == -1) {
+ if (c_out > 0) {
+ /* Can be a limitation of iconv or uconv */
+@@ -2536,7 +2534,6 @@ retry:
+ }
+
+ xmlBufAddLen(out, c_out);
+- writtentot += c_out;
+ goto retry;
+ }
+ }
+@@ -2567,9 +2564,7 @@ xmlCharEncOutFunc(xmlCharEncodingHandler *handler, xmlBufferPtr out,
+ xmlBufferPtr in) {
+ int ret;
+ int written;
+- int writtentot = 0;
+ int toconv;
+- int output = 0;
+
+ if (handler == NULL) return(-1);
+ if (out == NULL) return(-1);
+@@ -2612,7 +2607,6 @@ retry:
+ in->content, &toconv);
+ xmlBufferShrink(in, toconv);
+ out->use += written;
+- writtentot += written;
+ out->content[out->use] = 0;
+ if (ret == -1) {
+ if (written > 0) {
+@@ -2622,8 +2616,6 @@ retry:
+ ret = -3;
+ }
+
+- if (ret >= 0) output += ret;
+-
+ /*
+ * Attempt to handle error cases
+ */
+@@ -2700,7 +2692,6 @@ retry:
+ }
+
+ out->use += written;
+- writtentot += written;
+ out->content[out->use] = 0;
+ goto retry;
+ }
+diff --git a/libxml2-2.9.9/xpath.c b/libxml2-2.9.9/xpath.c
+index 5e3bb9f..505ec82 100644
+--- a/libxml2-2.9.9/xpath.c
++++ b/libxml2-2.9.9/xpath.c
+@@ -10547,7 +10547,7 @@ xmlXPathCompFilterExpr(xmlXPathParserContextPtr ctxt) {
+
+ static xmlChar *
+ xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
+- int len = 0, l;
++ int l;
+ int c;
+ const xmlChar *cur;
+ xmlChar *ret;
+@@ -10567,7 +10567,6 @@ xmlXPathScanName(xmlXPathParserContextPtr ctxt) {
+ (c == '_') || (c == ':') ||
+ (IS_COMBINING(c)) ||
+ (IS_EXTENDER(c)))) {
+- len += l;
+ NEXTL(l);
+ c = CUR_CHAR(l);
+ }
+diff --git a/make/libicu4c.mk b/make/libicu4c.mk
+index 21ec121..8b77865 100644
+--- a/make/libicu4c.mk
++++ b/make/libicu4c.mk
+@@ -250,7 +250,7 @@ LOCAL_MODULE := libicuuc
+ LOCAL_SRC_FILES := $(src_files)
+
+ # when built in android, they require uconfig_local (because of android project), but we don't need this
+-$(shell > $(ICU_COMMON_PATH)/unicode/uconfig_local.h echo /* Autogenerated stub file to make libicuuc build happy */) \
++$(shell > $(ICU_COMMON_PATH)/unicode/uconfig_local.h echo /\* Autogenerated stub file to make libicuuc build happy \*/) \
+
+ ifeq ($(LIBXML2_ENABLED),true)
+ include $(BUILD_STATIC_LIBRARY)
+diff --git a/make/libjpeg-turbo.mk b/make/libjpeg-turbo.mk
+index d39dd41..fdebcf3 100644
+--- a/make/libjpeg-turbo.mk
++++ b/make/libjpeg-turbo.mk
+@@ -230,30 +230,30 @@ JCONFIG_FLAGS += \
+ HAVE_UNSIGNED_SHORT
+
+ JCONFIGINT_FLAGS += \
+- BUILD="20190814" \
+- PACKAGE_NAME="libjpeg-turbo" \
+- VERSION="2.0.2"
++ BUILD=\"20190814\" \
++ PACKAGE_NAME=\"libjpeg-turbo\" \
++ VERSION=\"2.0.2\"
+
+ # originally defined in jconfigint.h, but the substitution has problems with spaces
+ LOCAL_CFLAGS := \
+ -DINLINE="inline __attribute__((always_inline))"
+
+ # create definition file jconfig.h, needed in order to build
+-$(shell echo /* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS */ > $(JPEG_LIB_PATH)/jconfig.h)
++$(shell echo \/\* autogenerated jconfig.h based on Android.mk var JCONFIG_FLAGS \*\/ > $(JPEG_LIB_PATH)/jconfig.h)
+ $(foreach name,$(JCONFIG_FLAGS), \
+ $(if $(findstring =,$(name)), \
+- $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #ifndef $(firstword $(subst =, ,$(name)))) \
++ $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#ifndef $(firstword $(subst =, ,$(name)))) \
+ , \
+- $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #ifndef $(name)) \
++ $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#ifndef $(name)) \
+ ) \
+- $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #define $(subst =, ,$(name))) \
+- $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo #endif) \
++ $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#define $(subst =, ,$(name))) \
++ $(shell >>$(JPEG_LIB_PATH)/jconfig.h echo \#endif) \
+ $(shell >> $(JPEG_LIB_PATH)/jconfig.h echo.) \
+ )
+
+ # create definition file jconfigint.h, needed in order to build
+-$(shell >$(JPEG_LIB_PATH)/jconfigint.h echo /* autogenerated jconfigint.h based on Android.mk vars JCONFIGINT_FLAGS */)
+-$(foreach name,$(JCONFIGINT_FLAGS),$(shell >>$(JPEG_LIB_PATH)/jconfigint.h echo #define $(subst =, ,$(name))))
++$(shell >$(JPEG_LIB_PATH)/jconfigint.h echo /\* autogenerated jconfigint.h based on Android.mk vars JCONFIGINT_FLAGS \*/)
++$(foreach name,$(JCONFIGINT_FLAGS),$(shell >>$(JPEG_LIB_PATH)/jconfigint.h echo \#define $(subst =, ,$(name))))
+
+ ifeq ($(LIBJPEG_TURBO_ENABLED),true)
+ include $(BUILD_STATIC_LIBRARY)
+diff --git a/make/liblcms2.mk b/make/liblcms2.mk
+index e1fd3b9..29ca791 100644
+--- a/make/liblcms2.mk
++++ b/make/liblcms2.mk
+@@ -10,6 +10,10 @@ LOCAL_C_INCLUDES := \
+ $(LCMS_LIB_PATH)/include \
+ $(LCMS_LIB_PATH)/src
+
++LOCAL_EXPORT_C_INCLUDES := \
++ $(LCMS_LIB_PATH) \
++ $(LCMS_LIB_PATH)/include \
++ $(LCMS_LIB_PATH)/src
+
+ LOCAL_CFLAGS := \
+ -DHAVE_FUNC_ATTRIBUTE_VISIBILITY=1 \
+diff --git a/make/libmagick++-7.mk b/make/libmagick++-7.mk
+index 5352ccb..929396d 100644
+--- a/make/libmagick++-7.mk
++++ b/make/libmagick++-7.mk
+@@ -12,7 +12,7 @@ LOCAL_C_INCLUDES := \
+
+ ifneq ($(STATIC_BUILD),true)
+ LOCAL_LDFLAGS += -fexceptions
+- LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
++ LOCAL_LDLIBS := -llog -lz
+ endif
+
+ LOCAL_SRC_FILES := \
+diff --git a/make/libmagickcore-7.mk b/make/libmagickcore-7.mk
+index 81293b2..d51fced 100644
+--- a/make/libmagickcore-7.mk
++++ b/make/libmagickcore-7.mk
+@@ -25,6 +25,7 @@ else ifeq ($(TARGET_ARCH_ABI),x86_64)
+
+ endif
+
++LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)
+
+ LOCAL_C_INCLUDES += \
+ $(IMAGE_MAGICK) \
+@@ -45,10 +46,9 @@ LOCAL_C_INCLUDES += \
+ $(BZLIB_LIB_PATH) \
+ $(LCMS_LIB_PATH)/include
+
+-
+ ifneq ($(STATIC_BUILD),true)
+ # ignored in static library builds
+- LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
++ LOCAL_LDLIBS := -llog -lz
+ endif
+
+
+diff --git a/make/libmagickwand-7.mk b/make/libmagickwand-7.mk
+index 7be2fb6..0bbcca5 100644
+--- a/make/libmagickwand-7.mk
++++ b/make/libmagickwand-7.mk
+@@ -14,7 +14,7 @@ LOCAL_C_INCLUDES := \
+
+ # always ignored in static builds
+ ifneq ($(STATIC_BUILD),true)
+- LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
++ LOCAL_LDLIBS := -llog -lz
+ endif
+
+ LOCAL_SRC_FILES := \
+@@ -54,6 +54,29 @@ ifeq ($(OPENCL_BUILD),true)
+ LOCAL_SHARED_LIBRARIES += libopencl
+ endif
+
++LOCAL_SHARED_LIBRARIES += libstdc++
++
++ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
++ LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm64
++ LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm64
++else ifeq ($(TARGET_ARCH_ABI),armeabi-v7a)
++ LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm
++ LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/arm
++else ifeq ($(TARGET_ARCH_ABI),x86)
++ LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86
++ LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86
++else ifeq ($(TARGET_ARCH_ABI),x86_64)
++ LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86-64
++ LOCAL_C_INCLUDES += $(IMAGE_MAGICK)/configs/x86-64
++
++ ifneq ($(STATIC_BUILD),true)
++ LOCAL_LDFLAGS += -latomic
++ endif
++
++endif
++
++LOCAL_EXPORT_C_INCLUDES += $(IMAGE_MAGICK)
++
+ ifeq ($(BUILD_MAGICKWAND),true)
+ ifeq ($(STATIC_BUILD),true)
+ LOCAL_STATIC_LIBRARIES := \
+diff --git a/make/libpng.mk b/make/libpng.mk
+index 24fb8ac..dda05fd 100644
+--- a/make/libpng.mk
++++ b/make/libpng.mk
+@@ -30,6 +30,7 @@ ifeq ($(TARGET_ARCH_ABI), arm64-v8a)
+ endif # TARGET_ARCH_ABI == arm64-v8a
+
+
++LOCAL_EXPORT_C_INCLUDES := $(PNG_LIB_PATH)
+ LOCAL_C_INCLUDES := $(PNG_LIB_PATH)
+
+ LOCAL_SRC_FILES += \
+diff --git a/make/libtiff.mk b/make/libtiff.mk
+index ca43f25..2b17508 100644
+--- a/make/libtiff.mk
++++ b/make/libtiff.mk
+@@ -12,6 +12,9 @@ LOCAL_C_INCLUDES := \
+ $(LZMA_LIB_PATH)/liblzma/api \
+ $(WEBP_LIB_PATH)/src
+
++LOCAL_EXPORT_C_INCLUDES := \
++ $(TIFF_LIB_PATH)
++
+ ifeq ($(LIBLZMA_ENABLED),true)
+ LOCAL_CFLAGS += -DLZMA_SUPPORT=1
+ endif
+diff --git a/make/magick.mk b/make/magick.mk
+index 3ba4b1d..5471608 100644
+--- a/make/magick.mk
++++ b/make/magick.mk
+@@ -18,7 +18,7 @@ LOCAL_C_INCLUDES := \
+ $(FREETYPE_LIB_PATH)/include
+
+
+-LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog -lz
++LOCAL_LDLIBS := -llog -lz
+ LOCAL_SRC_FILES := \
+ $(IMAGE_MAGICK)/utilities/magick.c \
+
+
+
+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/>.
diff --git a/java/Makefile.in b/java/Makefile.in
new file mode 100644
index 00000000000..84173cd9655
--- /dev/null
+++ b/java/Makefile.in
@@ -0,0 +1,331 @@
+### @configure_input@
+
+# 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/>.
+
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+srcdir = @srcdir@
+builddir = @builddir@
+version = @version@
+
+# Don't install movemail if mailutils are to be used.
+emacs_use_mailutils = @emacs_use_mailutils@
+
+# 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@
+JAVAC = @JAVAC@
+AAPT = @AAPT@
+D8 = @D8@
+ZIPALIGN = @ZIPALIGN@
+JARSIGNER = @JARSIGNER@
+APKSIGNER = @APKSIGNER@
+JARSIGNER_FLAGS =
+ANDROID_JAR = @ANDROID_JAR@
+ANDROID_ABI = @ANDROID_ABI@
+ANDROID_SDK_18_OR_EARLIER = @ANDROID_SDK_18_OR_EARLIER@
+ANDROID_SDK_8_OR_EARLIER = @ANDROID_SDK_8_OR_EARLIER@
+WARN_JAVAFLAGS = @WARN_JAVAFLAGS@
+JAVAFLAGS = $(WARN_JAVAFLAGS) -classpath "$(ANDROID_JAR):$(srcdir)"
+FIND_DELETE = @FIND_DELETE@
+
+# 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
+
+# When building Emacs for Android 2.2, assets must not be compressed.
+# Otherwise, the asset manager fails to extract files larger than 1
+# MB.
+
+ifneq (,$(ANDROID_SDK_8_OR_EARLIER))
+AAPT_ASSET_ARGS = -0 ""
+else
+AAPT_ASSET_ARGS =
+endif
+
+SIGN_EMACS = -keystore $(srcdir)/emacs.keystore -storepass \
+ emacs1 $(JARSIGNER_FLAGS)
+SIGN_EMACS_V2 = sign --v2-signing-enabled --ks \
+ $(srcdir)/emacs.keystore -debuggable-apk-permitted \
+ --ks-pass pass:emacs1
+
+JAVA_FILES := $(wildcard $(srcdir)/org/gnu/emacs/*.java)
+RESOURCE_FILES := $(foreach file,$(wildcard $(srcdir)/res/*), \
+ $(wildcard $(file)/*))
+
+# R.java is a file generated by the `aapt' utility containing
+# constants that can then be used to locate ``resource identifiers''.
+# It is not a regular file and should not be compiled as Java source
+# code. Instead, it is automatically included by the Java compiler.
+RESOURCE_FILE := $(srcdir)/org/gnu/emacs/R.java
+
+# CLASS_FILES is what should actually be built and included in the
+# resulting Emacs executable. The Java compiler might generate more
+# than one class file for each source file, so this only serves as a
+# list of dependencies for Make.
+CLASS_FILES := $(foreach file,$(JAVA_FILES),$(basename $(file)).class)
+
+# Remove RESOURCE_FILE from JAVA_FILES, if it is already present.
+JAVA_FILES := $(filter-out $(RESOURCE_FILE),$(JAVA_FILES))
+
+# Compute the name for the Emacs application package. This should be:
+# emacs-<version>-<min-sdk>-<abi>.apk
+
+ANDROID_MIN_SDK := @ANDROID_MIN_SDK@
+APK_NAME := emacs-$(version)-$(ANDROID_MIN_SDK)-$(ANDROID_ABI).apk
+
+# How this stuff works.
+
+# emacs.apk depends on emacs.apk-in, which is simply a ZIP archive
+# containing the following files:
+# lib/$(ANDROID_ABI)/libemacs.so
+# lib/$(ANDROID_ABI)/libandroid-emacs.so
+# lib/$(ANDROID_ABI)/libctags.so
+# lib/$(ANDROID_ABI)/libetags.so
+# lib/$(ANDROID_ABI)/libhexl.so
+# lib/$(ANDROID_ABI)/libmovemail.so
+# lib/$(ANDROID_ABI)/librcs2log.so
+# lib/$(ANDROID_ABI)/libebrowse.so
+# assets/info/
+# assets/etc/
+# assets/lisp/
+
+.PHONY: emacs.apk-in all
+all: $(APK_NAME)
+
+# Binaries to cross-compile.
+CROSS_SRC_BINS := $(top_builddir)/cross/src/android-emacs
+CROSS_LIBSRC_BINS := $(top_builddir)/cross/lib-src/ctags \
+ $(top_builddir)/cross/lib-src/hexl \
+ $(top_builddir)/cross/lib-src/ebrowse \
+ $(top_builddir)/cross/lib-src/emacsclient \
+ $(top_builddir)/cross/lib-src/etags
+CROSS_LIBSRC_BINS_MOVEMAIL := $(top_builddir)/cross/lib-src/movemail
+CROSS_EXEC_BINS := $(top_builddir)/exec/exec1 $(top_builddir)/exec/loader
+CROSS_BINS = $(CROSS_SRC_BINS) $(CROSS_LIBSRC_BINS) $(CROSS_EXEC_BINS)
+
+ifneq ($(emacs_use_mailutils),yes)
+CROSS_LIBSRC_BINS := $(CROSS_LIBSRC_BINS) $(CROSS_LIBSRC_BINS_MOVEMAIL)
+endif
+
+# Libraries to cross-compile.
+CROSS_LIBS = $(top_builddir)/cross/src/libemacs.so
+
+# Make sure gnulib is built first!
+# If not, then the recursive invocations of make below will try to
+# build gnulib at the same time.
+CROSS_ARCHIVES = $(top_builddir)/cross/lib/libgnu.a
+
+# Third party libraries to compile.
+-include $(top_builddir)/cross/ndk-build/ndk-build.mk
+
+.PHONY: $(CROSS_BINS) $(CROSS_LIBS) $(CROSS_ARCHIVES)
+
+# There should only be a single invocation of $(MAKE) -C
+# $(top_srcdir)/cross for each directory under $(top_srcdir)/cross.
+$(CROSS_SRC_BINS) $(CROSS_LIBS) &: $(CROSS_ARCHIVES)
+ $(MAKE) -C $(top_builddir)/cross $(foreach file, \
+ $(CROSS_SRC_BINS) \
+ $(CROSS_LIBS), \
+ src/$(notdir $(file)))
+
+$(CROSS_LIBSRC_BINS) &: $(CROSS_ARCHIVES)
+ $(MAKE) -C $(top_builddir)/cross $(foreach file, \
+ $(CROSS_LIBSRC_BINS), \
+ lib-src/$(notdir $(file)))
+
+$(CROSS_ARCHIVES):
+ $(MAKE) -C $(top_builddir)/cross lib/libgnu.a
+
+# These two binaries are helpers used to execute binaries on Android
+# 10 and later.
+
+$(CROSS_EXEC_BINS) &:
+ $(MAKE) -C $(top_builddir)/exec $(notdir $(CROSS_EXEC_BINS))
+
+# 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 $@)
+
+# install_tmp is a directory used to generate emacs.apk-in.
+# That is then packaged into $(APK_NAME).
+# There is no need to depend on NDK_BUILD_SHARED as libemacs.so
+# does already.
+
+.PHONY: install_temp install_temp/assets/directory-tree
+install_temp: $(CROSS_BINS) $(CROSS_LIBS) $(RESOURCE_FILES)
+ $(AM_V_GEN)
+# Make the working directory for this stuff
+ $(AM_V_SILENT) rm -rf install_temp
+ $(AM_V_SILENT) mkdir -p install_temp/lib/$(ANDROID_ABI)
+ $(AM_V_SILENT) mkdir -p install_temp/assets/etc
+ $(AM_V_SILENT) mkdir -p install_temp/assets/lisp
+ $(AM_V_SILENT) mkdir -p install_temp/assets/info
+# Install architecture independents to assets/etc and assets/lisp
+ $(AM_V_SILENT) cp -r $(top_srcdir)/lisp install_temp/assets
+ $(AM_V_SILENT) cp -r $(top_srcdir)/etc install_temp/assets
+ $(AM_V_SILENT) cp -r $(top_srcdir)/info install_temp/assets
+# Remove undesirable files from those directories.
+ $(AM_V_SILENT) \
+ for subdir in `find install_temp -type d -print`; do \
+ chmod a+rx $${subdir} ; \
+ rm -rf $${subdir}/.gitignore ; \
+ rm -rf $${subdir}/.DS_Store ; \
+ rm -rf $${subdir}/#* ; \
+ rm -rf $${subdir}/.#* ; \
+ rm -rf $${subdir}/*~ ; \
+ rm -rf $${subdir}/*.orig ; \
+ rm -rf $${subdir}/ChangeLog* ; \
+ rm -rf $${subdir}/[mM]akefile*[.-]in ; \
+ rm -rf $${subdir}/Makefile; \
+ done
+# Generate the directory tree for those directories.
+# Install architecture dependents to lib/$(ANDROID_ABI). This
+# perculiar naming scheme is required to make Android preserve these
+# binaries upon installation.
+ $(AM_V_SILENT) \
+ for file in $(CROSS_BINS); do \
+ if [ -x $$file ]; then \
+ filename=`basename $$file`; \
+ cp -f $$file install_temp/lib/$(ANDROID_ABI)/lib$${filename}.so; \
+ fi \
+ done
+ $(AM_V_SILENT) \
+ for file in $(CROSS_LIBS); do \
+ if [ -x $$file ]; then \
+ cp -f $$file install_temp/lib/$(ANDROID_ABI); \
+ fi \
+ done
+ifneq ($(NDK_BUILD_SHARED),)
+ $(AM_V_SILENT) cp -f $(NDK_BUILD_SHARED) \
+ install_temp/lib/$(ANDROID_ABI)
+endif
+
+install_temp/assets/directory-tree: $(libsrc)/asset-directory-tool \
+ install_temp install_temp/assets/version
+ $(AM_V_GEN) $(libsrc)/asset-directory-tool install_temp/assets \
+ install_temp/assets/directory-tree
+
+install_temp/assets/version: install_temp
+ $(AM_V_GEN) { (cd $(top_srcdir) \
+ && git rev-parse HEAD || echo "Unknown") \
+ && (git rev-parse --abbrev-ref HEAD \
+ || echo "Unknown") } 2> /dev/null > $@
+
+install_temp/assets/build_info: install_temp
+ $(AM_V_GEN) { hostname; date +%s; } > $@
+
+emacs.apk-in: install_temp install_temp/assets/directory-tree \
+ install_temp/assets/version install_temp/assets/build_info \
+ AndroidManifest.xml
+# Package everything. Specifying the assets on this command line is
+# necessary for AAssetManager_getNextFileName to work on old versions
+# of Android. Make sure not to generate R.java, as it's already been
+# generated.
+ $(AM_V_AAPT) $(AAPT) p -I "$(ANDROID_JAR)" -F $@ \
+ -f -M AndroidManifest.xml $(AAPT_ASSET_ARGS) \
+ -A install_temp/assets \
+ -S $(top_srcdir)/java/res -J install_temp
+ $(AM_V_SILENT) pushd install_temp &> /dev/null; \
+ $(AAPT) add ../$@ `find lib -type f`; \
+ popd &> /dev/null
+ $(AM_V_SILENT) rm -rf install_temp
+
+# Makefile itself.
+.PRECIOUS: $(top_srcdir)/config.status Makefile
+$(top_srcdir)/config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
+ $(MAKE) -C $(dir $@) $(notdir $@)
+Makefile: $(top_srcdir)/config.status $(top_srcdir)/java/Makefile.in
+ $(MAKE) -C .. java/$@
+
+# AndroidManifest.xml:
+AndroidManifest.xml: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4 \
+ $(srcdir)/AndroidManifest.xml.in
+ pushd ..; ./config.status java/AndroidManifest.xml; popd
+
+# R.java:
+$(RESOURCE_FILE): $(RESOURCE_FILES)
+ $(AM_V_GEN) $(AAPT) p -I "$(ANDROID_JAR)" -f \
+ -J $(dir $@) -M AndroidManifest.xml \
+ -S $(top_srcdir)/java/res
+
+# Make all class files depend on R.java being built.
+$(CLASS_FILES): $(RESOURCE_FILE)
+
+.SUFFIXES: .java .class
+$(CLASS_FILES) &: $(JAVA_FILES)
+ $(AM_V_JAVAC) $(JAVAC) $(JAVAFLAGS) $(JAVA_FILES)
+ $(AM_V_SILENT) touch $(CLASS_FILES)
+
+# N.B. that find must be called all over again in case javac generated
+# nested classes.
+
+classes.dex: $(CLASS_FILES)
+ $(AM_V_D8) $(D8) --classpath $(ANDROID_JAR) \
+ $(subst $$,\$$,$(shell find $(srcdir) -type f \
+ -name *.class)) --output $(builddir)
+
+# When emacs.keystore expires, regenerate it with:
+#
+# keytool -genkey -v -keystore emacs.keystore -alias "Emacs keystore" \
+# -keyalg RSA -sigalg SHA1withRSA -keysize 2048 -validity 100000
+
+.PHONY: clean maintainer-clean
+
+$(APK_NAME): classes.dex emacs.apk-in $(srcdir)/emacs.keystore
+ $(AM_V_GEN)
+ $(AM_V_SILENT) cp -f emacs.apk-in $@.unaligned
+ $(AM_V_SILENT) $(AAPT) add $@.unaligned classes.dex
+ $(AM_V_SILENT) $(JARSIGNER) $(SIGN_EMACS) $@.unaligned "Emacs keystore"
+ $(AM_V_SILENT) $(ZIPALIGN) -f 4 $@.unaligned $@
+# Signing must happen after alignment!
+ $(AM_V_SILENT) $(APKSIGNER) $(SIGN_EMACS_V2) $@
+ $(AM_V_SILENT) rm -f $@.unaligned *.idsig
+
+# TAGS generation.
+
+ETAGS = $(top_builddir)/lib-src/etags
+
+$(ETAGS): FORCE
+ $(MAKE) -C ../lib-src $(notdir $@)
+
+tagsfiles = $(JAVA_FILES) $(RESOURCE_FILE)
+
+.PHONY: tags FORCE
+tags: TAGS
+TAGS: $(ETAGS) $(tagsfiles)
+ $(AM_V_GEN) $(ETAGS) $(tagsfiles)
+
+clean:
+ rm -f *.apk emacs.apk-in *.dex *.unaligned *.class *.idsig
+ rm -rf install-temp $(RESOURCE_FILE) TAGS
+ find . -name '*.class' $(FIND_DELETE)
+
+maintainer-clean distclean bootstrap-clean: clean
+ rm -f Makefile ndk-build.mk
diff --git a/java/README b/java/README
new file mode 100644
index 00000000000..96271279c28
--- /dev/null
+++ b/java/README
@@ -0,0 +1,908 @@
+This directory holds the Java sources of the port of GNU Emacs to
+Android-like systems, along with files needed to create an application
+package out of them. If you need to build this port, please read the
+file INSTALL in this directory.
+
+The ``org/gnu/emacs'' subdirectory contains the Java sources under the
+``org.gnu.emacs'' package identifier.
+
+``AndroidManifest.xml'' contains a manifest describing the Java
+sources to the system.
+
+The ``res'' directory contains resources, mainly the Emacs icon and
+several ``boolean resources'' which are used as a form of conditional
+evaluation for manifest entries.
+
+`emacs.keystore' is the signing key used to build Emacs. It is kept
+here, and we encourage all people redistributing Emacs to use this
+key. It holds no security value, and otherwise it will be impossible
+to install different builds of Emacs on top of each other.
+
+Please keep the Java code indented with tabs and formatted according
+to the rules for C code in the GNU coding standards. Always use
+C-style comments.
+
+======================================================================
+
+OVERVIEW OF JAVA
+
+Emacs developers do not know Java, and there is no reason they should
+have to. Thus, the code in this directory is confined to what is
+strictly necessary to support Emacs, and only uses a subset of Java
+written in a way that is easily understandable to C programmers.
+
+Java is required because the entire Android runtime is based around
+Java, and there is no way to write an Android program which runs
+without Java.
+
+This text exists to prime other Emacs developers, already familar with
+C, on the basic architecture of the Android port, and to teach them
+how to read and write the Java code found in this directory.
+
+Java is an object oriented language with automatic memory management
+compiled down to bytecode, which is then subject to interpretation by
+a Java virtual machine.
+
+What that means, is that:
+
+struct emacs_window
+{
+ int some_fields;
+ int of_emacs_window;
+};
+
+static void
+do_something_with_emacs_window (struct emacs_window *a, int n)
+{
+ a->some_fields = a->of_emacs_window + n;
+}
+
+would be written:
+
+public class EmacsWindow
+{
+ public int someFields;
+ public int ofEmacsWindow;
+
+ public void
+ doSomething (int n)
+ {
+ someFields = ofEmacsWindow + n;
+ }
+}
+
+and instead of doing:
+
+do_something_with_emacs_window (my_window, 1);
+
+you say:
+
+myWindow.doSomething (1);
+
+In addition to functions associated with an object of a given class
+(such as EmacsWindow), Java also has two other kinds of functions.
+
+The first are so-called ``static'' functions (the static means
+something entirely different from what it does in C.)
+
+A static function, while still having to be defined within a class,
+can be called without any object. Instead of the object, you write
+the name of the Java class within which it is defined. For example,
+the following C code:
+
+int
+multiply_a_with_b_and_then_add_c (int a, int b, int c)
+{
+ return a * b + c;
+}
+
+would be:
+
+public class EmacsSomething
+{
+ public static int
+ multiplyAWithBAndThenAddC (int a, int b, int c)
+ {
+ return a * b + c;
+ }
+};
+
+Then, instead of calling:
+
+int foo;
+
+foo = multiply_a_with_b_then_add_c (1, 2, 3);
+
+you say:
+
+int foo;
+
+foo = EmacsSomething.multiplyAWithBAndThenAddC (1, 2, 3);
+
+In Java, ``static'' does not mean that the function is only used
+within its compilation unit! Instead, the ``private'' qualifier is
+used to mean more or less the same thing:
+
+static void
+this_procedure_is_only_used_within_this_file (void)
+{
+ do_something ();
+}
+
+becomes
+
+public class EmacsSomething
+{
+ private static void
+ thisProcedureIsOnlyUsedWithinThisClass ()
+ {
+
+ }
+}
+
+the other kind are called ``constructors''. They are functions that
+must be called to allocate memory to hold a class:
+
+public class EmacsFoo
+{
+ int bar;
+
+ public
+ EmacsFoo (int tokenA, int tokenB)
+ {
+ bar = tokenA + tokenB;
+ }
+}
+
+now, the following statement:
+
+EmacsFoo foo;
+
+foo = new EmacsFoo (1, 2);
+
+becomes more or less equivalent to the following C code:
+
+struct emacs_foo
+{
+ int bar;
+};
+
+struct emacs_foo *
+make_emacs_foo (int token_a, int token_b)
+{
+ struct emacs_foo *foo;
+
+ foo = xmalloc (sizeof *foo);
+ foo->bar = token_a + token_b;
+
+ return foo;
+}
+
+/* ... */
+
+struct emacs_foo *foo;
+
+foo = make_emacs_foo (1, 2);
+
+A class may have any number of constructors, or no constructors at
+all, in which case the compiler inserts an empty constructor.
+
+
+
+Sometimes, you will see Java code that looks like this:
+
+ allFiles = filesDirectory.listFiles (new FileFilter () {
+ @Override
+ public boolean
+ accept (File file)
+ {
+ return (!file.isDirectory ()
+ && file.getName ().endsWith (".pdmp"));
+ }
+ });
+
+This is Java's version of GCC's nested function extension. The major
+difference is that the nested function may still be called even after
+it goes out of scope, and always retains a reference to the class and
+local variables around where it was called.
+
+Being an object-oriented language, Java also allows defining that a
+class ``extends'' another class. The following C code:
+
+struct a
+{
+ long thirty_two;
+};
+
+struct b
+{
+ struct a a;
+ long long sixty_four;
+};
+
+extern void do_something (struct a *);
+
+void
+my_function (struct b *b)
+{
+ do_something (&b->a);
+}
+
+is roughly equivalent to the following Java code, split into two
+files:
+
+ A.java
+
+public class A
+{
+ int thirtyTwo;
+
+ public void
+ doSomething ()
+ {
+ etcEtcEtc ();
+ }
+};
+
+ B.java
+
+public class B extends A
+{
+ long sixty_four;
+
+ public static void
+ myFunction (B b)
+ {
+ b.doSomething ();
+ }
+}
+
+the Java runtime has transformed the call to ``b.doSomething'' to
+``((A) b).doSomething''.
+
+However, Java also allows overriding this behavior, by specifying the
+@Override keyword:
+
+public class B extends A
+{
+ long sixty_four;
+
+ @Override
+ public void
+ doSomething ()
+ {
+ Something.doSomethingTwo ();
+ super.doSomething ();
+ }
+}
+
+now, any call to ``doSomething'' on a ``B'' created using ``new B ()''
+will end up calling ``Something.doSomethingTwo'', before calling back
+to ``A.doSomething''. This override also applies in reverse; that is
+to say, even if you write:
+
+ ((A) b).doSomething ();
+
+B's version of doSomething will still be called, if ``b'' was created
+using ``new B ()''.
+
+This mechanism is used extensively throughout the Java language and
+Android windowing APIs.
+
+Elsewhere, you will encounter Java code that defines arrays:
+
+public class EmacsFrobinicator
+{
+ public static void
+ emacsFrobinicate (int something)
+ {
+ int[] primesFromSomething;
+
+ primesFromSomething = new int[numberOfPrimes];
+ /* ... */
+ }
+}
+
+Java arrays are similar to C arrays in that they can not grow. But
+they are very much unlike C arrays in that they are always references
+(as opposed to decaying into pointers in only some situations), and
+contain information about their length.
+
+If another function named ``frobinicate1'' takes an array as an
+argument, then it need not take the length of the array.
+
+Instead, it may simply iterate over the array like so:
+
+int i, k;
+
+for (i = 0; i < array.length; ++i)
+ {
+ k = array[i];
+
+ Whatever.doSomethingWithK (k);
+ }
+
+The syntax used to define arrays is also slightly different. As
+arrays are always references, there is no way for you to tell the
+runtime to allocate an array of size N in a structure (class.)
+
+Instead, if you need an array of that size, you must declare a field
+with the type of the array, and allocate the array inside the class's
+constructor, like so:
+
+public class EmacsArrayContainer
+{
+ public int[] myArray;
+
+ public
+ EmacsArrayContainer ()
+ {
+ myArray = new array[10];
+ }
+}
+
+while in C, you could just have written:
+
+struct emacs_array_container
+{
+ int my_array[10];
+};
+
+or, possibly even better,
+
+typedef int emacs_array_container[10];
+
+Alas, Java has no equivalent of `typedef'.
+
+Like in C, Java string literals are delimited by double quotes.
+Unlike C, however, strings are not NULL-terminated arrays of
+characters, but a distinct type named ``String''. They store their
+own length, characters in Java's 16-bit ``char'' type, and are capable
+of holding NULL bytes.
+
+Instead of writing:
+
+wchar_t character;
+extern char *s;
+size_t s;
+
+ for (/* determine n, s in a loop. */)
+ s += mbstowc (&character, s, n);
+
+or:
+
+const char *byte;
+
+for (byte = my_string; *byte; ++byte)
+ /* do something with *byte. */;
+
+or perhaps even:
+
+size_t length, i;
+char foo;
+
+length = strlen (my_string);
+
+for (i = 0; i < length; ++i)
+ foo = my_string[i];
+
+you write:
+
+char foo;
+int i;
+
+for (i = 0; i < myString.length (); ++i)
+ foo = myString.charAt (0);
+
+Java also has stricter rules on what can be used as a truth value in a
+conditional. While in C, any non-zero value is true, Java requires
+that every truth value be of the boolean type ``boolean''.
+
+What this means is that instead of simply writing:
+
+ if (foo || bar)
+
+where foo can either be 1 or 0, and bar can either be NULL or a
+pointer to something, you must explicitly write:
+
+ if (foo != 0 || bar != null)
+
+in Java.
+
+JAVA NATIVE INTERFACE
+
+Java also provides an interface for C code to interface with Java.
+
+C functions exported from a shared library become static Java
+functions within a class, like so:
+
+public class EmacsNative
+{
+ /* Obtain the fingerprint of this build of Emacs. The fingerprint
+ can be used to determine the dump file name. */
+ public static native String getFingerprint ();
+
+ /* Set certain parameters before initializing Emacs.
+
+ assetManager must be the asset manager associated with the
+ context that is loading Emacs. It is saved and remains for the
+ remainder the lifetime of the Emacs process.
+
+ filesDir must be the package's data storage location for the
+ current Android user.
+
+ libDir must be the package's data storage location for native
+ libraries. It is used as PATH.
+
+ cacheDir must be the package's cache directory. It is used as
+ the `temporary-file-directory'.
+
+ pixelDensityX and pixelDensityY are the DPI values that will be
+ used by Emacs.
+
+ classPath must be the classpath of this app_process process, or
+ NULL.
+
+ emacsService must be the EmacsService singleton, or NULL. */
+ public static native void setEmacsParams (AssetManager assetManager,
+ String filesDir,
+ String libDir,
+ String cacheDir,
+ float pixelDensityX,
+ float pixelDensityY,
+ String classPath,
+ EmacsService emacsService);
+}
+
+Where the corresponding C functions are located in android.c, and
+loaded by the special invocation:
+
+ static
+ {
+ System.loadLibrary ("emacs");
+ };
+
+where ``static'' defines a section of code which will be run upon the
+object (containing class) being loaded. This is like:
+
+ __attribute__((constructor))
+
+on systems where shared object constructors are supported.
+
+See http://docs.oracle.com/en/java/javase/19/docs/specs/jni/intro.html
+for more details.
+
+
+
+OVERVIEW OF ANDROID
+
+When the Android system starts an application, it does not actually
+call the application's ``main'' function. It may not even start the
+application's process if one is already running.
+
+Instead, Android is organized around components. When the user opens
+the ``Emacs'' icon, the Android system looks up and starts the
+component associated with the ``Emacs'' icon. In this case, the
+component is called an activity, and is declared in
+the AndroidManifest.xml in this directory:
+
+ <activity android:name="org.gnu.emacs.EmacsActivity"
+ android:launchMode="singleTop"
+ android:windowSoftInputMode="adjustResize"
+ android:exported="true"
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+This tells Android to start the activity defined in ``EmacsActivity''
+(defined in org/gnu/emacs/EmacsActivity.java), a class extending the
+Android class ``Activity''.
+
+To do so, the Android system creates an instance of ``EmacsActivity''
+and the window system window associated with it, and eventually calls:
+
+ Activity activity;
+
+ activity.onCreate (...);
+
+But which ``onCreate'' is really called?
+It is actually the ``onCreate'' defined in EmacsActivity.java, as
+it overrides the ``onCreate'' defined in Android's own Activity class:
+
+ @Override
+ public void
+ onCreate (Bundle savedInstanceState)
+ {
+ FrameLayout.LayoutParams params;
+ Intent intent;
+
+Then, this is what happens step-by-step within the ``onCreate''
+function:
+
+ /* See if Emacs should be started with -Q. */
+ intent = getIntent ();
+ EmacsService.needDashQ
+ = intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
+ false);
+
+Here, Emacs obtains the intent (a request to start a component) which
+was used to start Emacs, and sets a special flag if it contains a
+request for Emacs to start with the ``-Q'' command-line argument.
+
+ /* Set the theme to one without a title bar. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ setTheme (android.R.style.Theme_DeviceDefault_NoActionBar);
+ else
+ setTheme (android.R.style.Theme_NoTitleBar);
+
+Next, Emacs sets an appropriate theme for the activity's associated
+window decorations.
+
+ params = new FrameLayout.LayoutParams (LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT);
+
+ /* Make the frame layout. */
+ layout = new FrameLayout (this);
+ layout.setLayoutParams (params);
+
+ /* Set it as the content view. */
+ setContentView (layout);
+
+Then, Emacs creates a ``FrameLayout'', a widget that holds a single
+other widget, and makes it the activity's ``content view''.
+
+The activity itself is a ``FrameLayout'', so the ``layout parameters''
+here apply to the FrameLayout itself, and not its children.
+
+ /* Maybe start the Emacs service if necessary. */
+ EmacsService.startEmacsService (this);
+
+And after that, Emacs calls the static function ``startEmacsService'',
+defined in the class ``EmacsService''. This starts the Emacs service
+component if necessary.
+
+ /* Add this activity to the list of available activities. */
+ EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
+
+ super.onCreate (savedInstanceState);
+
+Finally, Emacs registers that this activity is now ready to receive
+top-level frames (windows) created from Lisp.
+
+Activities come and go, but Emacs has to stay running in the mean
+time. Thus, Emacs also defines a ``service'', which is a long-running
+component that the Android system allows to run in the background.
+
+Let us go back and review the definition of ``startEmacsService'':
+
+ public static void
+ startEmacsService (Context context)
+ {
+ if (EmacsService.SERVICE == null)
+ {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ /* Start the Emacs service now. */
+ context.startService (new Intent (context,
+ EmacsService.class));
+ else
+ /* Display the permanant notification and start Emacs as a
+ foreground service. */
+ context.startForegroundService (new Intent (context,
+ EmacsService.class));
+ }
+ }
+
+If ``EmacsService.SERVICE'' does not yet exist, what this does is to
+tell the ``context'' (the equivalent of an Xlib Display *) to start a
+service defined by the class ``EmacsService''. Eventually, this
+results in ``EmacsService.onCreate'' being called:
+
+ @Override
+ public void
+ onCreate ()
+ {
+ AssetManager manager;
+ Context app_context;
+ String filesDir, libDir, cacheDir, classPath;
+ double pixelDensityX;
+ double pixelDensityY;
+
+Here is what this function does, step-by-step:
+
+ SERVICE = this;
+
+First, it sets the special static variable ``SERVICE'' to ``this'',
+which is a pointer to the ``EmacsService' object that was created.
+
+ handler = new Handler (Looper.getMainLooper ());
+
+Next, it creates a ``Handler'' object for the ``main looper''.
+This is a helper structure which allows executing code on the Android
+user interface thread.
+
+ manager = getAssets ();
+ app_context = getApplicationContext ();
+ metrics = getResources ().getDisplayMetrics ();
+ pixelDensityX = metrics.xdpi;
+ pixelDensityY = metrics.ydpi;
+
+Finally, it obtains:
+
+ - the asset manager, which is used to retrieve assets packaged
+ into the Emacs application package.
+
+ - the application context, used to obtain application specific
+ information.
+
+ - the display metrics, and from them, the X and Y densities in dots
+ per inch.
+
+Then, inside a ``try'' block:
+
+ try
+ {
+ /* Configure Emacs with the asset manager and other necessary
+ parameters. */
+ filesDir = app_context.getFilesDir ().getCanonicalPath ();
+ libDir = getLibraryDirectory ();
+ cacheDir = app_context.getCacheDir ().getCanonicalPath ();
+
+It obtains the names of the Emacs home, shared library, and temporary
+file directories.
+
+ /* Now provide this application's apk file, so a recursive
+ invocation of app_process (through android-emacs) can
+ find EmacsNoninteractive. */
+ classPath = getApkFile ();
+
+The name of the Emacs application package.
+
+ Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
+ + ", libDir = " + libDir + ", and classPath = " + classPath);
+
+Prints a debug message to the Android system log with this
+information.
+
+ EmacsNative.setEmacsParams (manager, filesDir, libDir,
+ cacheDir, (float) pixelDensityX,
+ (float) pixelDensityY,
+ classPath, this);
+
+And calls the native function ``setEmacsParams'' (defined in
+android.c) to configure Emacs with this information.
+
+ /* Start the thread that runs Emacs. */
+ thread = new EmacsThread (this, needDashQ);
+ thread.start ();
+
+Then, it allocates an ``EmacsThread'' object, and starts that thread.
+Inside that thread is where Emacs's C code runs.
+
+ }
+ catch (IOException exception)
+ {
+ EmacsNative.emacsAbort ();
+ return;
+
+And here is the purpose of the ``try'' block. Functions related to
+file names in Java will signal errors of various types upon failure.
+
+This ``catch'' block means that the Java virtual machine will abort
+execution of the contents of the ``try'' block as soon as an error of
+type ``IOException'' is encountered, and begin executing the contents
+of the ``catch'' block.
+
+Any failure of that type here is a crash, and
+``EmacsNative.emacsAbort'' is called to quickly abort the process to
+get a useful backtrace.
+ }
+ }
+
+Now, let us look at the definition of the class ``EmacsThread'', found
+in org/gnu/emacs/EmacsThread.java:
+
+public class EmacsThread extends Thread
+{
+ /* Whether or not Emacs should be started -Q. */
+ private boolean startDashQ;
+
+ public
+ EmacsThread (EmacsService service, boolean startDashQ)
+ {
+ super ("Emacs main thread");
+ this.startDashQ = startDashQ;
+ }
+
+ @Override
+ public void
+ run ()
+ {
+ String args[];
+
+ if (!startDashQ)
+ args = new String[] { "libandroid-emacs.so", };
+ else
+ args = new String[] { "libandroid-emacs.so", "-Q", };
+
+ /* Run the native code now. */
+ EmacsNative.initEmacs (args, EmacsApplication.dumpFileName);
+ }
+};
+
+The class itself defines a single field, ``startDashQ'', a constructor
+with an unused argument of the type ``EmacsService'' (which is useful
+while debugging) and a flag ``startDashQ'', and a single function
+``run'', overriding the same function in the class ``Thread''.
+
+When ``thread.start'' is called, the Java virtual machine creates a
+new thread, and then calls the function ``run'' within that thread.
+
+This function then computes a suitable argument vector, and calls
+``EmacsNative.initEmacs'' (defined in android.c), which then calls a
+modified version of the regular Emacs ``main'' function.
+
+At that point, Emacs initialization proceeds as usual:
+Vinitial_window_system is set, loadup.el calls `normal-top-level',
+which calls `command-line', and finally
+`window-system-initialization', which initializes the `android'
+terminal interface as usual.
+
+What happens here is the same as on other platforms. Now, here is
+what happens when the initial frame is created: Fx_create_frame calls
+`android_create_frame_window' to create a top level window:
+
+static void
+android_create_frame_window (struct frame *f)
+{
+ struct android_set_window_attributes attributes;
+ enum android_window_value_mask attribute_mask;
+
+ attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f);
+ attribute_mask = ANDROID_CW_BACK_PIXEL;
+
+ block_input ();
+ FRAME_ANDROID_WINDOW (f)
+ = android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
+ f->left_pos,
+ f->top_pos,
+ FRAME_PIXEL_WIDTH (f),
+ FRAME_PIXEL_HEIGHT (f),
+ attribute_mask, &attributes);
+ unblock_input ();
+}
+
+This calls the function `android_create_window' with some arguments
+whose meanings are identical to the arguments to `XCreateWindow'.
+
+Here is the definition of `android_create_window', in android.c:
+
+android_window
+android_create_window (android_window parent, int x, int y,
+ int width, int height,
+ enum android_window_value_mask value_mask,
+ struct android_set_window_attributes *attrs)
+{
+ static jclass class;
+ static jmethodID constructor;
+ jobject object, parent_object, old;
+ android_window window;
+ android_handle prev_max_handle;
+ bool override_redirect;
+
+What does it do? First, some context:
+
+At any time, there can be at most 65535 Java objects referred to by
+the rest of Emacs through the Java native interface. Each such object
+is assigned a ``handle'' (similar to an XID on X) and given a unique
+type. The function `android_resolve_handle' returns the JNI `jobject'
+associated with a given handle.
+
+ parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
+
+Here, it is being used to look up the `jobject' associated with the
+`parent' handle.
+
+ prev_max_handle = max_handle;
+ window = android_alloc_id ();
+
+Next, `max_handle' is saved, and a new handle is allocated for
+`window'.
+
+ if (!window)
+ error ("Out of window handles!");
+
+An error is signalled if Emacs runs out of available handles.
+
+ if (!class)
+ {
+ class = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsWindow");
+ assert (class != NULL);
+
+Then, if this initialization has not yet been completed, Emacs
+proceeds to find the Java class named ``EmacsWindow''.
+
+ constructor
+ = (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
+ "(SLorg/gnu/emacs/EmacsWindow;"
+ "IIIIZ)V");
+ assert (constructor != NULL);
+
+And it tries to look up the constructor, which should take seven
+arguments:
+
+ S - a short. (the handle ID)
+ Lorg/gnu/Emacs/EmacsWindow; - an instance of the EmacsWindow
+ class. (the parent)
+ IIII - four ints. (the window geometry.)
+ Z - a boolean. (whether or not the
+ window is override-redirect; see
+ XChangeWindowAttributes.)
+
+ old = class;
+ class = (*android_java_env)->NewGlobalRef (android_java_env, class);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+Next, it saves a global reference to the class and deletes the local
+reference. Global references will never be deallocated by the Java
+virtual machine as long as they still exist.
+
+ if (!class)
+ memory_full (0);
+ }
+
+ /* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
+ creation time. */
+ override_redirect = ((value_mask
+ & ANDROID_CW_OVERRIDE_REDIRECT)
+ && attrs->override_redirect);
+
+ object = (*android_java_env)->NewObject (android_java_env, class,
+ constructor, (jshort) window,
+ parent_object, (jint) x, (jint) y,
+ (jint) width, (jint) height,
+ (jboolean) override_redirect);
+
+Then, it creates an instance of the ``EmacsWindow'' class with the
+appropriate arguments and previously determined constructor.
+
+ if (!object)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ max_handle = prev_max_handle;
+ memory_full (0);
+
+If creating the object fails, Emacs clears the ``pending exception''
+and signals that it is out of memory.
+ }
+
+ android_handles[window].type = ANDROID_HANDLE_WINDOW;
+ android_handles[window].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env,
+ object);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+Otherwise, it associates a new global reference to the object with the
+handle, and deletes the local reference returned from the JNI
+NewObject function.
+
+ if (!android_handles[window].handle)
+ memory_full (0);
+
+If allocating the global reference fails, Emacs signals that it is out
+of memory.
+
+ android_change_window_attributes (window, value_mask, attrs);
+ return window;
+
+Otherwise, it applies the specified window attributes and returns the
+handle of the new window.
+}
diff --git a/java/debug.sh b/java/debug.sh
new file mode 100755
index 00000000000..339b3604810
--- /dev/null
+++ b/java/debug.sh
@@ -0,0 +1,364 @@
+#!/bin/bash
+### Run Emacs under GDB or JDB on Android.
+
+## 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/>.
+
+set -m
+oldpwd=`pwd`
+cd `dirname $0`
+
+devices=`adb devices | grep device | awk -- '/device\y/ { print $1 }' -`
+device=
+progname=$0
+package=org.gnu.emacs
+activity=org.gnu.emacs.EmacsActivity
+gdb_port=5039
+jdb_port=64013
+jdb=no
+attach_existing=no
+gdbserver=
+gdb=gdb
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ ## This option specifies the serial number of a device to use.
+ "--device" )
+ device="$2"
+ if [ -z device ]; then
+ echo "You must specify an argument to --device"
+ exit 1
+ fi
+ shift
+ ;;
+ "--help" )
+ echo "Usage: $progname [options] -- [gdb options]"
+ echo ""
+ echo " --device DEVICE run Emacs on the specified device"
+ echo " --port PORT run the GDB server on a specific port"
+ echo " --jdb-port PORT run the JDB server on a specific port"
+ echo " --jdb run JDB instead of GDB"
+ echo " --gdb use specified GDB binary"
+ echo " --attach-existing attach to an existing process"
+ echo " --gdbserver BINARY upload and use the specified gdbserver binary"
+ echo " --help print this message"
+ echo ""
+ echo "Available devices:"
+ for device in $devices; do
+ echo " " $device
+ done
+ echo ""
+ exit 0
+ ;;
+ "--jdb" )
+ jdb=yes
+ ;;
+ "--gdb" )
+ shift
+ gdb=$1
+ ;;
+ "--gdbserver" )
+ shift
+ gdbserver=$1
+ ;;
+ "--port" )
+ shift
+ gdb_port=$1
+ ;;
+ "--jdb-port" )
+ shift
+ jdb_port=$1
+ ;;
+ "--attach-existing" )
+ attach_existing=yes
+ ;;
+ "--" )
+ shift
+ gdbargs=$@
+ break;
+ ;;
+ * )
+ echo "$progname: Unrecognized argument $1"
+ exit 1
+ ;;
+ esac
+ shift
+done
+
+if [ -z "$devices" ]; then
+ echo "No devices are available."
+ exit 1
+fi
+
+if [ -z $device ]; then
+ device=$devices
+fi
+
+if [ `wc -w <<< "$devices"` -gt 1 ] && [ -z device ]; then
+ echo "Multiple devices are available. Please pick one using"
+ echo "--device and try again."
+fi
+
+echo "Looking for $package on device $device"
+
+# Find the application data directory
+app_data_dir=`adb -s $device shell run-as $package sh -c 'pwd 2> /dev/null'`
+
+if [ -z $app_data_dir ]; then
+ echo "The data directory for the package $package was not found."
+ echo "Is it installed?"
+fi
+
+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.
+ for pid in $package_pids; do
+ echo "Killing existing process $pid..."
+ adb -s $device shell run-as $package kill -9 $pid &> /dev/null
+ done
+
+ # Now run the main activity. This must be done as the adb user and
+ # not as the package user.
+ echo "Starting activity $activity and attaching debugger"
+
+ # Exit if the activity could not be started.
+ 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 ps`
+
+ # Next, remove lines matching "ps" itself.
+ package_pids=`awk -f tmp.awk <<< $package_pids`
+fi
+
+pid=$package_pids
+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 | awk -- "{
+ if (!match (\$0, /ps/) && match (\$0, /$package/))
+ print \$0
+ }"
+ echo ""
+ printf "Which one do you want to attach to? "
+ read pid
+elif [ -z $package_pids ]; then
+ echo "No processes were found to attach to."
+ exit 1
+fi
+
+# 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
+ adb -s $device forward --remove-all
+ adb -s $device forward "tcp:$jdb_port" "jdwp:$pid"
+
+ if [ ! $? ]; then
+ echo "Failed to forward jdwp:$pid to $jdb_port!"
+ echo "Perhaps you need to specify a different port with --port?"
+ exit 1;
+ fi
+
+ jdb_command="jdb -connect \
+ com.sun.jdi.SocketAttach:hostname=localhost,port=$jdb_port"
+
+ if [ $jdb = "yes" ]; then
+ # Just start JDB and then exit
+ $jdb_command
+ exit 1
+ fi
+fi
+
+if [ -n "$jdb_command" ]; then
+ echo "Starting JDB to unblock application."
+
+ # Start JDB to unblock the application.
+ coproc JDB { $jdb_command; }
+
+ # 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
+gdbserver_cmd=
+is_root=
+if [ -z "$gdbserver" ]; then
+ gdbserver_bin=/system/bin/gdbserver
+else
+ gdbserver_bin=/data/local/tmp/gdbserver
+ gdbserver_cat="cat $gdbserver_bin | run-as $package sh -c \
+ \"tee gdbserver > /dev/null\""
+
+ # Upload the specified gdbserver binary to the device.
+ adb -s $device push "$gdbserver" "$gdbserver_bin"
+
+ if (adb -s $device shell ls /system/bin | grep -G tee); then
+ # Copy it to the user directory.
+ adb -s $device shell "$gdbserver_cat"
+ adb -s $device shell "run-as $package chmod 777 gdbserver"
+ gdbserver_cmd="./gdbserver"
+ else
+ # Hopefully this is an old version of Android which allows
+ # execution from /data/local/tmp. Its `chmod' doesn't support
+ # `+x' either.
+ adb -s $device shell "chmod 777 $gdbserver_bin"
+ gdbserver_cmd="$gdbserver_bin"
+
+ # If the user is root, then there is no need to open any kind
+ # of TCP socket.
+ if (adb -s $device shell id | grep -G root); then
+ gdbserver=
+ is_root=yes
+ fi
+ fi
+fi
+
+# Now start gdbserver on the device asynchronously.
+
+echo "Attaching gdbserver to $pid on $device..."
+exec 5<> /tmp/file-descriptor-stamp
+rm -f /tmp/file-descriptor-stamp
+
+if [ -z "$gdbserver" ]; then
+ if [ "$is_root" = "yes" ]; then
+ adb -s $device shell $gdbserver_bin --once \
+ "+/data/local/tmp/debug.$package.socket" --attach $pid >&5 &
+ gdb_socket="localfilesystem:/data/local/tmp/debug.$package.socket"
+ else
+ adb -s $device shell run-as $package $gdbserver_bin --once \
+ "+debug.$package.socket" --attach $pid >&5 &
+ gdb_socket="localfilesystem:$app_data_dir/debug.$package.socket"
+ fi
+else
+ # Normally the program cannot access $gdbserver_bin when it is
+ # placed in /data/local/tmp.
+ adb -s $device shell run-as $package $gdbserver_cmd --once \
+ "0.0.0.0:7654" --attach $pid >&5 &
+ gdb_socket="tcp:7654"
+fi
+
+# Wait until gdbserver successfully runs.
+line=
+while read -u 5 line; do
+ case "$line" in
+ *Attached* )
+ break;
+ ;;
+ *error* | *Error* | failed )
+ echo "GDB error:" $line
+ exit 1
+ ;;
+ * )
+ ;;
+ esac
+done
+
+# 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.socket"
+ echo "to $gdb_port! Perhaps you need to specify a different port"
+ echo "with --port?"
+ exit 1;
+fi
+
+# Finally, start gdb with any extra arguments needed.
+cd "$oldpwd"
+$gdb --eval-command "target remote localhost:$gdb_port" $gdbargs
diff --git a/java/emacs.keystore b/java/emacs.keystore
new file mode 100644
index 00000000000..76d80b6db65
--- /dev/null
+++ b/java/emacs.keystore
Binary files differ
diff --git a/java/org/gnu/emacs/EmacsActivity.java b/java/org/gnu/emacs/EmacsActivity.java
new file mode 100644
index 00000000000..b480fc40b2e
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsActivity.java
@@ -0,0 +1,434 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.lang.IllegalStateException;
+import java.util.List;
+import java.util.ArrayList;
+
+import android.app.Activity;
+
+import android.content.Context;
+import android.content.Intent;
+
+import android.os.Build;
+import android.os.Bundle;
+
+import android.util.Log;
+
+import android.view.Menu;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.Window;
+import android.view.WindowInsets;
+import android.view.WindowInsetsController;
+
+import android.widget.FrameLayout;
+
+public class EmacsActivity extends Activity
+ implements EmacsWindowAttachmentManager.WindowConsumer,
+ ViewTreeObserver.OnGlobalLayoutListener
+{
+ public static final String TAG = "EmacsActivity";
+
+ /* The currently attached EmacsWindow, or null if none. */
+ private EmacsWindow window;
+
+ /* The frame layout associated with the activity. */
+ private FrameLayout layout;
+
+ /* List of activities with focus. */
+ public static List<EmacsActivity> focusedActivities;
+
+ /* The last activity to have been focused. */
+ public static EmacsActivity lastFocusedActivity;
+
+ /* The currently focused window. */
+ public static EmacsWindow focusedWindow;
+
+ /* Whether or not this activity is paused. */
+ private boolean isPaused;
+
+ /* Whether or not this activity is fullscreen. */
+ private boolean isFullscreen;
+
+ /* The last context menu to be closed. */
+ private Menu lastClosedMenu;
+
+ static
+ {
+ focusedActivities = new ArrayList<EmacsActivity> ();
+ };
+
+ public static void
+ invalidateFocus1 (EmacsWindow window)
+ {
+ if (window.view.isFocused ())
+ focusedWindow = window;
+
+ for (EmacsWindow child : window.children)
+ invalidateFocus1 (child);
+ }
+
+ public static void
+ invalidateFocus ()
+ {
+ EmacsWindow oldFocus;
+
+ /* Walk through each focused activity and assign the window focus
+ to the bottom-most focused window within. Record the old focus
+ as well. */
+ oldFocus = focusedWindow;
+ focusedWindow = null;
+
+ for (EmacsActivity activity : focusedActivities)
+ {
+ if (activity.window != null)
+ invalidateFocus1 (activity.window);
+ }
+
+ /* Send focus in- and out- events to the previous and current
+ focus. */
+
+ if (oldFocus != null)
+ EmacsNative.sendFocusOut (oldFocus.handle,
+ System.currentTimeMillis ());
+
+ if (focusedWindow != null)
+ EmacsNative.sendFocusIn (focusedWindow.handle,
+ System.currentTimeMillis ());
+ }
+
+ @Override
+ public final void
+ detachWindow ()
+ {
+ syncFullscreenWith (null);
+
+ if (window == null)
+ Log.w (TAG, "detachWindow called, but there is no window");
+ else
+ {
+ /* Clear the window's pointer to this activity and remove the
+ window's view. */
+ window.setConsumer (null);
+
+ /* The window can't be iconified any longer. */
+ window.noticeDeiconified ();
+ layout.removeView (window.view);
+ window = null;
+
+ invalidateFocus ();
+ }
+ }
+
+ @Override
+ public final void
+ attachWindow (EmacsWindow child)
+ {
+ Log.d (TAG, "attachWindow: " + child);
+
+ if (window != null)
+ throw new IllegalStateException ("trying to attach window when one"
+ + " already exists");
+
+ syncFullscreenWith (child);
+
+ /* Record and attach the view. */
+
+ window = child;
+ layout.addView (window.view);
+ child.setConsumer (this);
+
+ /* If the window isn't no-focus-on-map, focus its view. */
+ if (!child.getDontFocusOnMap ())
+ window.view.requestFocus ();
+
+ /* If the activity is iconified, send that to the window. */
+ if (isPaused)
+ window.noticeIconified ();
+
+ /* Invalidate the focus. */
+ invalidateFocus ();
+ }
+
+ @Override
+ public final void
+ destroy ()
+ {
+ finish ();
+ }
+
+ @Override
+ public final EmacsWindow
+ getAttachedWindow ()
+ {
+ return window;
+ }
+
+ @Override
+ public final void
+ onCreate (Bundle savedInstanceState)
+ {
+ FrameLayout.LayoutParams params;
+ Intent intent;
+ View decorView;
+ ViewTreeObserver observer;
+ int matchParent;
+
+ /* See if Emacs should be started with -Q. */
+ intent = getIntent ();
+ EmacsService.needDashQ
+ = intent.getBooleanExtra ("org.gnu.emacs.START_DASH_Q",
+ false);
+
+ matchParent = FrameLayout.LayoutParams.MATCH_PARENT;
+ params
+ = new FrameLayout.LayoutParams (matchParent,
+ matchParent);
+
+ /* Make the frame layout. */
+ layout = new FrameLayout (this);
+ layout.setLayoutParams (params);
+
+ /* Set it as the content view. */
+ setContentView (layout);
+
+ /* Maybe start the Emacs service if necessary. */
+ EmacsService.startEmacsService (this);
+
+ /* Add this activity to the list of available activities. */
+ EmacsWindowAttachmentManager.MANAGER.registerWindowConsumer (this);
+
+ /* Start observing global layout changes between Jelly Bean and Q.
+ This is required to restore the fullscreen state whenever the
+ on screen keyboard is displayed, as there is otherwise no way
+ to determine when the on screen keyboard becomes visible. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+ && Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
+ {
+ decorView = getWindow ().getDecorView ();
+ observer = decorView.getViewTreeObserver ();
+ observer.addOnGlobalLayoutListener (this);
+ }
+
+ super.onCreate (savedInstanceState);
+ }
+
+ @Override
+ public final void
+ onGlobalLayout ()
+ {
+ syncFullscreenWith (window);
+ }
+
+ @Override
+ public final void
+ onDestroy ()
+ {
+ EmacsWindowAttachmentManager manager;
+ boolean isMultitask;
+
+ manager = EmacsWindowAttachmentManager.MANAGER;
+
+ /* The activity will die shortly hereafter. If there is a window
+ attached, close it now. */
+ Log.d (TAG, "onDestroy " + this);
+ isMultitask = this instanceof EmacsMultitaskActivity;
+ manager.removeWindowConsumer (this, isMultitask || isFinishing ());
+ focusedActivities.remove (this);
+ invalidateFocus ();
+
+ /* Remove this activity from the static field, lest it leak. */
+ if (lastFocusedActivity == this)
+ lastFocusedActivity = null;
+
+ super.onDestroy ();
+ }
+
+ @Override
+ public final void
+ onWindowFocusChanged (boolean isFocused)
+ {
+ Log.d (TAG, ("onWindowFocusChanged: "
+ + (isFocused ? "YES" : "NO")));
+
+ if (isFocused && !focusedActivities.contains (this))
+ {
+ focusedActivities.add (this);
+ lastFocusedActivity = this;
+
+ /* Update the window insets as the focus change may have
+ changed the window insets as well, and the system does not
+ automatically restore visibility flags. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+ && Build.VERSION.SDK_INT < Build.VERSION_CODES.R
+ && isFullscreen)
+ syncFullscreenWith (window);
+ }
+ else
+ focusedActivities.remove (this);
+
+ invalidateFocus ();
+ }
+
+ @Override
+ public final void
+ onPause ()
+ {
+ isPaused = true;
+
+ EmacsWindowAttachmentManager.MANAGER.noticeIconified (this);
+ super.onPause ();
+ }
+
+ @Override
+ public final void
+ onResume ()
+ {
+ isPaused = false;
+
+ EmacsWindowAttachmentManager.MANAGER.noticeDeiconified (this);
+ super.onResume ();
+ }
+
+ @Override
+ public final void
+ onContextMenuClosed (Menu menu)
+ {
+ int serial;
+
+ Log.d (TAG, "onContextMenuClosed: " + menu);
+
+ /* See the comment inside onMenuItemClick. */
+
+ if (((EmacsContextMenu.wasSubmenuSelected == -2)
+ || (EmacsContextMenu.wasSubmenuSelected >= 0
+ && ((System.currentTimeMillis ()
+ - EmacsContextMenu.wasSubmenuSelected)
+ <= 300)))
+ || menu == lastClosedMenu)
+ {
+ EmacsContextMenu.wasSubmenuSelected = -1;
+ lastClosedMenu = menu;
+ return;
+ }
+
+ /* lastClosedMenu is set because Android apparently calls this
+ function twice. */
+
+ lastClosedMenu = null;
+
+ /* Send a context menu event given that no menu item has already
+ been selected. */
+ if (!EmacsContextMenu.itemAlreadySelected)
+ {
+ serial = EmacsContextMenu.lastMenuEventSerial;
+ EmacsNative.sendContextMenu ((short) 0, 0,
+ serial);
+ }
+
+ super.onContextMenuClosed (menu);
+ }
+
+ @SuppressWarnings ("deprecation")
+ public final void
+ syncFullscreenWith (EmacsWindow emacsWindow)
+ {
+ WindowInsetsController controller;
+ Window window;
+ int behavior, flags;
+ View view;
+
+ if (emacsWindow != null)
+ isFullscreen = emacsWindow.fullscreen;
+ else
+ isFullscreen = false;
+
+ /* On Android 11 or later, use the window insets controller to
+ control whether or not the view is fullscreen. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
+ {
+ window = getWindow ();
+
+ /* If there is no attached window, return immediately. */
+ if (window == null)
+ return;
+
+ behavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+ controller = window.getInsetsController ();
+ controller.setSystemBarsBehavior (behavior);
+
+ if (isFullscreen)
+ controller.hide (WindowInsets.Type.statusBars ()
+ | WindowInsets.Type.navigationBars ());
+ else
+ controller.show (WindowInsets.Type.statusBars ()
+ | WindowInsets.Type.navigationBars ());
+
+ return;
+ }
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
+ {
+ /* On Android 4.1 or later, use `setSystemUiVisibility'. */
+
+ window = getWindow ();
+
+ if (window == null)
+ return;
+
+ view = window.getDecorView ();
+
+ if (isFullscreen)
+ {
+ flags = 0;
+ flags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+ {
+ /* These flags means that Emacs will be full screen as
+ long as the state flag is set. */
+ flags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
+ flags |= View.SYSTEM_UI_FLAG_IMMERSIVE;
+ flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
+ }
+
+ /* Apply the given flags. */
+ view.setSystemUiVisibility (flags);
+ }
+ else
+ view.setSystemUiVisibility (View.SYSTEM_UI_FLAG_VISIBLE);
+ }
+ }
+
+ @Override
+ public final void
+ onAttachedToWindow ()
+ {
+ super.onAttachedToWindow ();
+
+ /* Update the window insets. */
+ syncFullscreenWith (window);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsApplication.java b/java/org/gnu/emacs/EmacsApplication.java
new file mode 100644
index 00000000000..10099721744
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsApplication.java
@@ -0,0 +1,81 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.io.File;
+import java.io.FileFilter;
+
+import android.content.Context;
+
+import android.app.Application;
+import android.util.Log;
+
+public final class EmacsApplication extends Application
+{
+ private static final String TAG = "EmacsApplication";
+
+ /* The name of the dump file to use. */
+ public static String dumpFileName;
+
+ public static void
+ findDumpFile (Context context)
+ {
+ File filesDirectory;
+ File[] allFiles;
+ String wantedDumpFile;
+ int i;
+
+ wantedDumpFile = ("emacs-" + EmacsNative.getFingerprint ()
+ + ".pdmp");
+
+ /* Obtain a list of all files ending with ``.pdmp''. Then, look
+ for a file named ``emacs-<fingerprint>.pdmp'' and delete the
+ rest. */
+ filesDirectory = context.getFilesDir ();
+
+ allFiles = filesDirectory.listFiles (new FileFilter () {
+ @Override
+ public boolean
+ accept (File file)
+ {
+ return (!file.isDirectory ()
+ && file.getName ().endsWith (".pdmp"));
+ }
+ });
+
+ /* Now try to find the right dump file. */
+ for (i = 0; i < allFiles.length; ++i)
+ {
+ if (allFiles[i].getName ().equals (wantedDumpFile))
+ dumpFileName = allFiles[i].getAbsolutePath ();
+ else
+ /* Delete this outdated dump file. */
+ allFiles[i].delete ();
+ }
+ }
+
+ @Override
+ public void
+ onCreate ()
+ {
+ findDumpFile (this);
+ super.onCreate ();
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsClipboard.java b/java/org/gnu/emacs/EmacsClipboard.java
new file mode 100644
index 00000000000..5cd48af6e3a
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsClipboard.java
@@ -0,0 +1,47 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.os.Build;
+
+/* This class provides helper code for accessing the clipboard,
+ abstracting between the different interfaces on API 8 and 11. */
+
+public abstract class EmacsClipboard
+{
+ public abstract void setClipboard (byte[] bytes);
+ public abstract int ownsClipboard ();
+ public abstract boolean clipboardExists ();
+ public abstract byte[] getClipboard ();
+
+ public abstract byte[][] getClipboardTargets ();
+ public abstract long[] getClipboardData (byte[] target);
+
+ /* Create the correct kind of clipboard for this system. */
+
+ public static EmacsClipboard
+ makeClipboard ()
+ {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
+ return new EmacsSdk11Clipboard ();
+ else
+ return new EmacsSdk8Clipboard ();
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsContextMenu.java b/java/org/gnu/emacs/EmacsContextMenu.java
new file mode 100644
index 00000000000..5bae41bd61d
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsContextMenu.java
@@ -0,0 +1,374 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import android.content.Context;
+import android.content.Intent;
+
+import android.os.Build;
+
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.SubMenu;
+
+import android.util.Log;
+
+/* Context menu implementation. This object is built from JNI and
+ describes a menu hiearchy. Then, `inflate' can turn it into an
+ Android menu, which can be turned into a popup (or other kind of)
+ menu. */
+
+public final class EmacsContextMenu
+{
+ private static final String TAG = "EmacsContextMenu";
+
+ /* Whether or not an item was selected. */
+ public static boolean itemAlreadySelected;
+
+ /* Whether or not a submenu was selected.
+ Value is -1 if no; value is -2 if yes, and a context menu
+ close event will definitely be sent. Any other value is
+ the timestamp when the submenu was selected. */
+ public static long wasSubmenuSelected;
+
+ /* The serial ID of the last context menu to be displayed. */
+ public static int lastMenuEventSerial;
+
+ /* The last group ID used for a menu item. */
+ public int lastGroupId;
+
+ private static class Item implements MenuItem.OnMenuItemClickListener
+ {
+ public int itemID;
+ public String itemName, tooltip;
+ public EmacsContextMenu subMenu;
+ public boolean isEnabled, isCheckable, isChecked;
+ public EmacsView inflatedView;
+ public boolean isRadio;
+
+ @Override
+ public boolean
+ onMenuItemClick (MenuItem item)
+ {
+ Log.d (TAG, "onMenuItemClick: " + itemName + " (" + itemID + ")");
+
+ if (subMenu != null)
+ {
+ /* Android 6.0 and earlier don't support nested submenus
+ properly, so display the submenu popup by hand. */
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+ {
+ Log.d (TAG, "onMenuItemClick: displaying submenu " + subMenu);
+
+ /* Still set wasSubmenuSelected -- if not set, the
+ dismissal of this context menu will result in a
+ context menu event being sent. */
+ wasSubmenuSelected = -2;
+
+ /* Running a popup menu from inside a click handler
+ doesn't work, so make sure it is displayed
+ outside. */
+
+ inflatedView.post (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ inflatedView.popupMenu (subMenu, 0, 0, true);
+ }
+ });
+
+ return true;
+ }
+
+ /* After opening a submenu within a submenu, Android will
+ send onContextMenuClosed for a ContextMenuBuilder. This
+ will normally confuse Emacs into thinking that the
+ context menu has been dismissed. Wrong!
+
+ Setting this flag makes EmacsActivity to only handle
+ SubMenuBuilder being closed, which always means the menu
+ has actually been dismissed.
+
+ However, these extraneous events aren't sent on devices
+ where submenus display without dismissing their parents.
+ Thus, only ignore the close event if it happens within
+ 300 milliseconds of the submenu being selected. */
+ wasSubmenuSelected = System.currentTimeMillis ();
+ return false;
+ }
+
+ /* Send a context menu event. */
+ EmacsNative.sendContextMenu ((short) 0, itemID,
+ lastMenuEventSerial);
+
+ /* Say that an item has already been selected. */
+ itemAlreadySelected = true;
+ return true;
+ }
+ };
+
+ public List<Item> menuItems;
+ public String title;
+ private EmacsContextMenu parent;
+
+ /* Create a context menu with no items inside and the title TITLE,
+ which may be NULL. */
+
+ public static EmacsContextMenu
+ createContextMenu (String title)
+ {
+ EmacsContextMenu menu;
+
+ menu = new EmacsContextMenu ();
+ menu.menuItems = new ArrayList<Item> ();
+ menu.title = title;
+
+ return menu;
+ }
+
+ /* Add a normal menu item to the context menu with the id ITEMID and
+ the name ITEMNAME. Enable it if ISENABLED, else keep it
+ disabled.
+
+ If this is not a submenu and ISCHECKABLE is set, make the item
+ checkable. Likewise, if ISCHECKED is set, make the item
+ checked.
+
+ If TOOLTIP is non-NULL, set the menu item tooltip to TOOLTIP.
+
+ If ISRADIO, then display the check mark as a radio button. */
+
+ public void
+ addItem (int itemID, String itemName, boolean isEnabled,
+ boolean isCheckable, boolean isChecked,
+ String tooltip, boolean isRadio)
+ {
+ Item item;
+
+ item = new Item ();
+ item.itemID = itemID;
+ item.itemName = itemName;
+ item.isEnabled = isEnabled;
+ item.isCheckable = isCheckable;
+ item.isChecked = isChecked;
+ item.tooltip = tooltip;
+ item.isRadio = isRadio;
+
+ menuItems.add (item);
+ }
+
+ /* Create a disabled menu item with the name ITEMNAME. */
+
+ public void
+ addPane (String itemName)
+ {
+ Item item;
+
+ item = new Item ();
+ item.itemName = itemName;
+
+ menuItems.add (item);
+ }
+
+ /* Add a submenu to the context menu with the specified title and
+ item name. */
+
+ public EmacsContextMenu
+ addSubmenu (String itemName, String title, String tooltip)
+ {
+ EmacsContextMenu submenu;
+ Item item;
+
+ item = new Item ();
+ item.itemID = 0;
+ item.itemName = itemName;
+ item.tooltip = tooltip;
+ item.subMenu = createContextMenu (title);
+ item.subMenu.parent = this;
+
+ menuItems.add (item);
+ return item.subMenu;
+ }
+
+ /* Add the contents of this menu to MENU. Assume MENU will be
+ displayed in INFLATEDVIEW. */
+
+ private void
+ inflateMenuItems (Menu menu, EmacsView inflatedView)
+ {
+ Intent intent;
+ MenuItem menuItem;
+ SubMenu submenu;
+
+ for (Item item : menuItems)
+ {
+ if (item.subMenu != null)
+ {
+ /* This is a submenu. On versions of Android which
+ support doing so, create the submenu and add the
+ contents of the menu to it.
+
+ Note that Android 4.0 and later technically supports
+ having multiple layers of nested submenus, but if they
+ are used, onContextMenuClosed becomes unreliable. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ {
+ submenu = menu.addSubMenu (item.itemName);
+ item.subMenu.inflateMenuItems (submenu, inflatedView);
+
+ /* This is still needed to set wasSubmenuSelected. */
+ menuItem = submenu.getItem ();
+ }
+ else
+ menuItem = menu.add (item.itemName);
+
+ item.inflatedView = inflatedView;
+ menuItem.setOnMenuItemClickListener (item);
+ }
+ else
+ {
+ if (item.isRadio)
+ menuItem = menu.add (++lastGroupId, Menu.NONE, Menu.NONE,
+ item.itemName);
+ else
+ menuItem = menu.add (item.itemName);
+ menuItem.setOnMenuItemClickListener (item);
+
+ /* If the item ID is zero, then disable the item. */
+ if (item.itemID == 0 || !item.isEnabled)
+ menuItem.setEnabled (false);
+
+ /* Now make the menu item display a checkmark as
+ appropriate. */
+
+ if (item.isCheckable)
+ menuItem.setCheckable (true);
+
+ if (item.isChecked)
+ menuItem.setChecked (true);
+
+ /* Define an exclusively checkable group if the item is a
+ radio button. */
+
+ if (item.isRadio)
+ menu.setGroupCheckable (lastGroupId, true, true);
+
+ /* If the tooltip text is set and the system is new enough
+ to support menu item tooltips, set it on the item. */
+
+ if (item.tooltip != null
+ && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ menuItem.setTooltipText (item.tooltip);
+ }
+ }
+ }
+
+ /* Enter the items in this context menu to MENU.
+ Assume that MENU will be displayed in VIEW; this may lead to
+ popupMenu being called on VIEW if a submenu is selected. */
+
+ public void
+ expandTo (Menu menu, EmacsView view)
+ {
+ inflateMenuItems (menu, view);
+ }
+
+ /* Return the parent or NULL. */
+
+ public EmacsContextMenu
+ parent ()
+ {
+ return this.parent;
+ }
+
+ /* Like display, but does the actual work and runs in the main
+ thread. */
+
+ private boolean
+ display1 (EmacsWindow window, int xPosition, int yPosition)
+ {
+ /* Set this flag to false. It is used to decide whether or not to
+ send 0 in response to the context menu being closed. */
+ itemAlreadySelected = false;
+
+ /* No submenu has been selected yet. */
+ wasSubmenuSelected = -1;
+
+ return window.view.popupMenu (this, xPosition, yPosition,
+ false);
+ }
+
+ /* Display this context menu on WINDOW, at xPosition and yPosition.
+ SERIAL is a number that will be returned in any menu event
+ generated to identify this context menu. */
+
+ public boolean
+ display (final EmacsWindow window, final int xPosition,
+ final int yPosition, final int serial)
+ {
+ Runnable runnable;
+ final Holder<Boolean> rc;
+
+ rc = new Holder<Boolean> ();
+
+ runnable = new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ synchronized (this)
+ {
+ lastMenuEventSerial = serial;
+ rc.thing = display1 (window, xPosition, yPosition);
+ notify ();
+ }
+ }
+ };
+
+ EmacsService.syncRunnable (runnable);
+ return rc.thing;
+ }
+
+ /* Dismiss this context menu. WINDOW is the window where the
+ context menu is being displayed. */
+
+ public void
+ dismiss (final EmacsWindow window)
+ {
+ Runnable runnable;
+
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ window.view.cancelPopupMenu ();
+ itemAlreadySelected = false;
+ }
+ });
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsCopyArea.java b/java/org/gnu/emacs/EmacsCopyArea.java
new file mode 100644
index 00000000000..f69b0cde866
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsCopyArea.java
@@ -0,0 +1,206 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Xfermode;
+
+public final class EmacsCopyArea
+{
+ private static Xfermode overAlu;
+
+ static
+ {
+ overAlu = new PorterDuffXfermode (Mode.SRC_OVER);
+ };
+
+ private static void
+ insetRectBy (Rect rect, int left, int top, int right,
+ int bottom)
+ {
+ rect.left += left;
+ rect.top += top;
+ rect.right -= right;
+ rect.bottom -= bottom;
+ }
+
+ public static void
+ perform (EmacsDrawable source, EmacsGC gc,
+ EmacsDrawable destination,
+ int src_x, int src_y, int width, int height,
+ int dest_x, int dest_y)
+ {
+ int i;
+ Bitmap bitmap;
+ Paint maskPaint, paint;
+ Canvas maskCanvas, canvas;
+ Bitmap srcBitmap, maskBitmap, clipBitmap;
+ Rect rect, maskRect, srcRect, dstRect, maskDestRect;
+ boolean needFill;
+
+ /* TODO implement stippling. */
+ if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+ return;
+
+ paint = gc.gcPaint;
+
+ canvas = destination.lockCanvas (gc);
+
+ if (canvas == null)
+ return;
+
+ /* A copy must be created or drawBitmap could end up overwriting
+ itself. */
+ srcBitmap = source.getBitmap ();
+
+ /* If srcBitmap is out of bounds, then adjust the source rectangle
+ to be within bounds. Note that tiling on windows with
+ backgrounds is unimplemented. */
+
+ if (src_x < 0)
+ {
+ width += src_x;
+ dest_x -= src_x;
+ src_x = 0;
+ }
+
+ if (src_y < 0)
+ {
+ height += src_y;
+ dest_y -= src_y;
+ src_y = 0;
+ }
+
+ if (src_x + width > srcBitmap.getWidth ())
+ width = srcBitmap.getWidth () - src_x;
+
+ if (src_y + height > srcBitmap.getHeight ())
+ height = srcBitmap.getHeight () - src_y;
+
+ /* If width and height are empty or negative, then skip the entire
+ CopyArea operation lest createBitmap throw an exception. */
+
+ if (width <= 0 || height <= 0)
+ return;
+
+ rect = new Rect (dest_x, dest_y, dest_x + width,
+ dest_y + height);
+
+ if (gc.clip_mask == null)
+ {
+ if (source == destination)
+ {
+ /* Create a copy of the bitmap, since Android can't handle
+ overlapping copies. */
+ bitmap = Bitmap.createBitmap (srcBitmap,
+ src_x, src_y, width,
+ height);
+ canvas.drawBitmap (bitmap, null, rect, paint);
+ bitmap.recycle ();
+ }
+ else
+ {
+ /* But here the bitmaps are known to not overlap, so avoid
+ that extra consing overhead. */
+
+ srcRect = new Rect (src_x, src_y, src_x + width,
+ src_y + height);
+ canvas.drawBitmap (srcBitmap, srcRect, rect, paint);
+ }
+ }
+ else
+ {
+ /* Drawing with a clip mask involves calculating the
+ intersection of the clip mask with the dst rect, and
+ extrapolating the corresponding part of the src rect. */
+ clipBitmap = gc.clip_mask.bitmap;
+ dstRect = new Rect (dest_x, dest_y,
+ dest_x + width,
+ dest_y + height);
+ maskRect = new Rect (gc.clip_x_origin,
+ gc.clip_y_origin,
+ (gc.clip_x_origin
+ + clipBitmap.getWidth ()),
+ (gc.clip_y_origin
+ + clipBitmap.getHeight ()));
+ clipBitmap = gc.clip_mask.bitmap;
+
+ if (!maskRect.setIntersect (dstRect, maskRect))
+ /* There is no intersection between the clip mask and the
+ dest rect. */
+ return;
+
+ /* Now figure out which part of the source corresponds to
+ maskRect and return it relative to srcBitmap. */
+ srcRect = new Rect (src_x, src_y, src_x + width,
+ src_y + height);
+ insetRectBy (srcRect, maskRect.left - dstRect.left,
+ maskRect.top - dstRect.top,
+ maskRect.right - dstRect.right,
+ maskRect.bottom - dstRect.bottom);
+
+ /* Finally, create a temporary bitmap that is the size of
+ maskRect. */
+
+ maskBitmap
+ = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
+ Bitmap.Config.ARGB_8888);
+
+ /* Draw the mask onto the maskBitmap. */
+ maskCanvas = new Canvas (maskBitmap);
+ maskPaint = new Paint ();
+ maskRect.offset (-gc.clip_x_origin,
+ -gc.clip_y_origin);
+ maskCanvas.drawBitmap (gc.clip_mask.bitmap,
+ maskRect,
+ new Rect (0, 0,
+ maskRect.width (),
+ maskRect.height ()),
+ maskPaint);
+ maskRect.offset (gc.clip_x_origin,
+ gc.clip_y_origin);
+
+ /* Set the transfer mode to SRC_IN to preserve only the parts
+ of the source that overlap with the mask. */
+ maskPaint.setXfermode (EmacsGC.srcInAlu);
+
+ /* Draw the source. */
+ maskDestRect = new Rect (0, 0, srcRect.width (),
+ srcRect.height ());
+ maskCanvas.drawBitmap (srcBitmap, srcRect, maskDestRect,
+ maskPaint);
+
+ /* Finally, draw the mask bitmap to the destination. */
+ paint.setXfermode (overAlu);
+ canvas.drawBitmap (maskBitmap, null, maskRect, paint);
+ gc.resetXfermode ();
+
+ /* Recycle this unused bitmap. */
+ maskBitmap.recycle ();
+ }
+
+ destination.damageRect (rect);
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsCursor.java b/java/org/gnu/emacs/EmacsCursor.java
new file mode 100644
index 00000000000..c14c6f2a11b
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsCursor.java
@@ -0,0 +1,47 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.view.PointerIcon;
+import android.os.Build;
+
+/* Cursor wrapper. Note that pointer icons are not supported prior to
+ Android 24. */
+
+public final class EmacsCursor extends EmacsHandleObject
+{
+ /* The pointer icon associated with this cursor. */
+ public final PointerIcon icon;
+
+ public
+ EmacsCursor (short handle, int glyph)
+ {
+ super (handle);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+ {
+ icon = null;
+ return;
+ }
+
+ icon = PointerIcon.getSystemIcon (EmacsService.SERVICE,
+ glyph);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsDialog.java b/java/org/gnu/emacs/EmacsDialog.java
new file mode 100644
index 00000000000..3a5f22021fc
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDialog.java
@@ -0,0 +1,372 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import android.app.AlertDialog;
+
+import android.content.Context;
+import android.content.DialogInterface;
+
+import android.os.Build;
+
+import android.provider.Settings;
+
+import android.util.Log;
+
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.FrameLayout;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+
+/* Toolkit dialog implementation. This object is built from JNI and
+ describes a single alert dialog. Then, `inflate' turns it into
+ AlertDialog. */
+
+public final class EmacsDialog implements DialogInterface.OnDismissListener
+{
+ private static final String TAG = "EmacsDialog";
+
+ /* List of buttons in this dialog. */
+ private List<EmacsButton> buttons;
+
+ /* Dialog title. */
+ private String title;
+
+ /* Dialog text. */
+ private String text;
+
+ /* Whether or not a selection has already been made. */
+ private boolean wasButtonClicked;
+
+ /* Dialog to dismiss after click. */
+ private AlertDialog dismissDialog;
+
+ /* The menu serial associated with this dialog box. */
+ private int menuEventSerial;
+
+ private class EmacsButton implements View.OnClickListener,
+ DialogInterface.OnClickListener
+ {
+ /* Name of this button. */
+ public String name;
+
+ /* ID of this button. */
+ public int id;
+
+ /* Whether or not the button is enabled. */
+ public boolean enabled;
+
+ @Override
+ public void
+ onClick (View view)
+ {
+ Log.d (TAG, "onClicked " + this);
+
+ wasButtonClicked = true;
+ EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial);
+ dismissDialog.dismiss ();
+ }
+
+ @Override
+ public void
+ onClick (DialogInterface dialog, int which)
+ {
+ Log.d (TAG, "onClicked " + this);
+
+ wasButtonClicked = true;
+ EmacsNative.sendContextMenu ((short) 0, id, menuEventSerial);
+ }
+ };
+
+ /* Create a popup dialog with the title TITLE and the text TEXT.
+ TITLE may be NULL. MENUEVENTSERIAL is a number which will
+ identify this popup dialog inside events it sends. */
+
+ public static EmacsDialog
+ createDialog (String title, String text, int menuEventSerial)
+ {
+ EmacsDialog dialog;
+
+ dialog = new EmacsDialog ();
+ dialog.buttons = new ArrayList<EmacsButton> ();
+ dialog.title = title;
+ dialog.text = text;
+ dialog.menuEventSerial = menuEventSerial;
+
+ return dialog;
+ }
+
+ /* Add a button named NAME, with the identifier ID. If DISABLE,
+ disable the button. */
+
+ public void
+ addButton (String name, int id, boolean disable)
+ {
+ EmacsButton button;
+
+ button = new EmacsButton ();
+ button.name = name;
+ button.id = id;
+ button.enabled = !disable;
+ buttons.add (button);
+ }
+
+ /* Turn this dialog into an AlertDialog for the specified
+ CONTEXT.
+
+ Upon a button being selected, the dialog will send an
+ ANDROID_CONTEXT_MENU event with the id of that button.
+
+ Upon the dialog being dismissed, an ANDROID_CONTEXT_MENU event
+ will be sent with an id of 0. */
+
+ public AlertDialog
+ toAlertDialog (Context context)
+ {
+ AlertDialog dialog;
+ int size;
+ EmacsButton button;
+ LinearLayout layout;
+ Button buttonView;
+ ViewGroup.LayoutParams layoutParams;
+
+ size = buttons.size ();
+
+ if (size <= 3)
+ {
+ dialog = new AlertDialog.Builder (context).create ();
+ dialog.setMessage (text);
+ dialog.setCancelable (true);
+ dialog.setOnDismissListener (this);
+
+ if (title != null)
+ dialog.setTitle (title);
+
+ /* There are less than 4 buttons. Add the buttons the way
+ Android intends them to be added. */
+
+ if (size >= 1)
+ {
+ button = buttons.get (0);
+ dialog.setButton (DialogInterface.BUTTON_POSITIVE,
+ button.name, button);
+ }
+
+ if (size >= 2)
+ {
+ button = buttons.get (1);
+ dialog.setButton (DialogInterface.BUTTON_NEGATIVE,
+ button.name, button);
+ }
+
+ if (size >= 3)
+ {
+ button = buttons.get (2);
+ dialog.setButton (DialogInterface.BUTTON_NEUTRAL,
+ button.name, button);
+ }
+ }
+ else
+ {
+ /* There are more than 4 buttons. Add them all to a
+ LinearLayout. */
+ layout = new LinearLayout (context);
+ layoutParams
+ = new LinearLayout.LayoutParams (ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ for (EmacsButton emacsButton : buttons)
+ {
+ buttonView = new Button (context);
+ buttonView.setText (emacsButton.name);
+ buttonView.setOnClickListener (emacsButton);
+ buttonView.setLayoutParams (layoutParams);
+ buttonView.setEnabled (emacsButton.enabled);
+ layout.addView (buttonView);
+ }
+
+ layoutParams
+ = new FrameLayout.LayoutParams (ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ layout.setLayoutParams (layoutParams);
+
+ /* Add that layout to the dialog's custom view.
+
+ android.R.id.custom is documented to work. But looking it
+ up returns NULL, so setView must be used instead. */
+
+ dialog = new AlertDialog.Builder (context).setView (layout).create ();
+ dialog.setMessage (text);
+ dialog.setCancelable (true);
+ dialog.setOnDismissListener (this);
+
+ if (title != null)
+ dialog.setTitle (title);
+ }
+
+ return dialog;
+ }
+
+ /* Internal helper for display run on the main thread. */
+
+ @SuppressWarnings("deprecation")
+ private boolean
+ display1 ()
+ {
+ Context context;
+ int size, type;
+ Button buttonView;
+ EmacsButton button;
+ AlertDialog dialog;
+ Window window;
+
+ if (EmacsActivity.focusedActivities.isEmpty ())
+ {
+ /* If focusedActivities is empty then this dialog may have
+ been displayed immediately after a popup dialog is
+ dismissed. Or Emacs might legitimately be in the
+ background. Try the service context first if possible. */
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M
+ || Settings.canDrawOverlays (EmacsService.SERVICE))
+ context = EmacsService.SERVICE;
+ else
+ context = EmacsActivity.lastFocusedActivity;
+
+ if (context == null)
+ return false;
+ }
+ else
+ /* Display using the activity context when Emacs is in the
+ foreground, as this allows the dialog to be dismissed more
+ consistently. */
+ context = EmacsActivity.focusedActivities.get (0);
+
+ Log.d (TAG, "display1: using context " + context);
+
+ dialog = dismissDialog = toAlertDialog (context);
+
+ try
+ {
+ if (context == EmacsService.SERVICE)
+ {
+ /* Apply the system alert window type to make sure this
+ dialog can be displayed. */
+
+ window = dialog.getWindow ();
+ type = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
+ ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
+ : WindowManager.LayoutParams.TYPE_PHONE);
+ window.setType (type);
+ }
+
+ dismissDialog.show ();
+ }
+ catch (Exception exception)
+ {
+ /* This can happen when the system decides Emacs is not in the
+ foreground any longer. */
+ return false;
+ }
+
+ /* If there are less than four buttons, then they must be
+ individually enabled or disabled after the dialog is
+ displayed. */
+ size = buttons.size ();
+
+ if (size <= 3)
+ {
+ if (size >= 1)
+ {
+ button = buttons.get (0);
+ buttonView
+ = dialog.getButton (DialogInterface.BUTTON_POSITIVE);
+ buttonView.setEnabled (button.enabled);
+ }
+
+ if (size >= 2)
+ {
+ button = buttons.get (1);
+ buttonView
+ = dialog.getButton (DialogInterface.BUTTON_NEGATIVE);
+ buttonView.setEnabled (button.enabled);
+ }
+
+ if (size >= 3)
+ {
+ button = buttons.get (2);
+ buttonView
+ = dialog.getButton (DialogInterface.BUTTON_NEUTRAL);
+ buttonView.setEnabled (button.enabled);
+ }
+ }
+
+ return true;
+ }
+
+ /* Display this dialog for a suitable activity.
+ Value is false if the dialog could not be displayed,
+ and true otherwise. */
+
+ public boolean
+ display ()
+ {
+ Runnable runnable;
+ final Holder<Boolean> rc;
+
+ rc = new Holder<Boolean> ();
+ runnable = new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ synchronized (this)
+ {
+ rc.thing = display1 ();
+ notify ();
+ }
+ }
+ };
+
+ EmacsService.syncRunnable (runnable);
+ return rc.thing;
+ }
+
+
+
+ @Override
+ public void
+ onDismiss (DialogInterface dialog)
+ {
+ Log.d (TAG, "onDismiss: " + this);
+
+ if (wasButtonClicked)
+ return;
+
+ EmacsNative.sendContextMenu ((short) 0, 0, menuEventSerial);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsDocumentsProvider.java b/java/org/gnu/emacs/EmacsDocumentsProvider.java
new file mode 100644
index 00000000000..b4ac4624829
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDocumentsProvider.java
@@ -0,0 +1,575 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.content.Context;
+
+import android.database.Cursor;
+import android.database.MatrixCursor;
+
+import android.os.Build;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+
+import android.provider.DocumentsContract.Document;
+import android.provider.DocumentsContract.Root;
+import static android.provider.DocumentsContract.buildChildDocumentsUri;
+import android.provider.DocumentsProvider;
+
+import android.webkit.MimeTypeMap;
+
+import android.net.Uri;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/* ``Documents provider''. This allows Emacs's home directory to be
+ modified by other programs holding permissions to manage system
+ storage, which is useful to (for example) correct misconfigurations
+ which prevent Emacs from starting up.
+
+ This functionality is only available on Android 19 and later. */
+
+public final class EmacsDocumentsProvider extends DocumentsProvider
+{
+ /* Home directory. This is the directory whose contents are
+ initially returned to requesting applications. */
+ private File baseDir;
+
+ /* The default projection for requests for the root directory. */
+ private static final String[] DEFAULT_ROOT_PROJECTION;
+
+ /* The default projection for requests for a file. */
+ private static final String[] DEFAULT_DOCUMENT_PROJECTION;
+
+ static
+ {
+ DEFAULT_ROOT_PROJECTION = new String[] {
+ Root.COLUMN_ROOT_ID,
+ Root.COLUMN_MIME_TYPES,
+ Root.COLUMN_FLAGS,
+ Root.COLUMN_ICON,
+ Root.COLUMN_TITLE,
+ Root.COLUMN_SUMMARY,
+ Root.COLUMN_DOCUMENT_ID,
+ Root.COLUMN_AVAILABLE_BYTES,
+ };
+
+ DEFAULT_DOCUMENT_PROJECTION = new String[] {
+ Document.COLUMN_DOCUMENT_ID,
+ Document.COLUMN_MIME_TYPE,
+ Document.COLUMN_DISPLAY_NAME,
+ Document.COLUMN_LAST_MODIFIED,
+ Document.COLUMN_FLAGS,
+ Document.COLUMN_SIZE,
+ };
+ }
+
+ @Override
+ public boolean
+ onCreate ()
+ {
+ /* Set the base directory to Emacs's files directory. */
+ baseDir = getContext ().getFilesDir ();
+ return true;
+ }
+
+ @Override
+ public Cursor
+ queryRoots (String[] projection)
+ {
+ MatrixCursor result;
+ MatrixCursor.RowBuilder row;
+
+ /* If the requestor asked for nothing at all, then it wants some
+ data by default. */
+
+ if (projection == null)
+ projection = DEFAULT_ROOT_PROJECTION;
+
+ result = new MatrixCursor (projection);
+ row = result.newRow ();
+
+ /* Now create and add a row for each file in the base
+ directory. */
+ row.add (Root.COLUMN_ROOT_ID, baseDir.getAbsolutePath ());
+ row.add (Root.COLUMN_SUMMARY, "Emacs home directory");
+
+ /* Add the appropriate flags. */
+
+ row.add (Root.COLUMN_FLAGS, (Root.FLAG_SUPPORTS_CREATE
+ | Root.FLAG_SUPPORTS_IS_CHILD));
+ row.add (Root.COLUMN_ICON, R.drawable.emacs);
+ row.add (Root.FLAG_LOCAL_ONLY);
+ row.add (Root.COLUMN_TITLE, "Emacs");
+ row.add (Root.COLUMN_DOCUMENT_ID, baseDir.getAbsolutePath ());
+
+ return result;
+ }
+
+ private Uri
+ getNotificationUri (File file)
+ {
+ Uri updatedUri;
+ Context context;
+
+ context = getContext ();
+ updatedUri
+ = buildChildDocumentsUri ("org.gnu.emacs",
+ file.getAbsolutePath ());
+
+ return updatedUri;
+ }
+
+ /* Inform the system that FILE's contents (or FILE itself) has
+ changed. */
+
+ private void
+ notifyChange (File file)
+ {
+ Uri updatedUri;
+ Context context;
+
+ context = getContext ();
+ updatedUri
+ = buildChildDocumentsUri ("org.gnu.emacs",
+ file.getAbsolutePath ());
+ context.getContentResolver ().notifyChange (updatedUri, null);
+ }
+
+ /* Inform the system that FILE's contents (or FILE itself) has
+ changed. FILE is a string describing containing the file name of
+ a directory as opposed to a File. */
+
+ private void
+ notifyChangeByName (String file)
+ {
+ Uri updatedUri;
+ Context context;
+
+ context = getContext ();
+ updatedUri
+ = buildChildDocumentsUri ("org.gnu.emacs", file);
+ context.getContentResolver ().notifyChange (updatedUri, null);
+ }
+
+ /* Return the MIME type of a file FILE. */
+
+ private String
+ getMimeType (File file)
+ {
+ String name, extension, mime;
+ int extensionSeparator;
+ MimeTypeMap singleton;
+
+ if (file.isDirectory ())
+ return Document.MIME_TYPE_DIR;
+
+ /* Abuse WebView stuff to get the file's MIME type. */
+ name = file.getName ();
+ extensionSeparator = name.lastIndexOf ('.');
+
+ if (extensionSeparator > 0)
+ {
+ singleton = MimeTypeMap.getSingleton ();
+ extension = name.substring (extensionSeparator + 1);
+ mime = singleton.getMimeTypeFromExtension (extension);
+
+ if (mime != null)
+ return mime;
+ }
+
+ return "application/octet-stream";
+ }
+
+ /* Append the specified FILE to the query result RESULT.
+ Handle both directories and ordinary files. */
+
+ private void
+ queryDocument1 (MatrixCursor result, File file)
+ {
+ MatrixCursor.RowBuilder row;
+ String fileName, displayName, mimeType;
+ int flags;
+
+ row = result.newRow ();
+ flags = 0;
+
+ /* fileName is a string that the system will ask for some time in
+ the future. Here, it is just the absolute name of the file. */
+ fileName = file.getAbsolutePath ();
+
+ /* If file is a directory, add the right flags for that. */
+
+ if (file.isDirectory ())
+ {
+ if (file.canWrite ())
+ {
+ flags |= Document.FLAG_DIR_SUPPORTS_CREATE;
+ flags |= Document.FLAG_SUPPORTS_DELETE;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ flags |= Document.FLAG_SUPPORTS_RENAME;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ flags |= Document.FLAG_SUPPORTS_MOVE;
+ }
+ }
+ else if (file.canWrite ())
+ {
+ /* Apply the correct flags for a writable file. */
+ flags |= Document.FLAG_SUPPORTS_WRITE;
+ flags |= Document.FLAG_SUPPORTS_DELETE;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ flags |= Document.FLAG_SUPPORTS_RENAME;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ {
+ flags |= Document.FLAG_SUPPORTS_REMOVE;
+ flags |= Document.FLAG_SUPPORTS_MOVE;
+ }
+ }
+
+ displayName = file.getName ();
+ mimeType = getMimeType (file);
+
+ row.add (Document.COLUMN_DOCUMENT_ID, fileName);
+ row.add (Document.COLUMN_DISPLAY_NAME, displayName);
+ row.add (Document.COLUMN_SIZE, file.length ());
+ row.add (Document.COLUMN_MIME_TYPE, mimeType);
+ row.add (Document.COLUMN_LAST_MODIFIED, file.lastModified ());
+ row.add (Document.COLUMN_FLAGS, flags);
+ }
+
+ @Override
+ public Cursor
+ queryDocument (String documentId, String[] projection)
+ throws FileNotFoundException
+ {
+ MatrixCursor result;
+ File file;
+ Context context;
+
+ file = new File (documentId);
+ context = getContext ();
+
+ if (projection == null)
+ projection = DEFAULT_DOCUMENT_PROJECTION;
+
+ result = new MatrixCursor (projection);
+ queryDocument1 (result, file);
+
+ /* Now allow interested applications to detect changes. */
+ result.setNotificationUri (context.getContentResolver (),
+ getNotificationUri (file));
+
+ return result;
+ }
+
+ @Override
+ public Cursor
+ queryChildDocuments (String parentDocumentId, String[] projection,
+ String sortOrder) throws FileNotFoundException
+ {
+ MatrixCursor result;
+ File directory;
+ Context context;
+
+ if (projection == null)
+ projection = DEFAULT_DOCUMENT_PROJECTION;
+
+ result = new MatrixCursor (projection);
+
+ /* Try to open the file corresponding to the location being
+ requested. */
+ directory = new File (parentDocumentId);
+
+ /* Now add each child. */
+ for (File child : directory.listFiles ())
+ queryDocument1 (result, child);
+
+ context = getContext ();
+
+ /* Now allow interested applications to detect changes. */
+ result.setNotificationUri (context.getContentResolver (),
+ getNotificationUri (directory));
+
+ return result;
+ }
+
+ @Override
+ public ParcelFileDescriptor
+ openDocument (String documentId, String mode,
+ CancellationSignal signal) throws FileNotFoundException
+ {
+ return ParcelFileDescriptor.open (new File (documentId),
+ ParcelFileDescriptor.parseMode (mode));
+ }
+
+ @Override
+ public String
+ createDocument (String documentId, String mimeType,
+ String displayName) throws FileNotFoundException
+ {
+ File file, parentFile;
+ boolean rc;
+
+ file = new File (documentId, displayName);
+
+ try
+ {
+ rc = false;
+
+ if (Document.MIME_TYPE_DIR.equals (mimeType))
+ {
+ file.mkdirs ();
+
+ if (file.isDirectory ())
+ rc = true;
+ }
+ else
+ {
+ file.createNewFile ();
+
+ if (file.isFile ()
+ && file.setWritable (true)
+ && file.setReadable (true))
+ rc = true;
+ }
+
+ if (!rc)
+ throw new FileNotFoundException ("rc != 1");
+ }
+ catch (IOException e)
+ {
+ throw new FileNotFoundException (e.toString ());
+ }
+
+ parentFile = file.getParentFile ();
+
+ if (parentFile != null)
+ notifyChange (parentFile);
+
+ return file.getAbsolutePath ();
+ }
+
+ private void
+ deleteDocument1 (File child)
+ {
+ File[] children;
+
+ /* Don't delete symlinks recursively.
+
+ Calling readlink or stat is problematic due to file name
+ encoding problems, so try to delete the file first, and only
+ try to delete files recursively afterword. */
+
+ if (child.delete ())
+ return;
+
+ children = child.listFiles ();
+
+ if (children != null)
+ {
+ for (File file : children)
+ deleteDocument1 (file);
+ }
+
+ child.delete ();
+ }
+
+ @Override
+ public void
+ deleteDocument (String documentId)
+ throws FileNotFoundException
+ {
+ File file, parent;
+ File[] children;
+ Context context;
+
+ /* Java makes recursively deleting a file hard. File name
+ encoding issues also prevent easily calling into C... */
+
+ context = getContext ();
+ file = new File (documentId);
+ parent = file.getParentFile ();
+
+ if (parent == null)
+ throw new RuntimeException ("trying to delete file without"
+ + " parent!");
+
+ if (file.delete ())
+ {
+ /* Tell the system about the change. */
+ notifyChange (parent);
+ return;
+ }
+
+ children = file.listFiles ();
+
+ if (children != null)
+ {
+ for (File child : children)
+ deleteDocument1 (child);
+ }
+
+ if (file.delete ())
+ /* Tell the system about the change. */
+ notifyChange (parent);
+ }
+
+ @Override
+ public void
+ removeDocument (String documentId, String parentDocumentId)
+ throws FileNotFoundException
+ {
+ deleteDocument (documentId);
+ }
+
+ @Override
+ public String
+ getDocumentType (String documentId)
+ {
+ return getMimeType (new File (documentId));
+ }
+
+ @Override
+ public String
+ renameDocument (String documentId, String displayName)
+ throws FileNotFoundException
+ {
+ File file, newName;
+ File parent;
+
+ file = new File (documentId);
+ parent = file.getParentFile ();
+ newName = new File (parent, displayName);
+
+ if (parent == null)
+ throw new FileNotFoundException ("parent is null");
+
+ file = new File (documentId);
+
+ if (!file.renameTo (newName))
+ return null;
+
+ notifyChange (parent);
+ return newName.getAbsolutePath ();
+ }
+
+ @Override
+ public boolean
+ isChildDocument (String parentDocumentId, String documentId)
+ {
+ return documentId.startsWith (parentDocumentId);
+ }
+
+ @Override
+ public String
+ moveDocument (String sourceDocumentId,
+ String sourceParentDocumentId,
+ String targetParentDocumentId)
+ throws FileNotFoundException
+ {
+ File file, newName;
+ FileInputStream inputStream;
+ FileOutputStream outputStream;
+ byte buffer[];
+ int length;
+
+ file = new File (sourceDocumentId);
+
+ /* Now, create the file name of the parent document. */
+ newName = new File (targetParentDocumentId,
+ file.getName ());
+
+ /* Try to perform a simple rename, before falling back to
+ copying. */
+
+ if (file.renameTo (newName))
+ {
+ notifyChangeByName (file.getParent ());
+ notifyChangeByName (targetParentDocumentId);
+ return newName.getAbsolutePath ();
+ }
+
+ /* If that doesn't work, create the new file and copy over the old
+ file's contents. */
+
+ inputStream = null;
+ outputStream = null;
+
+ try
+ {
+ if (!newName.createNewFile ()
+ || !newName.setWritable (true)
+ || !newName.setReadable (true))
+ throw new FileNotFoundException ("failed to create new file");
+
+ /* Open the file in preparation for a copy. */
+
+ inputStream = new FileInputStream (file);
+ outputStream = new FileOutputStream (newName);
+
+ /* Allocate the buffer used to hold data. */
+
+ buffer = new byte[4096];
+
+ while ((length = inputStream.read (buffer)) > 0)
+ outputStream.write (buffer, 0, length);
+ }
+ catch (IOException e)
+ {
+ throw new FileNotFoundException ("IOException: " + e);
+ }
+ finally
+ {
+ try
+ {
+ if (inputStream != null)
+ inputStream.close ();
+ }
+ catch (IOException e)
+ {
+
+ }
+
+ try
+ {
+ if (outputStream != null)
+ outputStream.close ();
+ }
+ catch (IOException e)
+ {
+
+ }
+ }
+
+ file.delete ();
+ notifyChangeByName (file.getParent ());
+ notifyChangeByName (targetParentDocumentId);
+
+ return newName.getAbsolutePath ();
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawLine.java b/java/org/gnu/emacs/EmacsDrawLine.java
new file mode 100644
index 00000000000..92e03c48e26
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawLine.java
@@ -0,0 +1,66 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.lang.Math;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+public final class EmacsDrawLine
+{
+ public static void
+ perform (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y, int x2, int y2)
+ {
+ Rect rect;
+ Canvas canvas;
+ Paint paint;
+
+ /* TODO implement stippling. */
+ if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+ return;
+
+ paint = gc.gcPaint;
+ rect = new Rect (Math.min (x, x2 + 1),
+ Math.min (y, y2 + 1),
+ Math.max (x2 + 1, x),
+ Math.max (y2 + 1, y));
+ canvas = drawable.lockCanvas (gc);
+
+ if (canvas == null)
+ return;
+
+ paint.setStyle (Paint.Style.STROKE);
+
+ /* Since drawLine has PostScript style behavior, adjust the
+ coordinates appropriately. */
+
+ if (gc.clip_mask == null)
+ canvas.drawLine ((float) x, (float) y + 0.5f,
+ (float) x2 + 0.5f, (float) y2 + 0.5f,
+ paint);
+
+ /* DrawLine with clip mask not implemented; it is not used by
+ Emacs. */
+ drawable.damageRect (rect);
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawPoint.java b/java/org/gnu/emacs/EmacsDrawPoint.java
new file mode 100644
index 00000000000..de8ddf09cc4
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawPoint.java
@@ -0,0 +1,31 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+public final class EmacsDrawPoint
+{
+ public static void
+ perform (EmacsDrawable drawable,
+ EmacsGC immutableGC, int x, int y)
+ {
+ EmacsDrawRectangle.perform (drawable, immutableGC,
+ x, y, 1, 1);
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsDrawRectangle.java b/java/org/gnu/emacs/EmacsDrawRectangle.java
new file mode 100644
index 00000000000..3bd5779c54e
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawRectangle.java
@@ -0,0 +1,123 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+import android.util.Log;
+
+public final class EmacsDrawRectangle
+{
+ public static void
+ perform (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y, int width, int height)
+ {
+ Paint maskPaint, paint;
+ Canvas maskCanvas;
+ Bitmap maskBitmap;
+ Rect rect;
+ Rect maskRect, dstRect;
+ Canvas canvas;
+ Bitmap clipBitmap;
+
+ /* TODO implement stippling. */
+ if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+ return;
+
+ canvas = drawable.lockCanvas (gc);
+
+ if (canvas == null)
+ return;
+
+ paint = gc.gcPaint;
+ paint.setStyle (Paint.Style.STROKE);
+ rect = new Rect (x, y, x + width, y + height);
+
+ if (gc.clip_mask == null)
+ /* 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
+ intersection of the clip mask with the dst rect, and
+ extrapolating the corresponding part of the src rect. */
+ clipBitmap = gc.clip_mask.bitmap;
+ dstRect = new Rect (x, y, x + width, y + height);
+ maskRect = new Rect (gc.clip_x_origin,
+ gc.clip_y_origin,
+ (gc.clip_x_origin
+ + clipBitmap.getWidth ()),
+ (gc.clip_y_origin
+ + clipBitmap.getHeight ()));
+ clipBitmap = gc.clip_mask.bitmap;
+
+ if (!maskRect.setIntersect (dstRect, maskRect))
+ /* There is no intersection between the clip mask and the
+ dest rect. */
+ return;
+
+ /* Finally, create a temporary bitmap that is the size of
+ maskRect. */
+
+ maskBitmap
+ = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
+ Bitmap.Config.ARGB_8888);
+
+ /* Draw the mask onto the maskBitmap. */
+ maskCanvas = new Canvas (maskBitmap);
+ maskRect.offset (-gc.clip_x_origin,
+ -gc.clip_y_origin);
+ maskCanvas.drawBitmap (gc.clip_mask.bitmap,
+ maskRect, new Rect (0, 0,
+ maskRect.width (),
+ maskRect.height ()),
+ paint);
+ maskRect.offset (gc.clip_x_origin,
+ gc.clip_y_origin);
+
+ /* Set the transfer mode to SRC_IN to preserve only the parts
+ of the source that overlap with the mask. */
+ maskPaint = new Paint ();
+ maskPaint.setXfermode (EmacsGC.srcInAlu);
+ maskPaint.setStyle (Paint.Style.STROKE);
+
+ /* Draw the source. */
+ maskCanvas.drawRect (maskRect, maskPaint);
+
+ /* Finally, draw the mask bitmap to the destination. */
+ paint.setXfermode (null);
+ canvas.drawBitmap (maskBitmap, null, maskRect, paint);
+
+ /* Recycle this unused bitmap. */
+ maskBitmap.recycle ();
+ }
+
+ 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
new file mode 100644
index 00000000000..f2f8885e976
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsDrawable.java
@@ -0,0 +1,32 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.graphics.Rect;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+
+public interface EmacsDrawable
+{
+ 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
new file mode 100644
index 00000000000..ea8324c543c
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsFillPolygon.java
@@ -0,0 +1,82 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.lang.Math;
+
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+public final class EmacsFillPolygon
+{
+ public static void
+ perform (EmacsDrawable drawable, EmacsGC gc, Point points[])
+ {
+ Canvas canvas;
+ Path path;
+ Paint paint;
+ Rect rect;
+ RectF rectF;
+ int i;
+
+ canvas = drawable.lockCanvas (gc);
+
+ if (canvas == null)
+ return;
+
+ paint = gc.gcPaint;
+
+ /* Build the path from the given array of points. */
+ path = new Path ();
+
+ if (points.length >= 1)
+ {
+ path.moveTo (points[0].x, points[0].y);
+
+ for (i = 1; i < points.length; ++i)
+ path.lineTo (points[i].x, points[i].y);
+
+ path.close ();
+ }
+
+ /* Compute the damage rectangle. */
+ rectF = new RectF (0, 0, 0, 0);
+ path.computeBounds (rectF, true);
+
+ rect = new Rect ((int) Math.floor (rectF.left),
+ (int) Math.floor (rectF.top),
+ (int) Math.ceil (rectF.right),
+ (int) Math.ceil (rectF.bottom));
+
+ paint.setStyle (Paint.Style.FILL);
+
+ if (gc.clip_mask == null)
+ canvas.drawPath (path, paint);
+
+ drawable.damageRect (rect);
+
+ /* FillPolygon with clip mask not implemented; it is not used by
+ Emacs. */
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsFillRectangle.java b/java/org/gnu/emacs/EmacsFillRectangle.java
new file mode 100644
index 00000000000..4a0478b446f
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsFillRectangle.java
@@ -0,0 +1,116 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+import android.util.Log;
+
+public final class EmacsFillRectangle
+{
+ public static void
+ perform (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y, int width, int height)
+ {
+ Paint maskPaint, paint;
+ Canvas maskCanvas;
+ Bitmap maskBitmap;
+ Rect rect;
+ Rect maskRect, dstRect;
+ Canvas canvas;
+ Bitmap clipBitmap;
+
+ /* TODO implement stippling. */
+ if (gc.fill_style == EmacsGC.GC_FILL_OPAQUE_STIPPLED)
+ return;
+
+ canvas = drawable.lockCanvas (gc);
+
+ if (canvas == null)
+ return;
+
+ paint = gc.gcPaint;
+ rect = new Rect (x, y, x + width, y + height);
+
+ paint.setStyle (Paint.Style.FILL);
+
+ if (gc.clip_mask == null)
+ canvas.drawRect (rect, paint);
+ else
+ {
+ /* Drawing with a clip mask involves calculating the
+ intersection of the clip mask with the dst rect, and
+ extrapolating the corresponding part of the src rect. */
+ clipBitmap = gc.clip_mask.bitmap;
+ dstRect = new Rect (x, y, x + width, y + height);
+ maskRect = new Rect (gc.clip_x_origin,
+ gc.clip_y_origin,
+ (gc.clip_x_origin
+ + clipBitmap.getWidth ()),
+ (gc.clip_y_origin
+ + clipBitmap.getHeight ()));
+ clipBitmap = gc.clip_mask.bitmap;
+
+ if (!maskRect.setIntersect (dstRect, maskRect))
+ /* There is no intersection between the clip mask and the
+ dest rect. */
+ return;
+
+ /* Finally, create a temporary bitmap that is the size of
+ maskRect. */
+
+ maskBitmap
+ = Bitmap.createBitmap (maskRect.width (), maskRect.height (),
+ Bitmap.Config.ARGB_8888);
+
+ /* Draw the mask onto the maskBitmap. */
+ maskCanvas = new Canvas (maskBitmap);
+ maskRect.offset (-gc.clip_x_origin,
+ -gc.clip_y_origin);
+ maskCanvas.drawBitmap (gc.clip_mask.bitmap,
+ maskRect, new Rect (0, 0,
+ maskRect.width (),
+ maskRect.height ()),
+ paint);
+ maskRect.offset (gc.clip_x_origin,
+ gc.clip_y_origin);
+
+ /* Set the transfer mode to SRC_IN to preserve only the parts
+ of the source that overlap with the mask. */
+ maskPaint = new Paint ();
+ maskPaint.setXfermode (EmacsGC.srcInAlu);
+
+ /* Draw the source. */
+ maskCanvas.drawRect (maskRect, maskPaint);
+
+ /* Finally, draw the mask bitmap to the destination. */
+ paint.setXfermode (null);
+ canvas.drawBitmap (maskBitmap, null, maskRect, paint);
+
+ /* Recycle this unused bitmap. */
+ maskBitmap.recycle ();
+ }
+
+ drawable.damageRect (rect);
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsFontDriver.java b/java/org/gnu/emacs/EmacsFontDriver.java
new file mode 100644
index 00000000000..ff52899a897
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsFontDriver.java
@@ -0,0 +1,173 @@
+/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.os.Build;
+
+/* This code is mostly unused. See sfntfont-android.c for the code
+ that is actually used. */
+
+public abstract class EmacsFontDriver
+{
+ /* Font weights. */
+ public static final int THIN = 0;
+ public static final int ULTRA_LIGHT = 40;
+ public static final int LIGHT = 50;
+ public static final int SEMI_LIGHT = 55;
+ public static final int REGULAR = 80;
+ public static final int MEDIUM = 100;
+ public static final int SEMI_BOLD = 180;
+ public static final int BOLD = 200;
+ public static final int EXTRA_BOLD = 205;
+ public static final int BLACK = 210;
+ public static final int ULTRA_HEAVY = 250;
+
+ /* Font slants. */
+ public static final int REVERSE_OBLIQUE = 0;
+ public static final int REVERSE_ITALIC = 10;
+ public static final int NORMAL = 100;
+ public static final int ITALIC = 200;
+ public static final int OBLIQUE = 210;
+
+ /* Font widths. */
+ public static final int ULTRA_CONDENSED = 50;
+ public static final int EXTRA_CONDENSED = 63;
+ public static final int CONDENSED = 75;
+ public static final int SEMI_CONDENSED = 87;
+ public static final int UNSPECIFIED = 100;
+ public static final int SEMI_EXPANDED = 113;
+ public static final int EXPANDED = 125;
+ public static final int EXTRA_EXPANDED = 150;
+ public static final int ULTRA_EXPANDED = 200;
+
+ /* Font spacings. */
+ public static final int PROPORTIONAL = 0;
+ public static final int DUAL = 90;
+ public static final int MONO = 100;
+ public static final int CHARCELL = 110;
+
+ public static class FontSpec
+ {
+ /* The fields below mean the same as they do in enum
+ font_property_index in font.h. */
+
+ public String foundry;
+ public String family;
+ public String adstyle;
+ public String registry;
+ public Integer width;
+ public Integer weight;
+ public Integer slant;
+ public Integer size;
+ public Integer spacing;
+ public Integer avgwidth;
+ public Integer dpi;
+
+ @Override
+ public String
+ toString ()
+ {
+ return ("foundry: " + foundry
+ + " family: " + family
+ + " adstyle: " + adstyle
+ + " registry: " + registry
+ + " width: " + width
+ + " weight: " + weight
+ + " slant: " + slant
+ + " spacing: " + spacing
+ + " avgwidth: " + avgwidth
+ + " dpi: " + dpi);
+ }
+ };
+
+ public static class FontMetrics
+ {
+ public short lbearing;
+ public short rbearing;
+ public short width;
+ public short ascent;
+ public short descent;
+
+ @Override
+ public String
+ toString ()
+ {
+ return ("lbearing " + lbearing
+ + " rbearing " + rbearing
+ + " width " + width
+ + " ascent " + ascent
+ + " descent " + descent);
+ }
+ }
+
+ public static class FontEntity extends FontSpec
+ {
+ /* No extra fields here. */
+ };
+
+ public abstract class FontObject extends FontSpec
+ {
+ public int minWidth;
+ public int maxWidth;
+ public int pixelSize;
+ public int height;
+ public int spaceWidth;
+ public int averageWidth;
+ public int ascent;
+ public int descent;
+ public int underlineThickness;
+ public int underlinePosition;
+ public int baselineOffset;
+ public int relativeCompose;
+ public int defaultAscent;
+ public int encodingCharset;
+ public int repertoryCharset;
+
+ public
+ FontObject ()
+ {
+ encodingCharset = -1;
+ repertoryCharset = -1;
+ }
+ };
+
+ /* These mean the same as they do in struct font_driver. */
+ public abstract FontEntity[] list (FontSpec fontSpec);
+ public abstract FontEntity match (FontSpec fontSpec);
+ public abstract String[] listFamilies ();
+ public abstract FontObject openFont (FontEntity fontEntity, int pixelSize);
+ public abstract int hasChar (FontSpec font, char charCode);
+ public abstract void textExtents (FontObject font, int code[],
+ FontMetrics fontMetrics);
+ public abstract int encodeChar (FontObject fontObject, char charCode);
+ public abstract int draw (FontObject fontObject, EmacsGC gc,
+ EmacsDrawable drawable, int[] chars,
+ int x, int y, int backgroundWidth,
+ boolean withBackground);
+
+ public static EmacsFontDriver
+ createFontDriver ()
+ {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ return new EmacsSdk23FontDriver ();
+
+ return new EmacsSdk7FontDriver ();
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsGC.java b/java/org/gnu/emacs/EmacsGC.java
new file mode 100644
index 00000000000..a7467cb9bd0
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsGC.java
@@ -0,0 +1,121 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.graphics.Rect;
+import android.graphics.Paint;
+
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Xfermode;
+
+/* X like graphics context structures. Keep the enums in synch with
+ androidgui.h! */
+
+public final class EmacsGC extends EmacsHandleObject
+{
+ public static final int GC_COPY = 0;
+ public static final int GC_XOR = 1;
+
+ public static final int GC_FILL_SOLID = 0;
+ public static final int GC_FILL_OPAQUE_STIPPLED = 1;
+
+ public static final Xfermode xorAlu, srcInAlu;
+
+ public int function, fill_style;
+ public int foreground, background;
+ public int clip_x_origin, clip_y_origin;
+ public int ts_origin_x, ts_origin_y;
+ public Rect clip_rects[], real_clip_rects[];
+ 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);
+ srcInAlu = new PorterDuffXfermode (Mode.SRC_IN);
+ }
+
+ /* The following fields are only set on immutable GCs. */
+
+ public
+ EmacsGC (short handle)
+ {
+ /* For historical reasons the C code has an extra layer of
+ indirection above this GC handle. struct android_gc is the GC
+ used by Emacs code, while android_gcontext is the type of the
+ handle. */
+ super (handle);
+
+ fill_style = GC_FILL_SOLID;
+ function = GC_COPY;
+ foreground = 0;
+ background = 0xffffff;
+ gcPaint = new Paint ();
+ }
+
+ /* Mark this GC as dirty. Apply parameters to the paint and
+ recompute real_clip_rects. */
+
+ public void
+ markDirty (boolean clipRectsChanged)
+ {
+ int i;
+
+ if (clipRectsChanged)
+ {
+ if ((ts_origin_x != 0 || ts_origin_y != 0)
+ && clip_rects != null)
+ {
+ 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;
+ }
+
+ gcPaint.setStrokeWidth (1f);
+ gcPaint.setColor (foreground | 0xff000000);
+ gcPaint.setXfermode (function == GC_XOR
+ ? xorAlu : srcInAlu);
+ }
+
+ public void
+ resetXfermode ()
+ {
+ gcPaint.setXfermode (function == GC_XOR
+ ? xorAlu : srcInAlu);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsHandleObject.java b/java/org/gnu/emacs/EmacsHandleObject.java
new file mode 100644
index 00000000000..5b889895337
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsHandleObject.java
@@ -0,0 +1,59 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.lang.IllegalStateException;
+
+/* This defines something that is a so-called ``handle''. Handles
+ must be created by C code, and will remain existing until
+ destroyHandle is called. C code then refers to the handle by a
+ number which maps into the Java object representing the handle.
+
+ All handle operations must be done from the Emacs thread. */
+
+public abstract class EmacsHandleObject
+{
+ /* Whether or not this handle has been destroyed. */
+ volatile boolean destroyed;
+
+ /* The handle associated with this object. */
+ public short handle;
+
+ public
+ EmacsHandleObject (short handle)
+ {
+ this.handle = handle;
+ }
+
+ public void
+ destroyHandle () throws IllegalStateException
+ {
+ synchronized (this)
+ {
+ destroyed = true;
+ }
+ }
+
+ public boolean
+ isDestroyed ()
+ {
+ return destroyed;
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsInputConnection.java b/java/org/gnu/emacs/EmacsInputConnection.java
new file mode 100644
index 00000000000..21bbaca5d07
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsInputConnection.java
@@ -0,0 +1,348 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.TextSnapshot;
+import android.view.KeyEvent;
+
+import android.os.Build;
+
+import android.util.Log;
+
+/* Android input methods, take number six. See textconv.c for more
+ details; this is more-or-less a thin wrapper around that file. */
+
+public final class EmacsInputConnection extends BaseInputConnection
+{
+ private static final String TAG = "EmacsInputConnection";
+ private EmacsView view;
+ private short windowHandle;
+
+ /* Whether or not to synchronize and call `updateIC' with the
+ selection position after committing text.
+
+ This helps with on screen keyboard programs found in some vendor
+ versions of Android, which rely on immediate updates to the point
+ position after text is commited in order to place the cursor
+ within that text. */
+
+ private static boolean syncAfterCommit;
+
+ /* Whether or not to return empty text with the offset set to zero
+ if a request arrives that has no flags set and has requested no
+ characters at all.
+
+ This is necessary with on screen keyboard programs found in some
+ vendor versions of Android which don't rely on the documented
+ meaning of `ExtractedText.startOffset', and instead take the
+ selection offset inside at face value. */
+
+ private static boolean extractAbsoluteOffsets;
+
+ static
+ {
+ if (Build.MANUFACTURER.equalsIgnoreCase ("Huawei")
+ || Build.MANUFACTURER.equalsIgnoreCase ("Honor"))
+ extractAbsoluteOffsets = syncAfterCommit = true;
+ };
+
+ public
+ EmacsInputConnection (EmacsView view)
+ {
+ super (view, true);
+
+ this.view = view;
+ this.windowHandle = view.window.handle;
+ }
+
+ @Override
+ public boolean
+ beginBatchEdit ()
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "beginBatchEdit");
+
+ EmacsNative.beginBatchEdit (windowHandle);
+ return true;
+ }
+
+ @Override
+ public boolean
+ endBatchEdit ()
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "endBatchEdit");
+
+ EmacsNative.endBatchEdit (windowHandle);
+ return true;
+ }
+
+ @Override
+ public boolean
+ commitCompletion (CompletionInfo info)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "commitCompletion: " + info);
+
+ EmacsNative.commitCompletion (windowHandle,
+ info.getText ().toString (),
+ info.getPosition ());
+ return true;
+ }
+
+ @Override
+ public boolean
+ commitText (CharSequence text, int newCursorPosition)
+ {
+ int[] selection;
+
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "commitText: " + text + " " + newCursorPosition);
+
+ EmacsNative.commitText (windowHandle, text.toString (),
+ newCursorPosition);
+
+ if (syncAfterCommit)
+ {
+ /* Synchronize with the Emacs thread, obtain the new
+ selection, and report it immediately. */
+
+ selection = EmacsNative.getSelection (windowHandle);
+
+ if (EmacsService.DEBUG_IC && selection != null)
+ Log.d (TAG, "commitText: new selection is " + selection[0]
+ + ", by " + selection[1]);
+
+ if (selection != null)
+ /* N.B. that the composing region is removed after text is
+ committed. */
+ view.imManager.updateSelection (view, selection[0],
+ selection[1], -1, -1);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean
+ deleteSurroundingText (int leftLength, int rightLength)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, ("deleteSurroundingText: "
+ + leftLength + " " + rightLength));
+
+ EmacsNative.deleteSurroundingText (windowHandle, leftLength,
+ rightLength);
+ return true;
+ }
+
+ @Override
+ public boolean
+ finishComposingText ()
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "finishComposingText");
+
+ EmacsNative.finishComposingText (windowHandle);
+ return true;
+ }
+
+ @Override
+ public String
+ getSelectedText (int flags)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "getSelectedText: " + flags);
+
+ return EmacsNative.getSelectedText (windowHandle, flags);
+ }
+
+ @Override
+ public String
+ getTextAfterCursor (int length, int flags)
+ {
+ String string;
+
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "getTextAfterCursor: " + length + " " + flags);
+
+ string = EmacsNative.getTextAfterCursor (windowHandle, length,
+ flags);
+
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, " --> " + string);
+
+ return string;
+ }
+
+ @Override
+ public String
+ getTextBeforeCursor (int length, int flags)
+ {
+ String string;
+
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "getTextBeforeCursor: " + length + " " + flags);
+
+ string = EmacsNative.getTextBeforeCursor (windowHandle, length,
+ flags);
+
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, " --> " + string);
+
+ return string;
+ }
+
+ @Override
+ public boolean
+ setComposingText (CharSequence text, int newCursorPosition)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, ("setComposingText: "
+ + text + " ## " + newCursorPosition));
+
+ EmacsNative.setComposingText (windowHandle, text.toString (),
+ newCursorPosition);
+ return true;
+ }
+
+ @Override
+ public boolean
+ setComposingRegion (int start, int end)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "setComposingRegion: " + start + " " + end);
+
+ EmacsNative.setComposingRegion (windowHandle, start, end);
+ return true;
+ }
+
+ @Override
+ public boolean
+ performEditorAction (int editorAction)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "performEditorAction: " + editorAction);
+
+ EmacsNative.performEditorAction (windowHandle, editorAction);
+ return true;
+ }
+
+ @Override
+ public ExtractedText
+ getExtractedText (ExtractedTextRequest request, int flags)
+ {
+ ExtractedText text;
+ int[] selection;
+
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "getExtractedText: " + request.hintMaxChars + ", "
+ + request.hintMaxLines + " " + flags);
+
+ /* If a request arrives with hintMaxChars, hintMaxLines and flags
+ set to 0, and the system is known to be buggy, return an empty
+ extracted text object with the absolute selection positions. */
+
+ if (extractAbsoluteOffsets
+ && request.hintMaxChars == 0
+ && request.hintMaxLines == 0
+ && flags == 0)
+ {
+ /* Obtain the selection. */
+ selection = EmacsNative.getSelection (windowHandle);
+ if (selection == null)
+ return null;
+
+ /* Create the workaround extracted text. */
+ text = new ExtractedText ();
+ text.partialStartOffset = -1;
+ text.partialEndOffset = -1;
+ text.text = "";
+ text.selectionStart = selection[0];
+ text.selectionEnd = selection[1];
+ }
+ else
+ text = EmacsNative.getExtractedText (windowHandle, request,
+ flags);
+
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "getExtractedText: " + text.text + " @"
+ + text.startOffset + ":" + text.selectionStart
+ + ", " + text.selectionEnd);
+
+ return text;
+ }
+
+ @Override
+ public boolean
+ setSelection (int start, int end)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "setSelection: " + start + " " + end);
+
+ EmacsNative.setSelection (windowHandle, start, end);
+ return true;
+ }
+
+ @Override
+ public boolean
+ sendKeyEvent (KeyEvent key)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "sendKeyEvent: " + key);
+
+ return super.sendKeyEvent (key);
+ }
+
+ @Override
+ public boolean
+ deleteSurroundingTextInCodePoints (int beforeLength, int afterLength)
+ {
+ /* This can be implemented the same way as
+ deleteSurroundingText. */
+ return this.deleteSurroundingText (beforeLength, afterLength);
+ }
+
+ @Override
+ public boolean
+ requestCursorUpdates (int cursorUpdateMode)
+ {
+ if (EmacsService.DEBUG_IC)
+ Log.d (TAG, "requestCursorUpdates: " + cursorUpdateMode);
+
+ EmacsNative.requestCursorUpdates (windowHandle, cursorUpdateMode);
+ return true;
+ }
+
+
+ /* Override functions which are not implemented. */
+
+ @Override
+ public TextSnapshot
+ takeSnapshot ()
+ {
+ Log.d (TAG, "takeSnapshot");
+ return null;
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsMultitaskActivity.java b/java/org/gnu/emacs/EmacsMultitaskActivity.java
new file mode 100644
index 00000000000..b1c48f03fba
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsMultitaskActivity.java
@@ -0,0 +1,29 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+/* This class only exists because EmacsActivity is already defined as
+ an activity, and the system wants a new class in order to define a
+ new activity. */
+
+public final class EmacsMultitaskActivity extends EmacsActivity
+{
+
+}
diff --git a/java/org/gnu/emacs/EmacsNative.java b/java/org/gnu/emacs/EmacsNative.java
new file mode 100644
index 00000000000..e699dda9ad4
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsNative.java
@@ -0,0 +1,254 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.content.res.AssetManager;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+
+public final class EmacsNative
+{
+ /* List of native libraries that must be loaded during class
+ initialization. */
+ private static final String[] libraryDeps;
+
+
+ /* Like `dup' in C. */
+ public static native int dup (int fd);
+
+ /* Obtain the fingerprint of this build of Emacs. The fingerprint
+ can be used to determine the dump file name. */
+ public static native String getFingerprint ();
+
+ /* Set certain parameters before initializing Emacs.
+
+ assetManager must be the asset manager associated with the
+ context that is loading Emacs. It is saved and remains for the
+ remainder the lifetime of the Emacs process.
+
+ filesDir must be the package's data storage location for the
+ current Android user.
+
+ libDir must be the package's data storage location for native
+ libraries. It is used as PATH.
+
+ cacheDir must be the package's cache directory. It is used as
+ the `temporary-file-directory'.
+
+ pixelDensityX and pixelDensityY are the DPI values that will be
+ used by Emacs.
+
+ classPath must be the classpath of this app_process process, or
+ NULL.
+
+ emacsService must be the EmacsService singleton, or NULL. */
+ public static native void setEmacsParams (AssetManager assetManager,
+ String filesDir,
+ String libDir,
+ String cacheDir,
+ float pixelDensityX,
+ float pixelDensityY,
+ String classPath,
+ EmacsService emacsService);
+
+ /* Initialize Emacs with the argument array ARGV. Each argument
+ must contain a NULL terminated string, or else the behavior is
+ undefined.
+
+ DUMPFILE is the dump file to use, or NULL if Emacs is to load
+ loadup.el itself.
+
+ APILEVEL is the version of Android being used. */
+ public static native void initEmacs (String argv[], String dumpFile,
+ int apiLevel);
+
+ /* Abort and generate a native core dump. */
+ public static native void emacsAbort ();
+
+ /* Set Vquit_flag to t, resulting in Emacs quitting as soon as
+ possible. */
+ public static native void quit ();
+
+ /* Send an ANDROID_CONFIGURE_NOTIFY event. The values of all the
+ functions below are the serials of the events sent. */
+ public static native long sendConfigureNotify (short window, long time,
+ int x, int y, int width,
+ int height);
+
+ /* Send an ANDROID_KEY_PRESS event. */
+ public static native long sendKeyPress (short window, long time, int state,
+ int keyCode, int unicodeChar);
+
+ /* Send an ANDROID_KEY_RELEASE event. */
+ public static native long sendKeyRelease (short window, long time, int state,
+ int keyCode, int unicodeChar);
+
+ /* Send an ANDROID_FOCUS_IN event. */
+ public static native long sendFocusIn (short window, long time);
+
+ /* Send an ANDROID_FOCUS_OUT event. */
+ public static native long sendFocusOut (short window, long time);
+
+ /* Send an ANDROID_WINDOW_ACTION event. */
+ public static native long sendWindowAction (short window, int action);
+
+ /* Send an ANDROID_ENTER_NOTIFY event. */
+ public static native long sendEnterNotify (short window, int x, int y,
+ long time);
+
+ /* Send an ANDROID_LEAVE_NOTIFY event. */
+ public static native long sendLeaveNotify (short window, int x, int y,
+ long time);
+
+ /* Send an ANDROID_MOTION_NOTIFY event. */
+ public static native long sendMotionNotify (short window, int x, int y,
+ long time);
+
+ /* Send an ANDROID_BUTTON_PRESS event. */
+ public static native long sendButtonPress (short window, int x, int y,
+ long time, int state,
+ int button);
+
+ /* Send an ANDROID_BUTTON_RELEASE event. */
+ public static native long sendButtonRelease (short window, int x, int y,
+ long time, int state,
+ int button);
+
+ /* Send an ANDROID_TOUCH_DOWN event. */
+ public static native long sendTouchDown (short window, int x, int y,
+ long time, int pointerID);
+
+ /* Send an ANDROID_TOUCH_UP event. */
+ public static native long sendTouchUp (short window, int x, int y,
+ long time, int pointerID);
+
+ /* Send an ANDROID_TOUCH_MOVE event. */
+ public static native long sendTouchMove (short window, int x, int y,
+ long time, int pointerID);
+
+ /* Send an ANDROID_WHEEL event. */
+ public static native long sendWheel (short window, int x, int y,
+ long time, int state,
+ float xDelta, float yDelta);
+
+ /* Send an ANDROID_ICONIFIED event. */
+ public static native long sendIconified (short window);
+
+ /* Send an ANDROID_DEICONIFIED event. */
+ public static native long sendDeiconified (short window);
+
+ /* Send an ANDROID_CONTEXT_MENU event. */
+ public static native long sendContextMenu (short window, int menuEventID,
+ int menuEventSerial);
+
+ /* Send an ANDROID_EXPOSE event. */
+ public static native long sendExpose (short window, int x, int y,
+ int width, int height);
+
+ /* Return the file name associated with the specified file
+ descriptor, or NULL if there is none. */
+ public static native byte[] getProcName (int fd);
+
+ /* Notice that the Emacs thread will now start waiting for the main
+ thread's looper to respond. */
+ public static native void beginSynchronous ();
+
+ /* Notice that the Emacs thread will has finished waiting for the
+ main thread's looper to respond. */
+ public static native void endSynchronous ();
+
+ /* Return whether or not KEYCODE_VOLUME_DOWN, KEYCODE_VOLUME_UP and
+ KEYCODE_VOLUME_MUTE should be forwarded to Emacs. */
+ public static native boolean shouldForwardMultimediaButtons ();
+
+
+
+ /* Input connection functions. These mostly correspond to their
+ counterparts in Android's InputConnection. */
+
+ public static native void beginBatchEdit (short window);
+ public static native void endBatchEdit (short window);
+ public static native void commitCompletion (short window, String text,
+ int position);
+ public static native void commitText (short window, String text,
+ int position);
+ public static native void deleteSurroundingText (short window,
+ int leftLength,
+ int rightLength);
+ public static native void finishComposingText (short window);
+ public static native String getSelectedText (short window, int flags);
+ public static native String getTextAfterCursor (short window, int length,
+ int flags);
+ public static native String getTextBeforeCursor (short window, int length,
+ int flags);
+ public static native void setComposingText (short window, String text,
+ int newCursorPosition);
+ public static native void setComposingRegion (short window, int start,
+ int end);
+ public static native void setSelection (short window, int start, int end);
+ public static native void performEditorAction (short window,
+ int editorAction);
+ public static native ExtractedText getExtractedText (short window,
+ ExtractedTextRequest req,
+ int flags);
+ public static native void requestSelectionUpdate (short window);
+ public static native void requestCursorUpdates (short window, int mode);
+
+
+ /* Return the current value of the selection, or -1 upon
+ failure. */
+ public static native int[] getSelection (short window);
+
+ static
+ {
+ /* Older versions of Android cannot link correctly with shared
+ libraries that link with other shared libraries built along
+ Emacs unless all requisite shared libraries are explicitly
+ loaded from Java.
+
+ Every time you add a new shared library dependency to Emacs,
+ please add it here as well. */
+
+ libraryDeps = new String[] { "png_emacs", "selinux_emacs",
+ "crypto_emacs", "pcre_emacs",
+ "packagelistparser_emacs",
+ "gnutls_emacs", "gmp_emacs",
+ "nettle_emacs", "p11-kit_emacs",
+ "tasn1_emacs", "hogweed_emacs",
+ "jansson_emacs", "jpeg_emacs",
+ "tiff_emacs", "xml2_emacs",
+ "icuuc_emacs",
+ "tree-sitter_emacs", };
+
+ for (String dependency : libraryDeps)
+ {
+ try
+ {
+ System.loadLibrary (dependency);
+ }
+ catch (UnsatisfiedLinkError exception)
+ {
+ /* Ignore this exception. */
+ }
+ }
+
+ System.loadLibrary ("emacs");
+ };
+};
diff --git a/java/org/gnu/emacs/EmacsNoninteractive.java b/java/org/gnu/emacs/EmacsNoninteractive.java
new file mode 100644
index 00000000000..aaba74d877c
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsNoninteractive.java
@@ -0,0 +1,203 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.os.Looper;
+import android.os.Build;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+/* Noninteractive Emacs.
+
+ This is the class that libandroid-emacs.so starts.
+ libandroid-emacs.so figures out the system classpath, then starts
+ dalvikvm with the framework jars.
+
+ At that point, dalvikvm calls main, which sets up the main looper,
+ creates an ActivityThread and attaches it to the main thread.
+
+ Then, it obtains an application context for the LoadedApk in the
+ application thread.
+
+ Finally, it obtains the necessary context specific objects and
+ initializes Emacs. */
+
+@SuppressWarnings ("unchecked")
+public final class EmacsNoninteractive
+{
+ public static void
+ main (String[] args)
+ {
+ Object activityThread, loadedApk;
+ Class activityThreadClass, loadedApkClass, contextImplClass;
+ Class compatibilityInfoClass;
+ Method method;
+ Context context;
+ AssetManager assets;
+ String filesDir, libDir, cacheDir;
+
+ Looper.prepare ();
+ context = null;
+ assets = null;
+ filesDir = libDir = cacheDir = null;
+
+ try
+ {
+ /* Get the activity thread. */
+ activityThreadClass = Class.forName ("android.app.ActivityThread");
+
+ /* Get the systemMain method. */
+ method = activityThreadClass.getMethod ("systemMain");
+
+ /* Create and attach the activity thread. */
+ activityThread = method.invoke (null);
+ context = null;
+
+ /* Now get an LoadedApk. */
+
+ try
+ {
+ loadedApkClass = Class.forName ("android.app.LoadedApk");
+ }
+ catch (ClassNotFoundException exception)
+ {
+ /* Android 2.2 has no LoadedApk class, but fortunately it
+ does not need to be used, since contexts can be
+ directly created. */
+
+ loadedApkClass = null;
+ contextImplClass = Class.forName ("android.app.ContextImpl");
+
+ method = activityThreadClass.getDeclaredMethod ("getSystemContext");
+ context = (Context) method.invoke (activityThread);
+ method = contextImplClass.getDeclaredMethod ("createPackageContext",
+ String.class,
+ int.class);
+ method.setAccessible (true);
+ context = (Context) method.invoke (context, "org.gnu.emacs",
+ 0);
+ }
+
+ /* If the context has not already been created, then do what
+ is appropriate for newer versions of Android. */
+
+ if (context == null)
+ {
+ /* Get a LoadedApk. How to do this varies by Android version.
+ On Android 2.3.3 and earlier, there is no
+ ``compatibilityInfo'' argument to getPackageInfo. */
+
+ if (Build.VERSION.SDK_INT
+ <= Build.VERSION_CODES.GINGERBREAD_MR1)
+ {
+ method
+ = activityThreadClass.getMethod ("getPackageInfo",
+ String.class,
+ int.class);
+ loadedApk = method.invoke (activityThread, "org.gnu.emacs",
+ 0);
+ }
+ else
+ {
+ compatibilityInfoClass
+ = Class.forName ("android.content.res.CompatibilityInfo");
+
+ method
+ = activityThreadClass.getMethod ("getPackageInfo",
+ String.class,
+ compatibilityInfoClass,
+ int.class);
+ loadedApk = method.invoke (activityThread, "org.gnu.emacs",
+ null, 0);
+ }
+
+ if (loadedApk == null)
+ throw new RuntimeException ("getPackageInfo returned NULL");
+
+ /* Now, get a context. */
+ contextImplClass = Class.forName ("android.app.ContextImpl");
+
+ try
+ {
+ method
+ = contextImplClass.getDeclaredMethod ("createAppContext",
+ activityThreadClass,
+ loadedApkClass);
+ method.setAccessible (true);
+ context = (Context) method.invoke (null, activityThread,
+ loadedApk);
+ }
+ catch (NoSuchMethodException exception)
+ {
+ /* Older Android versions don't have createAppContext, but
+ instead require creating a ContextImpl, and then
+ calling createPackageContext. */
+ method
+ = activityThreadClass.getDeclaredMethod ("getSystemContext");
+ context = (Context) method.invoke (activityThread);
+ method
+ = contextImplClass.getDeclaredMethod ("createPackageContext",
+ String.class,
+ int.class);
+ method.setAccessible (true);
+ context = (Context) method.invoke (context, "org.gnu.emacs",
+ 0);
+ }
+ }
+
+ /* Don't actually start the looper or anything. Instead, obtain
+ an AssetManager. */
+ assets = context.getAssets ();
+
+ /* Now configure Emacs. The class path should already be set. */
+
+ filesDir = context.getFilesDir ().getCanonicalPath ();
+ libDir = EmacsService.getLibraryDirectory (context);
+ cacheDir = context.getCacheDir ().getCanonicalPath ();
+ }
+ catch (Exception e)
+ {
+ System.err.println ("Internal error: " + e);
+ System.err.println ("This means that the Android platform changed,");
+ System.err.println ("and that Emacs needs adjustments in order to");
+ System.err.println ("obtain required system internal resources.");
+ System.err.println ("Please report this bug to bug-gnu-emacs@gnu.org.");
+ e.printStackTrace ();
+
+ System.exit (1);
+ }
+
+ EmacsNative.setEmacsParams (assets, filesDir,
+ libDir, cacheDir, 0.0f,
+ 0.0f, null, null);
+
+ /* Now find the dump file that Emacs should use, if it has already
+ been dumped. */
+ EmacsApplication.findDumpFile (context);
+
+ /* Start Emacs. */
+ EmacsNative.initEmacs (args, EmacsApplication.dumpFileName,
+ Build.VERSION.SDK_INT);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsOpenActivity.java b/java/org/gnu/emacs/EmacsOpenActivity.java
new file mode 100644
index 00000000000..f402e25c7fb
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsOpenActivity.java
@@ -0,0 +1,472 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+/* This class makes the Emacs server work reasonably on Android.
+
+ There is no way to make the Unix socket publicly available on
+ Android.
+
+ Instead, this activity tries to connect to the Emacs server, to
+ make it open files the system asks Emacs to open, and to emulate
+ some reasonable behavior when Emacs has not yet started.
+
+ First, Emacs registers itself as an application that can open text
+ and image files.
+
+ Then, when the user is asked to open a file and selects ``Emacs''
+ as the application that will open the file, the system pops up a
+ window, this activity, and calls the `onCreate' function.
+
+ `onCreate' then tries very to find the file name of the file that
+ was selected, and give it to emacsclient.
+
+ If emacsclient successfully opens the file, then this activity
+ starts EmacsActivity (to bring it on to the screen); otherwise, it
+ displays the output of emacsclient or any error message that occurs
+ and exits. */
+
+import android.app.AlertDialog;
+import android.app.Activity;
+
+import android.content.ContentResolver;
+import android.content.DialogInterface;
+import android.content.Intent;
+
+import android.net.Uri;
+
+import android.os.Build;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+public final class EmacsOpenActivity extends Activity
+ implements DialogInterface.OnClickListener,
+ DialogInterface.OnCancelListener
+{
+ private static final String TAG = "EmacsOpenActivity";
+ public static String fileToOpen;
+
+ private class EmacsClientThread extends Thread
+ {
+ private ProcessBuilder builder;
+
+ public
+ EmacsClientThread (ProcessBuilder processBuilder)
+ {
+ builder = processBuilder;
+ }
+
+ @Override
+ public void
+ run ()
+ {
+ Process process;
+ InputStream error;
+ String errorText;
+
+ try
+ {
+ /* Start emacsclient. */
+ process = builder.start ();
+ process.waitFor ();
+
+ /* Now figure out whether or not starting the process was
+ successful. */
+ if (process.exitValue () == 0)
+ finishSuccess ();
+ else
+ finishFailure ("Error opening file", null);
+ }
+ catch (IOException exception)
+ {
+ finishFailure ("Internal error", exception.toString ());
+ }
+ catch (InterruptedException exception)
+ {
+ finishFailure ("Internal error", exception.toString ());
+ }
+ }
+ }
+
+ @Override
+ public void
+ onClick (DialogInterface dialog, int which)
+ {
+ finish ();
+ }
+
+ @Override
+ public void
+ onCancel (DialogInterface dialog)
+ {
+ finish ();
+ }
+
+ public String
+ readEmacsClientLog ()
+ {
+ File file, cache;
+ FileReader reader;
+ char[] buffer;
+ int rc;
+ String what;
+
+ /* Because the ProcessBuilder functions necessary to redirect
+ process output are not implemented on Android 7 and earlier,
+ print a generic error message. */
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ return ("This is likely because the Emacs server"
+ + " is not running, or because you did"
+ + " not grant Emacs permission to access"
+ + " external storage.");
+
+ cache = getCacheDir ();
+ file = new File (cache, "emacsclient.log");
+ what = "";
+
+ try
+ {
+ reader = new FileReader (file);
+ buffer = new char[2048];
+
+ while ((rc = reader.read (buffer, 0, 2048)) != -1)
+ what += String.valueOf (buffer, 0, 2048);
+
+ reader.close ();
+ return what;
+ }
+ catch (IOException exception)
+ {
+ return ("Couldn't read emacsclient.log: "
+ + exception.toString ());
+ }
+ }
+
+ private void
+ displayFailureDialog (String title, String text)
+ {
+ AlertDialog.Builder builder;
+ AlertDialog dialog;
+
+ builder = new AlertDialog.Builder (this);
+ dialog = builder.create ();
+ dialog.setTitle (title);
+
+ if (text == null)
+ /* Read in emacsclient.log instead. */
+ text = readEmacsClientLog ();
+
+ dialog.setMessage (text);
+ dialog.setButton (DialogInterface.BUTTON_POSITIVE, "OK", this);
+ dialog.setOnCancelListener (this);
+ dialog.show ();
+ }
+
+ /* Check that the specified FILE is readable. If Android 4.4 or
+ later is being used, return URI formatted into a `/content/' file
+ name.
+
+ If it is not, then copy the file in FD to a location in the
+ system cache directory and return the name of that file. */
+
+ private String
+ checkReadableOrCopy (String file, ParcelFileDescriptor fd,
+ Uri uri)
+ throws IOException, FileNotFoundException
+ {
+ File inFile;
+ FileOutputStream outStream;
+ InputStream stream;
+ byte buffer[];
+ int read;
+ String content;
+
+ Log.d (TAG, "checkReadableOrCopy: " + file);
+
+ inFile = new File (file);
+
+ if (inFile.canRead ())
+ return file;
+
+ Log.d (TAG, "checkReadableOrCopy: NO");
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
+ {
+ content = "/content/" + uri.getEncodedAuthority ();
+
+ for (String segment : uri.getPathSegments ())
+ content += "/" + Uri.encode (segment);
+
+ /* Append the URI query. */
+
+ if (uri.getEncodedQuery () != null)
+ content += "?" + uri.getEncodedQuery ();
+
+ Log.d (TAG, "checkReadableOrCopy: " + content);
+
+ return content;
+ }
+
+ /* inFile is now the file being written to. */
+ inFile = new File (getCacheDir (), inFile.getName ());
+ buffer = new byte[4098];
+ outStream = new FileOutputStream (inFile);
+ stream = new FileInputStream (fd.getFileDescriptor ());
+
+ try
+ {
+ while ((read = stream.read (buffer)) >= 0)
+ outStream.write (buffer, 0, read);
+ }
+ finally
+ {
+ /* Note that this does not close FD.
+
+ Keep in mind that execution is transferred to ``finally''
+ even if an exception happens inside the while loop
+ above. */
+ stream.close ();
+ outStream.close ();
+ }
+
+ return inFile.getCanonicalPath ();
+ }
+
+ /* Finish this activity in response to emacsclient having
+ successfully opened a file.
+
+ In the main thread, close this window, and open a window
+ belonging to an Emacs frame. */
+
+ public void
+ finishSuccess ()
+ {
+ runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ Intent intent;
+
+ intent = new Intent (EmacsOpenActivity.this,
+ EmacsActivity.class);
+
+ /* This means only an existing frame will be displayed. */
+ intent.addFlags (Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
+ startActivity (intent);
+
+ EmacsOpenActivity.this.finish ();
+ }
+ });
+ }
+
+ /* Finish this activity after displaying a dialog associated with
+ failure to open a file.
+
+ Use TITLE as the title of the dialog. If TEXT is non-NULL,
+ display that text in the dialog. Otherwise, use the contents of
+ emacsclient.log in the cache directory instead, or describe why
+ that file cannot be read. */
+
+ public void
+ finishFailure (final String title, final String text)
+ {
+ runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ displayFailureDialog (title, text);
+ }
+ });
+ }
+
+ public void
+ startEmacsClient (String fileName)
+ {
+ String libDir;
+ ProcessBuilder builder;
+ Process process;
+ EmacsClientThread thread;
+ File file;
+ Intent intent;
+
+ /* If the Emacs service is not running, then start Emacs and make
+ it open this file. */
+
+ if (EmacsService.SERVICE == null)
+ {
+ fileToOpen = fileName;
+ intent = new Intent (EmacsOpenActivity.this,
+ EmacsActivity.class);
+ finish ();
+ startActivity (intent);
+ return;
+ }
+
+ libDir = EmacsService.getLibraryDirectory (this);
+ builder = new ProcessBuilder (libDir + "/libemacsclient.so",
+ fileName, "--reuse-frame",
+ "--timeout=10", "--no-wait");
+
+ /* Redirection is unfortunately not possible in Android 7 and
+ earlier. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ {
+ file = new File (getCacheDir (), "emacsclient.log");
+
+ /* Redirect standard error to a file so that errors can be
+ meaningfully reported. */
+
+ if (file.exists ())
+ file.delete ();
+
+ builder.redirectError (file);
+ }
+
+ /* Track process output in a new thread, since this is the UI
+ thread and doing so here can cause deadlocks when EmacsService
+ decides to wait for something. */
+
+ thread = new EmacsClientThread (builder);
+ thread.start ();
+ }
+
+ @Override
+ public void
+ onCreate (Bundle savedInstanceState)
+ {
+ String action, fileName;
+ Intent intent;
+ Uri uri;
+ ContentResolver resolver;
+ ParcelFileDescriptor fd;
+ byte[] names;
+ String errorBlurb;
+
+ super.onCreate (savedInstanceState);
+
+ /* Obtain the intent that started Emacs. */
+ intent = getIntent ();
+ action = intent.getAction ();
+
+ if (action == null)
+ {
+ finish ();
+ return;
+ }
+
+ /* Now see if the action specified is supported by Emacs. */
+
+ if (action.equals ("android.intent.action.VIEW")
+ || action.equals ("android.intent.action.EDIT")
+ || action.equals ("android.intent.action.PICK"))
+ {
+ /* Obtain the URI of the action. */
+ uri = intent.getData ();
+
+ if (uri == null)
+ {
+ finish ();
+ return;
+ }
+
+ /* Now, try to get the file name. */
+
+ if (uri.getScheme ().equals ("file"))
+ fileName = uri.getPath ();
+ else
+ {
+ fileName = null;
+
+ if (uri.getScheme ().equals ("content"))
+ {
+ /* This is one of the annoying Android ``content''
+ URIs. Most of the time, there is actually an
+ underlying file, but it cannot be found without
+ opening the file and doing readlink on its file
+ descriptor in /proc/self/fd. */
+ resolver = getContentResolver ();
+ fd = null;
+
+ try
+ {
+ fd = resolver.openFileDescriptor (uri, "r");
+ names = EmacsNative.getProcName (fd.getFd ());
+
+ /* What is the right encoding here? */
+
+ if (names != null)
+ fileName = new String (names, "UTF-8");
+
+ fileName = checkReadableOrCopy (fileName, fd, uri);
+ }
+ catch (FileNotFoundException exception)
+ {
+ /* Do nothing. */
+ }
+ catch (IOException exception)
+ {
+ /* Do nothing. */
+ }
+
+ if (fd != null)
+ {
+ try
+ {
+ fd.close ();
+ }
+ catch (IOException exception)
+ {
+ /* Do nothing. */
+ }
+ }
+ }
+
+ if (fileName == null)
+ {
+ errorBlurb = ("The URI: " + uri + " could not be opened"
+ + ", as it does not encode file name inform"
+ + "ation.");
+ displayFailureDialog ("Error opening file", errorBlurb);
+ return;
+ }
+ }
+
+ /* And start emacsclient. */
+ startEmacsClient (fileName);
+ }
+ else
+ finish ();
+ }
+}
diff --git a/java/org/gnu/emacs/EmacsPixmap.java b/java/org/gnu/emacs/EmacsPixmap.java
new file mode 100644
index 00000000000..eb011bc5e65
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsPixmap.java
@@ -0,0 +1,192 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.lang.IllegalArgumentException;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+
+import android.os.Build;
+
+/* Drawable backed by bitmap. */
+
+public final class EmacsPixmap extends EmacsHandleObject
+ implements EmacsDrawable
+{
+ /* The depth of the bitmap. This is not actually used, just defined
+ in order to be consistent with X. */
+ public int depth, width, height;
+
+ /* The bitmap itself. */
+ public Bitmap bitmap;
+
+ /* 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)
+ {
+ super (handle);
+
+ if (depth != 1 && depth != 24)
+ throw new IllegalArgumentException ("Invalid depth specified"
+ + " for pixmap: " + depth);
+
+ switch (depth)
+ {
+ case 1:
+ bitmap = Bitmap.createBitmap (colors, width, height,
+ Bitmap.Config.ALPHA_8);
+ break;
+
+ case 24:
+ bitmap = Bitmap.createBitmap (colors, width, height,
+ Bitmap.Config.ARGB_8888);
+ bitmap.setHasAlpha (false);
+ break;
+ }
+
+ this.width = width;
+ this.height = height;
+ this.depth = depth;
+ }
+
+ public
+ EmacsPixmap (short handle, int width, int height, int depth)
+ {
+ super (handle);
+
+ if (depth != 1 && depth != 24)
+ throw new IllegalArgumentException ("Invalid depth specified"
+ + " for pixmap: " + depth);
+
+ switch (depth)
+ {
+ case 1:
+ 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:
+
+ /* 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;
+ this.height = height;
+ this.depth = depth;
+ }
+
+ @Override
+ public Canvas
+ lockCanvas (EmacsGC gc)
+ {
+ int i;
+
+ if (canvas == null)
+ {
+ 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;
+ }
+
+ @Override
+ public void
+ damageRect (Rect damageRect)
+ {
+
+ }
+
+ @Override
+ public Bitmap
+ getBitmap ()
+ {
+ return bitmap;
+ }
+
+ @Override
+ public void
+ destroyHandle ()
+ {
+ bitmap.recycle ();
+ bitmap = null;
+
+ /* Collect the bitmap storage if the bitmap is big. */
+ if (needCollect)
+ Runtime.getRuntime ().gc ();
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsPreferencesActivity.java b/java/org/gnu/emacs/EmacsPreferencesActivity.java
new file mode 100644
index 00000000000..70934fa4bd4
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsPreferencesActivity.java
@@ -0,0 +1,141 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.io.File;
+
+import android.app.Activity;
+
+import android.content.Intent;
+
+import android.os.Bundle;
+import android.os.Build;
+
+import android.widget.Toast;
+
+import android.preference.*;
+
+/* This module provides a ``preferences'' display for Emacs. It is
+ supposed to be launched from inside the Settings application to
+ perform various actions, such as starting Emacs with the ``-Q''
+ option, which would not be possible otherwise, as there is no
+ command line on Android.
+
+ Android provides a preferences activity, but it is deprecated.
+ Unfortunately, there is no alternative that looks the same way. */
+
+@SuppressWarnings ("deprecation")
+public final class EmacsPreferencesActivity extends PreferenceActivity
+{
+ /* Restart Emacs with -Q. Call EmacsThread.exit to kill Emacs now, and
+ tell the system to EmacsActivity with some parameters later. */
+
+ private void
+ startEmacsQ ()
+ {
+ Intent intent;
+
+ intent = new Intent (this, EmacsActivity.class);
+ intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.putExtra ("org.gnu.emacs.START_DASH_Q", true);
+ startActivity (intent);
+ System.exit (0);
+ }
+
+ /* Erase Emacs's dump file. */
+
+ private void
+ eraseDumpFile ()
+ {
+ String wantedDumpFile;
+ File file;
+ Toast toast;
+
+ wantedDumpFile = ("emacs-" + EmacsNative.getFingerprint ()
+ + ".pdmp");
+ file = new File (getFilesDir (), wantedDumpFile);
+
+ if (file.exists ())
+ file.delete ();
+
+ /* Make sure to clear EmacsApplication.dumpFileName, or
+ starting Emacs without restarting this program will
+ make Emacs try to load a nonexistent dump file. */
+ EmacsApplication.dumpFileName = null;
+
+ /* Display a message stating that the dump file has been
+ erased. */
+ toast = Toast.makeText (this, "Dump file removed",
+ Toast.LENGTH_SHORT);
+ toast.show ();
+ }
+
+ @Override
+ public void
+ onCreate (Bundle savedInstanceState)
+ {
+ Preference tem;
+ Preference.OnPreferenceClickListener listener;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
+ setTheme (android.R.style.Theme_DeviceDefault_Settings);
+ else if (Build.VERSION.SDK_INT
+ >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ setTheme (android.R.style.Theme_DeviceDefault);
+
+ /* This must come before using any preference APIs. */
+ super.onCreate (savedInstanceState);
+
+ /* Add preferences from the XML file where they are defined. */
+ addPreferencesFromResource (R.xml.preferences);
+
+ /* Now, set up on click handlers for each of the preferences
+ items. */
+
+ tem = findPreference ("start_quick");
+
+ listener = new Preference.OnPreferenceClickListener () {
+ @Override
+ public boolean
+ onPreferenceClick (Preference preference)
+ {
+ startEmacsQ ();
+ return true;
+ }
+ };
+
+ tem.setOnPreferenceClickListener (listener);
+
+ tem = findPreference ("erase_dump");
+
+ listener = new Preference.OnPreferenceClickListener () {
+ @Override
+ public boolean
+ onPreferenceClick (Preference preference)
+ {
+ eraseDumpFile ();
+ return true;
+ }
+ };
+
+ tem.setOnPreferenceClickListener (listener);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk11Clipboard.java b/java/org/gnu/emacs/EmacsSdk11Clipboard.java
new file mode 100644
index 00000000000..4959ec36eed
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk11Clipboard.java
@@ -0,0 +1,290 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.ClipData;
+import android.content.ClipDescription;
+
+import android.content.res.AssetFileDescriptor;
+
+import android.net.Uri;
+
+import android.util.Log;
+
+import android.os.Build;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+/* This class implements EmacsClipboard for Android 3.0 and later
+ systems. */
+
+public final class EmacsSdk11Clipboard extends EmacsClipboard
+ implements ClipboardManager.OnPrimaryClipChangedListener
+{
+ private static final String TAG = "EmacsSdk11Clipboard";
+ private ClipboardManager manager;
+ private boolean ownsClipboard;
+ private int clipboardChangedCount;
+ private int monitoredClipboardChangedCount;
+ private ContentResolver resolver;
+
+ public
+ EmacsSdk11Clipboard ()
+ {
+ manager = EmacsService.SERVICE.getClipboardManager ();
+
+ /* The system forbids Emacs from reading clipboard data in the
+ background under Android 10 or later. */
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)
+ manager.addPrimaryClipChangedListener (this);
+
+ /* Now obtain the content resolver used to open file
+ descriptors. */
+
+ resolver = EmacsService.SERVICE.getContentResolver ();
+ }
+
+ @Override
+ public synchronized void
+ onPrimaryClipChanged ()
+ {
+ Log.d (TAG, ("onPrimaryClipChanged: "
+ + monitoredClipboardChangedCount
+ + " " + clipboardChangedCount));
+
+ /* Increment monitoredClipboardChangeCount. If it is now greater
+ than clipboardChangedCount, then Emacs no longer owns the
+ clipboard. */
+ monitoredClipboardChangedCount++;
+
+ if (monitoredClipboardChangedCount > clipboardChangedCount)
+ {
+ ownsClipboard = false;
+
+ /* Reset both values back to 0. */
+ monitoredClipboardChangedCount = 0;
+ clipboardChangedCount = 0;
+ }
+ }
+
+ /* Set the clipboard text to CLIPBOARD, a string in UTF-8
+ encoding. */
+
+ @Override
+ public synchronized void
+ setClipboard (byte[] bytes)
+ {
+ ClipData data;
+ String string;
+
+ try
+ {
+ string = new String (bytes, "UTF-8");
+ data = ClipData.newPlainText ("Emacs", string);
+ manager.setPrimaryClip (data);
+ ownsClipboard = true;
+
+ /* onPrimaryClipChanged will be called again. Use this
+ variable to keep track of how many times the clipboard has
+ been changed. */
+ ++clipboardChangedCount;
+ }
+ catch (UnsupportedEncodingException exception)
+ {
+ Log.w (TAG, "setClipboard: " + exception);
+ }
+ }
+
+ /* Return whether or not Emacs owns the clipboard. Value is 1 if
+ Emacs does, 0 if Emacs does not, and -1 if that information is
+ unavailable. */
+
+ @Override
+ public synchronized int
+ ownsClipboard ()
+ {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
+ return -1;
+
+ return ownsClipboard ? 1 : 0;
+ }
+
+ /* Return whether or not clipboard content currently exists. */
+
+ @Override
+ public boolean
+ clipboardExists ()
+ {
+ return manager.hasPrimaryClip ();
+ }
+
+ /* Return the current content of the clipboard, as plain text, or
+ NULL if no content is available. */
+
+ @Override
+ public byte[]
+ getClipboard ()
+ {
+ ClipData clip;
+ CharSequence text;
+ Context context;
+
+ clip = manager.getPrimaryClip ();
+
+ if (clip == null || clip.getItemCount () < 1)
+ return null;
+
+ context = EmacsService.SERVICE;
+
+ try
+ {
+ text = clip.getItemAt (0).coerceToText (context);
+ return text.toString ().getBytes ("UTF-8");
+ }
+ catch (UnsupportedEncodingException exception)
+ {
+ Log.w (TAG, "getClipboard: " + exception);
+ }
+
+ return null;
+ }
+
+ /* Return an array of targets currently provided by the
+ clipboard, or NULL if there are none. */
+
+ @Override
+ public byte[][]
+ getClipboardTargets ()
+ {
+ ClipData clip;
+ ClipDescription description;
+ byte[][] typeArray;
+ int i;
+
+ /* N.B. that Android calls the clipboard the ``primary clip''; it
+ is not related to the X primary selection. */
+ clip = manager.getPrimaryClip ();
+ description = clip.getDescription ();
+ i = description.getMimeTypeCount ();
+ typeArray = new byte[i][i];
+
+ try
+ {
+ for (i = 0; i < description.getMimeTypeCount (); ++i)
+ typeArray[i] = description.getMimeType (i).getBytes ("UTF-8");
+ }
+ catch (UnsupportedEncodingException exception)
+ {
+ return null;
+ }
+
+ return typeArray;
+ }
+
+ /* Return the clipboard data for the given target, or NULL if it
+ does not exist.
+
+ Value is normally an array of three longs: the file descriptor,
+ the start offset of the data, and its length; length may be
+ AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends
+ from that offset to the end of the file.
+
+ Do not use this function to open text targets; use `getClipboard'
+ for that instead, as it will handle selection data consisting
+ solely of a URI. */
+
+ @Override
+ public long[]
+ getClipboardData (byte[] target)
+ {
+ ClipData data;
+ String mimeType;
+ int fd;
+ AssetFileDescriptor assetFd;
+ Uri uri;
+ long[] value;
+
+ /* Decode the target given by Emacs. */
+ try
+ {
+ mimeType = new String (target, "UTF-8");
+ }
+ catch (UnsupportedEncodingException exception)
+ {
+ return null;
+ }
+
+ Log.d (TAG, "getClipboardData: "+ mimeType);
+
+ /* Now obtain the clipboard data and the data corresponding to
+ that MIME type. */
+
+ data = manager.getPrimaryClip ();
+
+ if (data.getItemCount () < 1)
+ return null;
+
+ try
+ {
+ uri = data.getItemAt (0).getUri ();
+
+ if (uri == null)
+ return null;
+
+ Log.d (TAG, "getClipboardData: "+ uri);
+
+ /* Now open the file descriptor. */
+ assetFd = resolver.openTypedAssetFileDescriptor (uri, mimeType,
+ null);
+
+ /* Duplicate the file descriptor. */
+ fd = assetFd.getParcelFileDescriptor ().getFd ();
+ fd = EmacsNative.dup (fd);
+
+ /* Return the relevant information. */
+ value = new long[] { fd, assetFd.getStartOffset (),
+ assetFd.getLength (), };
+
+ /* Close the original offset. */
+ assetFd.close ();
+
+ Log.d (TAG, "getClipboardData: "+ value);
+ }
+ catch (FileNotFoundException e)
+ {
+ return null;
+ }
+ catch (IOException e)
+ {
+ return null;
+ }
+
+ /* Don't return value if the file descriptor couldn't be
+ created. */
+
+ return fd != -1 ? value : null;
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk23FontDriver.java b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
new file mode 100644
index 00000000000..aaba8dbd166
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk23FontDriver.java
@@ -0,0 +1,114 @@
+/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.graphics.Paint;
+import android.graphics.Rect;
+
+public final class EmacsSdk23FontDriver extends EmacsSdk7FontDriver
+{
+ private void
+ textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
+ Paint paint, Rect bounds)
+ {
+ char[] text;
+
+ text = new char[2];
+ text[0] = (char) code;
+ text[1] = 'c';
+
+ paint.getTextBounds (text, 0, 1, bounds);
+
+ metrics.lbearing = (short) bounds.left;
+ metrics.rbearing = (short) bounds.right;
+ metrics.ascent = (short) -bounds.top;
+ metrics.descent = (short) bounds.bottom;
+ metrics.width
+ = (short) paint.getRunAdvance (text, 0, 1, 0, 1, false, 1);
+ }
+
+ @Override
+ public void
+ textExtents (FontObject font, int code[], FontMetrics fontMetrics)
+ {
+ int i;
+ Paint paintCache;
+ Rect boundsCache;
+ Sdk7FontObject fontObject;
+ char[] text;
+ float width;
+
+ fontObject = (Sdk7FontObject) font;
+ paintCache = fontObject.typeface.typefacePaint;
+ paintCache.setTextSize (fontObject.pixelSize);
+ boundsCache = new Rect ();
+
+ if (code.length == 0)
+ {
+ fontMetrics.lbearing = 0;
+ fontMetrics.rbearing = 0;
+ fontMetrics.ascent = 0;
+ fontMetrics.descent = 0;
+ fontMetrics.width = 0;
+ }
+ else if (code.length == 1)
+ textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
+ paintCache, boundsCache);
+ else
+ {
+ text = new char[code.length + 1];
+
+ for (i = 0; i < code.length; ++i)
+ text[i] = (char) code[i];
+
+ text[code.length] = 'c';
+
+ paintCache.getTextBounds (text, 0, code.length,
+ boundsCache);
+ width = paintCache.getRunAdvance (text, 0, code.length, 0,
+ code.length,
+ false, code.length);
+
+ fontMetrics.lbearing = (short) boundsCache.left;
+ fontMetrics.rbearing = (short) boundsCache.right;
+ fontMetrics.ascent = (short) -boundsCache.top;
+ fontMetrics.descent = (short) boundsCache.bottom;
+ fontMetrics.width = (short) width;
+ }
+ }
+
+ @Override
+ public int
+ hasChar (FontSpec font, char charCode)
+ {
+ Sdk7FontObject fontObject;
+ Paint paint;
+
+ if (font instanceof Sdk7FontObject)
+ {
+ fontObject = (Sdk7FontObject) font;
+ paint = fontObject.typeface.typefacePaint;
+ }
+ else
+ paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
+
+ return paint.hasGlyph (String.valueOf (charCode)) ? 1 : 0;
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk7FontDriver.java b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
new file mode 100644
index 00000000000..6df102f18a2
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk7FontDriver.java
@@ -0,0 +1,538 @@
+/* Font backend for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.io.File;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.Canvas;
+
+import android.util.Log;
+
+public class EmacsSdk7FontDriver extends EmacsFontDriver
+{
+ private static final String TOFU_STRING = "\uDB3F\uDFFD";
+ private static final String EM_STRING = "m";
+ private static final String TAG = "EmacsSdk7FontDriver";
+
+ protected static final class Sdk7Typeface
+ {
+ /* The typeface and paint. */
+ public Typeface typeface;
+ public Paint typefacePaint;
+ public String familyName;
+ public int slant, width, weight, spacing;
+
+ public
+ Sdk7Typeface (String fileName, Typeface typeface)
+ {
+ String style, testString;
+ int index, measured, i;
+ float[] widths;
+
+ slant = NORMAL;
+ weight = REGULAR;
+ width = UNSPECIFIED;
+ spacing = PROPORTIONAL;
+
+ this.typeface = typeface;
+
+ typefacePaint = new Paint ();
+ typefacePaint.setAntiAlias (true);
+ typefacePaint.setTypeface (typeface);
+
+ /* For the calls to measureText below. */
+ typefacePaint.setTextSize (10.0f);
+
+ /* Parse the file name into some useful data. First, strip off
+ the extension. */
+ fileName = fileName.split ("\\.", 2)[0];
+
+ /* Next, split the file name by dashes. Everything before the
+ last dash is part of the family name. */
+ index = fileName.lastIndexOf ("-");
+
+ if (index > 0)
+ {
+ style = fileName.substring (index + 1, fileName.length ());
+ familyName = fileName.substring (0, index);
+
+ /* Look for something describing the weight. */
+ if (style.contains ("Thin"))
+ weight = THIN;
+ else if (style.contains ("UltraLight"))
+ weight = ULTRA_LIGHT;
+ else if (style.contains ("SemiLight"))
+ weight = SEMI_LIGHT;
+ else if (style.contains ("Light"))
+ weight = LIGHT;
+ else if (style.contains ("Medium"))
+ weight = MEDIUM;
+ else if (style.contains ("SemiBold"))
+ weight = SEMI_BOLD;
+ else if (style.contains ("ExtraBold"))
+ weight = EXTRA_BOLD;
+ else if (style.contains ("Bold"))
+ weight = BOLD;
+ else if (style.contains ("Black"))
+ weight = BLACK;
+ else if (style.contains ("UltraHeavy"))
+ weight = ULTRA_HEAVY;
+
+ /* And the slant. */
+ if (style.contains ("ReverseOblique"))
+ slant = OBLIQUE;
+ else if (style.contains ("ReverseItalic"))
+ slant = REVERSE_ITALIC;
+ else if (style.contains ("Italic"))
+ slant = ITALIC;
+ else if (style.contains ("Oblique"))
+ slant = OBLIQUE;
+
+ /* Finally, the width. */
+ if (style.contains ("UltraCondensed"))
+ width = ULTRA_CONDENSED;
+ else if (style.contains ("ExtraCondensed"))
+ width = EXTRA_CONDENSED;
+ else if (style.contains ("SemiCondensed"))
+ width = SEMI_CONDENSED;
+ else if (style.contains ("Condensed"))
+ width = CONDENSED;
+ else if (style.contains ("SemiExpanded"))
+ width = SEMI_EXPANDED;
+ else if (style.contains ("ExtraExpanded"))
+ width = EXTRA_EXPANDED;
+ else if (style.contains ("UltraExpanded"))
+ width = ULTRA_EXPANDED;
+ else if (style.contains ("Expanded"))
+ width = EXPANDED;
+
+ /* Guess the spacing information. */
+ testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ widths = new float[testString.length ()];
+
+ measured = typefacePaint.getTextWidths (testString,
+ 0, testString.length (),
+ widths);
+ spacing = MONO;
+ for (i = 0; i < measured; ++i)
+ {
+ if (i != 0 && widths[i - 1] != widths[i])
+ /* This isn't a monospace font. */
+ spacing = PROPORTIONAL;
+ }
+ }
+ else
+ familyName = fileName;
+ }
+
+ @Override
+ public String
+ toString ()
+ {
+ return ("Sdk7Typeface ("
+ + String.valueOf (familyName) + ", "
+ + String.valueOf (slant) + ", "
+ + String.valueOf (width) + ", "
+ + String.valueOf (weight) + ", "
+ + String.valueOf (spacing) + ")");
+ }
+ };
+
+ protected static final class Sdk7FontEntity extends FontEntity
+ {
+ /* The typeface. */
+ public Sdk7Typeface typeface;
+
+ public
+ Sdk7FontEntity (Sdk7Typeface typeface)
+ {
+ float width;
+
+ foundry = "Google";
+ family = typeface.familyName;
+ adstyle = null;
+ weight = typeface.weight;
+ slant = typeface.slant;
+ spacing = typeface.spacing;
+ width = typeface.width;
+ dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
+
+ this.typeface = typeface;
+ }
+ };
+
+ protected final class Sdk7FontObject extends FontObject
+ {
+ /* The typeface. */
+ public Sdk7Typeface typeface;
+
+ public
+ Sdk7FontObject (Sdk7Typeface typeface, int pixelSize)
+ {
+ float totalWidth;
+ String testWidth, testString;
+
+ this.typeface = typeface;
+ this.pixelSize = pixelSize;
+
+ family = typeface.familyName;
+ adstyle = null;
+ weight = typeface.weight;
+ slant = typeface.slant;
+ spacing = typeface.spacing;
+ width = typeface.width;
+ dpi = Math.round (EmacsService.SERVICE.metrics.scaledDensity * 160f);
+
+ /* Compute the ascent and descent. */
+ typeface.typefacePaint.setTextSize (pixelSize);
+ ascent
+ = Math.round (-typeface.typefacePaint.ascent ());
+ descent
+ = Math.round (typeface.typefacePaint.descent ());
+
+ /* Compute the average width. */
+ testString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ totalWidth = typeface.typefacePaint.measureText (testString);
+
+ if (totalWidth > 0)
+ avgwidth = Math.round (totalWidth
+ / testString.length ());
+
+ /* Android doesn't expose the font average width and height
+ information, so this will have to do. */
+ minWidth = maxWidth = avgwidth;
+
+ /* This is different from avgwidth in the font spec! */
+ averageWidth = avgwidth;
+
+ /* Set the space width. */
+ totalWidth = typeface.typefacePaint.measureText (" ");
+ spaceWidth = Math.round (totalWidth);
+
+ /* Set the height and default ascent. */
+ height = ascent + descent;
+ defaultAscent = ascent;
+ }
+ };
+
+ private String[] fontFamilyList;
+ private Sdk7Typeface[] typefaceList;
+ private Sdk7Typeface fallbackTypeface;
+
+ public
+ EmacsSdk7FontDriver ()
+ {
+ int i;
+ File systemFontsDirectory, fontFile;
+ Typeface typeface;
+
+ systemFontsDirectory = new File ("/system/fonts");
+
+ fontFamilyList = systemFontsDirectory.list ();
+ typefaceList = new Sdk7Typeface[fontFamilyList.length + 3];
+
+ /* It would be nice to avoid opening each and every font upon
+ startup. But that doesn't seem to be possible on
+ Android. */
+
+ for (i = 0; i < fontFamilyList.length; ++i)
+ {
+ fontFile = new File (systemFontsDirectory,
+ fontFamilyList[i]);
+ typeface = Typeface.createFromFile (fontFile);
+ typefaceList[i] = new Sdk7Typeface (fontFile.getName (),
+ typeface);
+ }
+
+ /* Initialize the default monospace and serif typefaces. */
+ fallbackTypeface = new Sdk7Typeface ("monospace",
+ Typeface.MONOSPACE);
+ typefaceList[fontFamilyList.length] = fallbackTypeface;
+
+ fallbackTypeface = new Sdk7Typeface ("Monospace",
+ Typeface.MONOSPACE);
+ typefaceList[fontFamilyList.length + 1] = fallbackTypeface;
+
+ fallbackTypeface = new Sdk7Typeface ("Sans Serif",
+ Typeface.DEFAULT);
+ typefaceList[fontFamilyList.length + 2] = fallbackTypeface;
+ }
+
+ private boolean
+ checkMatch (Sdk7Typeface typeface, FontSpec fontSpec)
+ {
+ if (fontSpec.family != null
+ && !fontSpec.family.equals (typeface.familyName))
+ return false;
+
+ if (fontSpec.slant != null
+ && !fontSpec.weight.equals (typeface.weight))
+ return false;
+
+ if (fontSpec.spacing != null
+ && !fontSpec.spacing.equals (typeface.spacing))
+ return false;
+
+ if (fontSpec.weight != null
+ && !fontSpec.weight.equals (typeface.weight))
+ return false;
+
+ if (fontSpec.width != null
+ && !fontSpec.width.equals (typeface.width))
+ return false;
+
+ return true;
+ }
+
+ @Override
+ public FontEntity[]
+ list (FontSpec fontSpec)
+ {
+ LinkedList<FontEntity> list;
+ int i;
+
+ list = new LinkedList<FontEntity> ();
+
+ for (i = 0; i < typefaceList.length; ++i)
+ {
+ if (checkMatch (typefaceList[i], fontSpec))
+ list.add (new Sdk7FontEntity (typefaceList[i]));
+ }
+
+ return list.toArray (new FontEntity[0]);
+ }
+
+ @Override
+ public FontEntity
+ match (FontSpec fontSpec)
+ {
+ FontEntity[] entities;
+ int i;
+
+ entities = this.list (fontSpec);
+
+ if (entities.length == 0)
+ return new Sdk7FontEntity (fallbackTypeface);
+
+ return entities[0];
+ }
+
+ @Override
+ public String[]
+ listFamilies ()
+ {
+ return fontFamilyList;
+ }
+
+ @Override
+ public FontObject
+ openFont (FontEntity fontEntity, int pixelSize)
+ {
+ return new Sdk7FontObject (((Sdk7FontEntity) fontEntity).typeface,
+ pixelSize);
+ }
+
+ @Override
+ public int
+ hasChar (FontSpec font, char charCode)
+ {
+ float missingGlyphWidth, emGlyphWidth, width;
+ Rect rect1, rect2;
+ Paint paint;
+ Sdk7FontObject fontObject;
+
+ if (font instanceof Sdk7FontObject)
+ {
+ fontObject = (Sdk7FontObject) font;
+ paint = fontObject.typeface.typefacePaint;
+ }
+ else
+ paint = ((Sdk7FontEntity) font).typeface.typefacePaint;
+
+ paint.setTextSize (10);
+
+ if (Character.isWhitespace (charCode))
+ return 1;
+
+ missingGlyphWidth = paint.measureText (TOFU_STRING);
+ emGlyphWidth = paint.measureText (EM_STRING);
+ width = paint.measureText ("" + charCode);
+
+ if (width == 0f)
+ return 0;
+
+ if (width != missingGlyphWidth)
+ return 1;
+
+ rect1 = new Rect ();
+ rect2 = new Rect ();
+
+ paint.getTextBounds (TOFU_STRING, 0, TOFU_STRING.length (),
+ rect1);
+ paint.getTextBounds ("" + charCode, 0, 1, rect2);
+ return rect1.equals (rect2) ? 0 : 1;
+ }
+
+ private void
+ textExtents1 (Sdk7FontObject font, int code, FontMetrics metrics,
+ Paint paint, Rect bounds)
+ {
+ char[] text;
+
+ text = new char[1];
+ text[0] = (char) code;
+
+ paint.getTextBounds (text, 0, 1, bounds);
+
+ /* bounds is the bounding box of the glyph corresponding to CODE.
+ Translate these into XCharStruct values.
+
+ The origin is at 0, 0, and lbearing is the distance counting
+ rightwards from the origin to the left most pixel in the glyph
+ raster. rbearing is the distance between the origin and the
+ rightmost pixel in the glyph raster. ascent is the distance
+ counting upwards between the the topmost pixel in the glyph
+ raster. descent is the distance (once again counting
+ downwards) between the origin and the bottommost pixel in the
+ glyph raster.
+
+ width is the distance between the origin and the origin of any
+ character to the right. */
+
+ metrics.lbearing = (short) bounds.left;
+ metrics.rbearing = (short) bounds.right;
+ metrics.ascent = (short) -bounds.top;
+ metrics.descent = (short) bounds.bottom;
+ metrics.width = (short) paint.measureText ("" + text[0]);
+ }
+
+ @Override
+ public void
+ textExtents (FontObject font, int code[], FontMetrics fontMetrics)
+ {
+ int i;
+ Paint paintCache;
+ Rect boundsCache;
+ Sdk7FontObject fontObject;
+ char[] text;
+ float width;
+
+ fontObject = (Sdk7FontObject) font;
+ paintCache = fontObject.typeface.typefacePaint;
+ paintCache.setTextSize (fontObject.pixelSize);
+ boundsCache = new Rect ();
+
+ if (code.length == 0)
+ {
+ fontMetrics.lbearing = 0;
+ fontMetrics.rbearing = 0;
+ fontMetrics.ascent = 0;
+ fontMetrics.descent = 0;
+ fontMetrics.width = 0;
+ }
+ else if (code.length == 1)
+ textExtents1 ((Sdk7FontObject) font, code[0], fontMetrics,
+ paintCache, boundsCache);
+ else
+ {
+ text = new char[code.length];
+
+ for (i = 0; i < code.length; ++i)
+ text[i] = (char) code[i];
+
+ paintCache.getTextBounds (text, 0, code.length,
+ boundsCache);
+ width = paintCache.measureText (text, 0, code.length);
+
+ fontMetrics.lbearing = (short) boundsCache.left;
+ fontMetrics.rbearing = (short) boundsCache.right;
+ fontMetrics.ascent = (short) -boundsCache.top;
+ fontMetrics.descent = (short) boundsCache.bottom;
+ fontMetrics.width = (short) Math.round (width);
+ }
+ }
+
+ @Override
+ public int
+ encodeChar (FontObject fontObject, char charCode)
+ {
+ return charCode;
+ }
+
+ @Override
+ public int
+ draw (FontObject fontObject, EmacsGC gc, EmacsDrawable drawable,
+ int[] chars, int x, int y, int backgroundWidth,
+ boolean withBackground)
+ {
+ Rect backgroundRect, bounds;
+ Sdk7FontObject sdk7FontObject;
+ char[] charsArray;
+ int i;
+ Canvas canvas;
+ Paint paint;
+
+ sdk7FontObject = (Sdk7FontObject) fontObject;
+ charsArray = new char[chars.length];
+
+ for (i = 0; i < chars.length; ++i)
+ charsArray[i] = (char) chars[i];
+
+ backgroundRect = new Rect ();
+ backgroundRect.top = y - sdk7FontObject.ascent;
+ backgroundRect.left = x;
+ backgroundRect.right = x + backgroundWidth;
+ backgroundRect.bottom = y + sdk7FontObject.descent;
+
+ canvas = drawable.lockCanvas (gc);
+
+ if (canvas == null)
+ return 0;
+
+ paint = gc.gcPaint;
+ paint.setStyle (Paint.Style.FILL);
+
+ if (withBackground)
+ {
+ paint.setColor (gc.background | 0xff000000);
+ canvas.drawRect (backgroundRect, paint);
+ paint.setColor (gc.foreground | 0xff000000);
+ }
+
+ paint.setTextSize (sdk7FontObject.pixelSize);
+ paint.setTypeface (sdk7FontObject.typeface.typeface);
+ paint.setAntiAlias (true);
+ canvas.drawText (charsArray, 0, chars.length, x, y, paint);
+
+ bounds = new Rect ();
+ paint.getTextBounds (charsArray, 0, chars.length, bounds);
+ bounds.offset (x, y);
+ bounds.union (backgroundRect);
+ drawable.damageRect (bounds);
+ paint.setAntiAlias (false);
+ return 1;
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsSdk8Clipboard.java b/java/org/gnu/emacs/EmacsSdk8Clipboard.java
new file mode 100644
index 00000000000..9622641810f
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSdk8Clipboard.java
@@ -0,0 +1,147 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+/* Importing the entire package instead of just the legacy
+ ClipboardManager class avoids the deprecation warning. */
+
+import android.text.*;
+
+import android.content.Context;
+import android.util.Log;
+
+import java.io.UnsupportedEncodingException;
+
+/* This class implements EmacsClipboard for Android 2.2 and other
+ similarly old systems. */
+
+@SuppressWarnings ("deprecation")
+public final class EmacsSdk8Clipboard extends EmacsClipboard
+{
+ private static final String TAG = "EmacsSdk8Clipboard";
+ private ClipboardManager manager;
+
+ public
+ EmacsSdk8Clipboard ()
+ {
+ String what;
+ Context context;
+
+ what = Context.CLIPBOARD_SERVICE;
+ context = EmacsService.SERVICE;
+ manager
+ = (ClipboardManager) context.getSystemService (what);
+ }
+
+ /* Set the clipboard text to CLIPBOARD, a string in UTF-8
+ encoding. */
+
+ @Override
+ public void
+ setClipboard (byte[] bytes)
+ {
+ try
+ {
+ manager.setText (new String (bytes, "UTF-8"));
+ }
+ catch (UnsupportedEncodingException exception)
+ {
+ Log.w (TAG, "setClipboard: " + exception);
+ }
+ }
+
+ /* Return whether or not Emacs owns the clipboard. Value is 1 if
+ Emacs does, 0 if Emacs does not, and -1 if that information is
+ unavailable. */
+
+ @Override
+ public int
+ ownsClipboard ()
+ {
+ return -1;
+ }
+
+ /* Return whether or not clipboard content currently exists. */
+
+ @Override
+ public boolean
+ clipboardExists ()
+ {
+ return manager.hasText ();
+ }
+
+ /* Return the current content of the clipboard, as plain text, or
+ NULL if no content is available. */
+
+ @Override
+ public byte[]
+ getClipboard ()
+ {
+ String string;
+ CharSequence text;
+
+ text = manager.getText ();
+
+ if (text == null)
+ return null;
+
+ string = text.toString ();
+
+ try
+ {
+ return string.getBytes ("UTF-8");
+ }
+ catch (UnsupportedEncodingException exception)
+ {
+ Log.w (TAG, "getClipboard: " + exception);
+ }
+
+ return null;
+ }
+
+ /* Return an array of targets currently provided by the
+ clipboard, or NULL if there are none. */
+
+ @Override
+ public byte[][]
+ getClipboardTargets ()
+ {
+ return null;
+ }
+
+ /* Return the clipboard data for the given target, or NULL if it
+ does not exist.
+
+ Value is normally an array of three longs: the file descriptor,
+ the start offset of the data, and its length; length may be
+ AssetFileDescriptor.UNKOWN_LENGTH, meaning that the data extends
+ from that offset to the end of the file.
+
+ Do not use this function to open text targets; use `getClipboard'
+ for that instead, as it will handle selection data consisting
+ solely of a URI. */
+
+ @Override
+ public long[]
+ getClipboardData (byte[] target)
+ {
+ return null;
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsService.java b/java/org/gnu/emacs/EmacsService.java
new file mode 100644
index 00000000000..30ef71540a9
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsService.java
@@ -0,0 +1,914 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import java.util.List;
+
+import android.graphics.Matrix;
+import android.graphics.Point;
+
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.ExtractedText;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.NotificationChannel;
+import android.app.Service;
+
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.ApplicationInfoFlags;
+import android.content.pm.PackageManager;
+import android.content.res.AssetManager;
+
+import android.hardware.input.InputManager;
+
+import android.net.Uri;
+
+import android.os.BatteryManager;
+import android.os.Build;
+import android.os.Looper;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import android.os.Vibrator;
+import android.os.VibratorManager;
+import android.os.VibrationEffect;
+
+import android.util.Log;
+import android.util.DisplayMetrics;
+
+import android.widget.Toast;
+
+class Holder<T>
+{
+ T thing;
+};
+
+/* EmacsService is the service that starts the thread running Emacs
+ and handles requests by that Emacs instance. */
+
+public final class EmacsService extends Service
+{
+ public static final String TAG = "EmacsService";
+ public static volatile EmacsService SERVICE;
+ public static boolean needDashQ;
+
+ private EmacsThread thread;
+ private Handler handler;
+ private ContentResolver resolver;
+
+ /* Keep this in synch with androidgui.h. */
+ public static final int IC_MODE_NULL = 0;
+ public static final int IC_MODE_ACTION = 1;
+ public static final int IC_MODE_TEXT = 2;
+
+ /* Display metrics used by font backends. */
+ public DisplayMetrics metrics;
+
+ /* Flag that says whether or not to print verbose debugging
+ information. */
+ public static final boolean DEBUG_IC = false;
+
+ /* Return the directory leading to the directory in which native
+ library files are stored on behalf of CONTEXT. */
+
+ public static String
+ getLibraryDirectory (Context context)
+ {
+ int apiLevel;
+
+ apiLevel = Build.VERSION.SDK_INT;
+
+ if (apiLevel >= Build.VERSION_CODES.GINGERBREAD)
+ return context.getApplicationInfo ().nativeLibraryDir;
+
+ return context.getApplicationInfo ().dataDir + "/lib";
+ }
+
+ @Override
+ public int
+ onStartCommand (Intent intent, int flags, int startId)
+ {
+ Notification notification;
+ NotificationManager manager;
+ NotificationChannel channel;
+ String infoBlurb;
+ Object tem;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ {
+ tem = getSystemService (Context.NOTIFICATION_SERVICE);
+ manager = (NotificationManager) tem;
+ infoBlurb = ("This notification is displayed to keep Emacs"
+ + " running while it is in the background. You"
+ + " may disable if you want;"
+ + " see (emacs)Android Environment.");
+ channel
+ = new NotificationChannel ("emacs", "Emacs persistent notification",
+ NotificationManager.IMPORTANCE_DEFAULT);
+ manager.createNotificationChannel (channel);
+ notification = (new Notification.Builder (this, "emacs")
+ .setContentTitle ("Emacs")
+ .setContentText (infoBlurb)
+ .setSmallIcon (android.R.drawable.sym_def_app_icon)
+ .build ());
+ manager.notify (1, notification);
+ startForeground (1, notification);
+ }
+
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public IBinder
+ onBind (Intent intent)
+ {
+ return null;
+ }
+
+ @SuppressWarnings ("deprecation")
+ private String
+ getApkFile ()
+ {
+ PackageManager manager;
+ ApplicationInfo info;
+
+ manager = getPackageManager ();
+
+ try
+ {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU)
+ info = manager.getApplicationInfo ("org.gnu.emacs", 0);
+ else
+ info = manager.getApplicationInfo ("org.gnu.emacs",
+ ApplicationInfoFlags.of (0));
+
+ /* Return an empty string upon failure. */
+
+ if (info.sourceDir != null)
+ return info.sourceDir;
+
+ return "";
+ }
+ catch (Exception e)
+ {
+ return "";
+ }
+ }
+
+ @Override
+ public void
+ onCreate ()
+ {
+ final AssetManager manager;
+ Context app_context;
+ final String filesDir, libDir, cacheDir, classPath;
+ final double pixelDensityX;
+ final double pixelDensityY;
+
+ SERVICE = this;
+ handler = new Handler (Looper.getMainLooper ());
+ manager = getAssets ();
+ app_context = getApplicationContext ();
+ metrics = getResources ().getDisplayMetrics ();
+ pixelDensityX = metrics.xdpi;
+ pixelDensityY = metrics.ydpi;
+ resolver = getContentResolver ();
+
+ try
+ {
+ /* Configure Emacs with the asset manager and other necessary
+ parameters. */
+ filesDir = app_context.getFilesDir ().getCanonicalPath ();
+ libDir = getLibraryDirectory (this);
+ cacheDir = app_context.getCacheDir ().getCanonicalPath ();
+
+ /* Now provide this application's apk file, so a recursive
+ invocation of app_process (through android-emacs) can
+ find EmacsNoninteractive. */
+ classPath = getApkFile ();
+
+ Log.d (TAG, "Initializing Emacs, where filesDir = " + filesDir
+ + ", libDir = " + libDir + ", and classPath = " + classPath
+ + "; fileToOpen = " + EmacsOpenActivity.fileToOpen);
+
+ /* Start the thread that runs Emacs. */
+ thread = new EmacsThread (this, new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ EmacsNative.setEmacsParams (manager, filesDir, libDir,
+ cacheDir, (float) pixelDensityX,
+ (float) pixelDensityY,
+ classPath, EmacsService.this);
+ }
+ }, needDashQ,
+ /* If any file needs to be opened, open it now. */
+ EmacsOpenActivity.fileToOpen);
+ thread.start ();
+ }
+ catch (IOException exception)
+ {
+ EmacsNative.emacsAbort ();
+ return;
+ }
+ }
+
+
+
+ /* Functions from here on must only be called from the Emacs
+ thread. */
+
+ public void
+ runOnUiThread (Runnable runnable)
+ {
+ handler.post (runnable);
+ }
+
+ public EmacsView
+ getEmacsView (final EmacsWindow window, final int visibility,
+ final boolean isFocusedByDefault)
+ {
+ Runnable runnable;
+ final Holder<EmacsView> view;
+
+ view = new Holder<EmacsView> ();
+
+ runnable = new Runnable () {
+ public void
+ run ()
+ {
+ synchronized (this)
+ {
+ view.thing = new EmacsView (window);
+ view.thing.setVisibility (visibility);
+
+ /* 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 ();
+ }
+ }
+ };
+
+ syncRunnable (runnable);
+ return view.thing;
+ }
+
+ public void
+ getLocationOnScreen (final EmacsView view, final int[] coordinates)
+ {
+ Runnable runnable;
+
+ runnable = new Runnable () {
+ public void
+ run ()
+ {
+ synchronized (this)
+ {
+ view.getLocationOnScreen (coordinates);
+ notify ();
+ }
+ }
+ };
+
+ syncRunnable (runnable);
+ }
+
+ public void
+ fillRectangle (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y, int width, int height)
+ {
+ EmacsFillRectangle.perform (drawable, gc, x, y,
+ width, height);
+ }
+
+ public void
+ fillPolygon (EmacsDrawable drawable, EmacsGC gc,
+ Point points[])
+ {
+ EmacsFillPolygon.perform (drawable, gc, points);
+ }
+
+ public void
+ drawRectangle (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y, int width, int height)
+ {
+ EmacsDrawRectangle.perform (drawable, gc, x, y,
+ width, height);
+ }
+
+ public void
+ drawLine (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y, int x2, int y2)
+ {
+ EmacsDrawLine.perform (drawable, gc, x, y,
+ x2, y2);
+ }
+
+ public void
+ drawPoint (EmacsDrawable drawable, EmacsGC gc,
+ int x, int y)
+ {
+ EmacsDrawPoint.perform (drawable, gc, x, y);
+ }
+
+ public void
+ copyArea (EmacsDrawable srcDrawable, EmacsDrawable dstDrawable,
+ EmacsGC gc,
+ int srcX, int srcY, int width, int height, int destX,
+ int destY)
+ {
+ EmacsCopyArea.perform (srcDrawable, gc, dstDrawable,
+ srcX, srcY, width, height, destX,
+ destY);
+ }
+
+ public void
+ clearWindow (EmacsWindow window)
+ {
+ window.clearWindow ();
+ }
+
+ public void
+ clearArea (EmacsWindow window, int x, int y, int width,
+ int height)
+ {
+ window.clearArea (x, y, width, height);
+ }
+
+ @SuppressWarnings ("deprecation")
+ public void
+ ringBell ()
+ {
+ Vibrator vibrator;
+ VibrationEffect effect;
+ VibratorManager vibratorManager;
+ Object tem;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
+ {
+ tem = getSystemService (Context.VIBRATOR_MANAGER_SERVICE);
+ vibratorManager = (VibratorManager) tem;
+ vibrator = vibratorManager.getDefaultVibrator ();
+ }
+ else
+ vibrator
+ = (Vibrator) getSystemService (Context.VIBRATOR_SERVICE);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ {
+ effect
+ = VibrationEffect.createOneShot (50,
+ VibrationEffect.DEFAULT_AMPLITUDE);
+ vibrator.vibrate (effect);
+ }
+ else
+ vibrator.vibrate (50);
+ }
+
+ public short[]
+ queryTree (EmacsWindow window)
+ {
+ short[] array;
+ List<EmacsWindow> windowList;
+ int i;
+
+ if (window == null)
+ /* Just return all the windows without a parent. */
+ windowList = EmacsWindowAttachmentManager.MANAGER.copyWindows ();
+ else
+ windowList = window.children;
+
+ array = new short[windowList.size () + 1];
+ i = 1;
+
+ array[0] = (window == null
+ ? 0 : (window.parent != null
+ ? window.parent.handle : 0));
+
+ for (EmacsWindow treeWindow : windowList)
+ array[i++] = treeWindow.handle;
+
+ return array;
+ }
+
+ public int
+ getScreenWidth (boolean mmWise)
+ {
+ DisplayMetrics metrics;
+
+ metrics = getResources ().getDisplayMetrics ();
+
+ if (!mmWise)
+ return metrics.widthPixels;
+ else
+ return (int) ((metrics.widthPixels / metrics.xdpi) * 2540.0);
+ }
+
+ public int
+ getScreenHeight (boolean mmWise)
+ {
+ DisplayMetrics metrics;
+
+ metrics = getResources ().getDisplayMetrics ();
+
+ if (!mmWise)
+ return metrics.heightPixels;
+ else
+ return (int) ((metrics.heightPixels / metrics.ydpi) * 2540.0);
+ }
+
+ public boolean
+ detectMouse ()
+ {
+ InputManager manager;
+ InputDevice device;
+ int[] ids;
+ int i;
+
+ if (Build.VERSION.SDK_INT
+ < Build.VERSION_CODES.JELLY_BEAN)
+ return false;
+
+ manager = (InputManager) getSystemService (Context.INPUT_SERVICE);
+ ids = manager.getInputDeviceIds ();
+
+ for (i = 0; i < ids.length; ++i)
+ {
+ device = manager.getInputDevice (ids[i]);
+
+ if (device == null)
+ continue;
+
+ if (device.supportsSource (InputDevice.SOURCE_MOUSE))
+ return true;
+ }
+
+ return false;
+ }
+
+ public String
+ nameKeysym (int keysym)
+ {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1)
+ return KeyEvent.keyCodeToString (keysym);
+
+ return String.valueOf (keysym);
+ }
+
+
+
+ /* Start the Emacs service if necessary. On Android 26 and up,
+ start Emacs as a foreground service with a notification, to avoid
+ it being killed by the system.
+
+ On older systems, simply start it as a normal background
+ service. */
+
+ public static void
+ startEmacsService (Context context)
+ {
+ if (EmacsService.SERVICE == null)
+ {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ /* Start the Emacs service now. */
+ context.startService (new Intent (context,
+ EmacsService.class));
+ else
+ /* Display the permanant notification and start Emacs as a
+ foreground service. */
+ context.startForegroundService (new Intent (context,
+ EmacsService.class));
+ }
+ }
+
+ /* Ask the system to open the specified URL.
+ Value is NULL upon success, or a string describing the error
+ upon failure. */
+
+ public String
+ browseUrl (String url)
+ {
+ Intent intent;
+
+ try
+ {
+ intent = new Intent (Intent.ACTION_VIEW, Uri.parse (url));
+ intent.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity (intent);
+ }
+ catch (Exception e)
+ {
+ return e.toString ();
+ }
+
+ return null;
+ }
+
+ /* Get a SDK 11 ClipboardManager.
+
+ Android 4.0.x requires that this be called from the main
+ thread. */
+
+ public ClipboardManager
+ getClipboardManager ()
+ {
+ final Holder<ClipboardManager> manager;
+ Runnable runnable;
+
+ manager = new Holder<ClipboardManager> ();
+
+ runnable = new Runnable () {
+ public void
+ run ()
+ {
+ Object tem;
+
+ synchronized (this)
+ {
+ tem = getSystemService (Context.CLIPBOARD_SERVICE);
+ manager.thing = (ClipboardManager) tem;
+ notify ();
+ }
+ }
+ };
+
+ syncRunnable (runnable);
+ return manager.thing;
+ }
+
+ public void
+ restartEmacs ()
+ {
+ Intent intent;
+
+ intent = new Intent (this, EmacsActivity.class);
+ intent.addFlags (Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ startActivity (intent);
+ System.exit (0);
+ }
+
+ /* Wait synchronously for the specified RUNNABLE to complete in the
+ UI thread. Must be called from the Emacs thread. */
+
+ public static void
+ syncRunnable (Runnable runnable)
+ {
+ EmacsNative.beginSynchronous ();
+
+ synchronized (runnable)
+ {
+ SERVICE.runOnUiThread (runnable);
+
+ while (true)
+ {
+ try
+ {
+ runnable.wait ();
+ break;
+ }
+ catch (InterruptedException e)
+ {
+ continue;
+ }
+ }
+ }
+
+ EmacsNative.endSynchronous ();
+ }
+
+ public void
+ updateIC (EmacsWindow window, int newSelectionStart,
+ int newSelectionEnd, int composingRegionStart,
+ int composingRegionEnd)
+ {
+ if (DEBUG_IC)
+ Log.d (TAG, ("updateIC: " + window + " " + newSelectionStart
+ + " " + newSelectionEnd + " "
+ + composingRegionStart + " "
+ + composingRegionEnd));
+ window.view.imManager.updateSelection (window.view,
+ newSelectionStart,
+ newSelectionEnd,
+ composingRegionStart,
+ composingRegionEnd);
+ }
+
+ public void
+ resetIC (EmacsWindow window, int icMode)
+ {
+ if (DEBUG_IC)
+ Log.d (TAG, "resetIC: " + window);
+
+ window.view.setICMode (icMode);
+ window.view.imManager.restartInput (window.view);
+ }
+
+ public void
+ updateCursorAnchorInfo (EmacsWindow window, float x,
+ float y, float yBaseline,
+ float yBottom)
+ {
+ CursorAnchorInfo info;
+ CursorAnchorInfo.Builder builder;
+ Matrix matrix;
+ int[] offsets;
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+ return;
+
+ offsets = new int[2];
+ builder = new CursorAnchorInfo.Builder ();
+ matrix = new Matrix (window.view.getMatrix ());
+ window.view.getLocationOnScreen (offsets);
+ matrix.postTranslate (offsets[0], offsets[1]);
+ builder.setMatrix (matrix);
+ builder.setInsertionMarkerLocation (x, y, yBaseline, yBottom,
+ 0);
+ info = builder.build ();
+
+ if (DEBUG_IC)
+ Log.d (TAG, ("updateCursorAnchorInfo: " + x + " " + y
+ + " " + yBaseline + "-" + yBottom));
+
+ window.view.imManager.updateCursorAnchorInfo (window.view, info);
+ }
+
+ /* Open a content URI described by the bytes BYTES, a non-terminated
+ string; make it writable if WRITABLE, and readable if READABLE.
+ Truncate the file if TRUNCATE.
+
+ Value is the resulting file descriptor or -1 upon failure. */
+
+ public int
+ openContentUri (byte[] bytes, boolean writable, boolean readable,
+ boolean truncate)
+ {
+ String name, mode;
+ ParcelFileDescriptor fd;
+ int i;
+
+ /* Figure out the file access mode. */
+
+ mode = "";
+
+ if (readable)
+ mode += "r";
+
+ if (writable)
+ mode += "w";
+
+ if (truncate)
+ mode += "t";
+
+ /* Try to open an associated ParcelFileDescriptor. */
+
+ try
+ {
+ /* The usual file name encoding question rears its ugly head
+ again. */
+ name = new String (bytes, "UTF-8");
+ Log.d (TAG, "openContentUri: " + Uri.parse (name));
+
+ fd = resolver.openFileDescriptor (Uri.parse (name), mode);
+
+ /* Use detachFd on newer versions of Android or plain old
+ dup. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1)
+ {
+ i = fd.detachFd ();
+ fd.close ();
+
+ return i;
+ }
+ else
+ {
+ i = EmacsNative.dup (fd.getFd ());
+ fd.close ();
+
+ return i;
+ }
+ }
+ catch (Exception exception)
+ {
+ return -1;
+ }
+ }
+
+ public boolean
+ checkContentUri (byte[] string, boolean readable, boolean writable)
+ {
+ String mode, name;
+ ParcelFileDescriptor fd;
+
+ /* Decode this into a URI. */
+
+ try
+ {
+ /* The usual file name encoding question rears its ugly head
+ again. */
+ name = new String (string, "UTF-8");
+ Log.d (TAG, "checkContentUri: " + Uri.parse (name));
+ }
+ catch (UnsupportedEncodingException exception)
+ {
+ name = null;
+ throw new RuntimeException (exception);
+ }
+
+ mode = "r";
+
+ if (writable)
+ mode += "w";
+
+ Log.d (TAG, "checkContentUri: checking against mode " + mode);
+
+ try
+ {
+ fd = resolver.openFileDescriptor (Uri.parse (name), mode);
+ fd.close ();
+
+ Log.d (TAG, "checkContentUri: YES");
+
+ return true;
+ }
+ catch (Exception exception)
+ {
+ Log.d (TAG, "checkContentUri: NO");
+ Log.d (TAG, exception.toString ());
+ return false;
+ }
+ }
+
+ private long[]
+ queryBattery19 ()
+ {
+ IntentFilter filter;
+ Intent battery;
+ long capacity, chargeCounter, currentAvg, currentNow;
+ long status, remaining, plugged, temp;
+
+ filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
+ battery = registerReceiver (null, filter);
+
+ if (battery == null)
+ return null;
+
+ capacity = battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 0);
+ chargeCounter
+ = (battery.getIntExtra (BatteryManager.EXTRA_SCALE, 0)
+ / battery.getIntExtra (BatteryManager.EXTRA_LEVEL, 100) * 100);
+ currentAvg = 0;
+ currentNow = 0;
+ status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
+ remaining = -1;
+ plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
+ temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
+
+ return new long[] { capacity, chargeCounter, currentAvg,
+ currentNow, remaining, status, plugged,
+ temp, };
+ }
+
+ /* Return the status of the battery. See struct
+ android_battery_status for the order of the elements
+ returned.
+
+ Value may be null upon failure. */
+
+ public long[]
+ queryBattery ()
+ {
+ Object tem;
+ BatteryManager manager;
+ long capacity, chargeCounter, currentAvg, currentNow;
+ long status, remaining, plugged, temp;
+ int prop;
+ IntentFilter filter;
+ Intent battery;
+
+ /* Android 4.4 or earlier require applications to use a different
+ API to query the battery status. */
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
+ return queryBattery19 ();
+
+ tem = getSystemService (Context.BATTERY_SERVICE);
+ manager = (BatteryManager) tem;
+ remaining = -1;
+
+ prop = BatteryManager.BATTERY_PROPERTY_CAPACITY;
+ capacity = manager.getLongProperty (prop);
+ prop = BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER;
+ chargeCounter = manager.getLongProperty (prop);
+ prop = BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE;
+ currentAvg = manager.getLongProperty (prop);
+ prop = BatteryManager.BATTERY_PROPERTY_CURRENT_NOW;
+ currentNow = manager.getLongProperty (prop);
+
+ /* Return the battery status. N.B. that Android 7.1 and earlier
+ only return ``charging'' or ``discharging''. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ status
+ = manager.getIntProperty (BatteryManager.BATTERY_PROPERTY_STATUS);
+ else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ status = (manager.isCharging ()
+ ? BatteryManager.BATTERY_STATUS_CHARGING
+ : BatteryManager.BATTERY_STATUS_DISCHARGING);
+ else
+ status = (currentNow > 0
+ ? BatteryManager.BATTERY_STATUS_CHARGING
+ : BatteryManager.BATTERY_STATUS_DISCHARGING);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
+ remaining = manager.computeChargeTimeRemaining ();
+
+ plugged = -1;
+ temp = -1;
+
+ /* Now obtain additional information from the battery manager. */
+
+ filter = new IntentFilter (Intent.ACTION_BATTERY_CHANGED);
+ battery = registerReceiver (null, filter);
+
+ if (battery != null)
+ {
+ plugged = battery.getIntExtra (BatteryManager.EXTRA_PLUGGED, 0);
+ temp = battery.getIntExtra (BatteryManager.EXTRA_TEMPERATURE, 0);
+
+ /* Make status more reliable. */
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
+ status = battery.getIntExtra (BatteryManager.EXTRA_STATUS, 0);
+ }
+
+ return new long[] { capacity, chargeCounter, currentAvg,
+ currentNow, remaining, status, plugged,
+ temp, };
+ }
+
+ /* Display the specified STRING in a small dialog box on the main
+ thread. */
+
+ public void
+ displayToast (final String string)
+ {
+ runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ Toast toast;
+
+ toast = Toast.makeText (getApplicationContext (),
+ string, Toast.LENGTH_SHORT);
+ toast.show ();
+ }
+ });
+ }
+
+ public void
+ updateExtractedText (EmacsWindow window, ExtractedText text,
+ int token)
+ {
+ if (DEBUG_IC)
+ Log.d (TAG, "updateExtractedText: @" + token + ", " + text);
+
+ window.view.imManager.updateExtractedText (window.view,
+ token, text);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsSurfaceView.java b/java/org/gnu/emacs/EmacsSurfaceView.java
new file mode 100644
index 00000000000..e0411f7f8b3
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsSurfaceView.java
@@ -0,0 +1,141 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.view.View;
+
+import android.os.Build;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Paint;
+
+import java.lang.ref.WeakReference;
+
+/* This originally extended SurfaceView. However, doing so proved to
+ be too slow, and Android's surface view keeps up to three of its
+ own back buffers, which use too much memory (up to 96 MB for a
+ single frame.) */
+
+public final class EmacsSurfaceView extends View
+{
+ private static final String TAG = "EmacsSurfaceView";
+ private EmacsView view;
+ private Bitmap frontBuffer;
+ private Canvas bitmapCanvas;
+ private WeakReference<Bitmap> bitmap;
+ private Paint bitmapPaint;
+
+ public
+ EmacsSurfaceView (final EmacsView view)
+ {
+ super (view.getContext ());
+
+ this.view = view;
+ this.bitmapPaint = new Paint ();
+ this.bitmap = new WeakReference<Bitmap> (null);
+ }
+
+ private void
+ copyToFrontBuffer (Bitmap bitmap, Rect damageRect)
+ {
+ if (damageRect != null)
+ bitmapCanvas.drawBitmap (bitmap, damageRect, damageRect,
+ bitmapPaint);
+ else
+ bitmapCanvas.drawBitmap (bitmap, 0f, 0f, bitmapPaint);
+ }
+
+ private void
+ reconfigureFrontBuffer (Bitmap bitmap)
+ {
+ /* First, remove the old front buffer. */
+
+ if (frontBuffer != null)
+ {
+ frontBuffer.recycle ();
+ frontBuffer = null;
+ bitmapCanvas = null;
+ }
+
+ this.bitmap = new WeakReference<Bitmap> (bitmap);
+
+ /* Next, create the new front buffer if necessary. */
+
+ if (bitmap != null && frontBuffer == null)
+ {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
+ frontBuffer = Bitmap.createBitmap (bitmap.getWidth (),
+ bitmap.getHeight (),
+ Bitmap.Config.ARGB_8888,
+ false);
+ else
+ frontBuffer = Bitmap.createBitmap (bitmap.getWidth (),
+ bitmap.getHeight (),
+ Bitmap.Config.ARGB_8888);
+
+ bitmapCanvas = new Canvas (frontBuffer);
+
+ /* And copy over the bitmap contents. */
+ copyToFrontBuffer (bitmap, null);
+ }
+ else if (bitmap != null)
+ /* Just copy over the bitmap contents. */
+ copyToFrontBuffer (bitmap, null);
+ }
+
+ public synchronized void
+ setBitmap (Bitmap bitmap, Rect damageRect)
+ {
+ if (bitmap != this.bitmap.get ())
+ reconfigureFrontBuffer (bitmap);
+ else if (bitmap != null)
+ copyToFrontBuffer (bitmap, damageRect);
+
+ if (bitmap != null)
+ {
+ /* In newer versions of Android, the invalid rectangle is
+ supposedly internally calculated by the system. How that
+ is done is unknown, but calling `invalidateRect' is now
+ deprecated.
+
+ Fortunately, nobody has deprecated the version of
+ `postInvalidate' that accepts a dirty rectangle. */
+
+ if (damageRect != null)
+ postInvalidate (damageRect.left, damageRect.top,
+ damageRect.right, damageRect.bottom);
+ else
+ postInvalidate ();
+ }
+ }
+
+ @Override
+ public synchronized void
+ onDraw (Canvas canvas)
+ {
+ /* Paint the view's bitmap; the bitmap might be recycled right
+ now. */
+
+ if (frontBuffer != null)
+ canvas.drawBitmap (frontBuffer, 0f, 0f, bitmapPaint);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsThread.java b/java/org/gnu/emacs/EmacsThread.java
new file mode 100644
index 00000000000..d175fe332b5
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsThread.java
@@ -0,0 +1,81 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.lang.Thread;
+import java.util.Arrays;
+
+import android.os.Build;
+import android.util.Log;
+
+public class EmacsThread extends Thread
+{
+ private static final String TAG = "EmacsThread";
+
+ /* Whether or not Emacs should be started -Q. */
+ private boolean startDashQ;
+
+ /* Runnable run to initialize Emacs. */
+ private Runnable paramsClosure;
+
+ /* Whether or not to open a file after starting Emacs. */
+ private String fileToOpen;
+
+ public
+ EmacsThread (EmacsService service, Runnable paramsClosure,
+ boolean startDashQ, String fileToOpen)
+ {
+ super ("Emacs main thread");
+ this.startDashQ = startDashQ;
+ this.paramsClosure = paramsClosure;
+ this.fileToOpen = fileToOpen;
+ }
+
+ @Override
+ public void
+ run ()
+ {
+ String args[];
+
+ if (fileToOpen == null)
+ {
+ if (!startDashQ)
+ args = new String[] { "libandroid-emacs.so", };
+ else
+ args = new String[] { "libandroid-emacs.so", "-Q", };
+ }
+ else
+ {
+ if (!startDashQ)
+ args = new String[] { "libandroid-emacs.so",
+ fileToOpen, };
+ else
+ args = new String[] { "libandroid-emacs.so", "-Q",
+ fileToOpen, };
+ }
+
+ paramsClosure.run ();
+
+ /* Run the native code now. */
+ Log.d (TAG, "run: " + Arrays.toString (args));
+ EmacsNative.initEmacs (args, EmacsApplication.dumpFileName,
+ Build.VERSION.SDK_INT);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsView.java b/java/org/gnu/emacs/EmacsView.java
new file mode 100644
index 00000000000..10c1af9e19a
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsView.java
@@ -0,0 +1,708 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import android.content.Context;
+
+import android.text.InputType;
+
+import android.view.ContextMenu;
+import android.view.View;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Paint;
+
+import android.os.Build;
+import android.util.Log;
+
+/* This is an Android view which has a back and front buffer. When
+ swapBuffers is called, the back buffer is swapped to the front
+ buffer, and any damage is invalidated. frontBitmap and backBitmap
+ are modified and used both from the UI and the Emacs thread. As a
+ result, there is a lock held during all drawing operations.
+
+ It is also a ViewGroup, as it also lays out children. */
+
+public final class EmacsView extends ViewGroup
+ implements ViewTreeObserver.OnGlobalLayoutListener
+{
+ public static final String TAG = "EmacsView";
+
+ /* The associated EmacsWindow. */
+ public EmacsWindow window;
+
+ /* The buffer bitmap. */
+ public Bitmap bitmap;
+
+ /* The associated canvases. */
+ public Canvas canvas;
+
+ /* The damage region. */
+ public Region damageRegion;
+
+ /* The paint. */
+ public Paint paint;
+
+ /* The associated surface view. */
+ private EmacsSurfaceView surfaceView;
+
+ /* Whether or not a configure event must be sent for the next layout
+ event regardless of what changed. */
+ public boolean mustReportLayout;
+
+ /* Whether or not bitmaps must be recreated upon the next call to
+ getBitmap. */
+ private boolean bitmapDirty;
+
+ /* Whether or not a popup is active. */
+ private boolean popupActive;
+
+ /* The current context menu. */
+ private EmacsContextMenu contextMenu;
+
+ /* The last measured width and height. */
+ private int measuredWidth, measuredHeight;
+
+ /* The serial of the last clip rectangle change. */
+ private long lastClipSerial;
+
+ /* The InputMethodManager for this view's context. */
+ public InputMethodManager imManager;
+
+ /* Whether or not this view is attached to a window. */
+ public boolean isAttachedToWindow;
+
+ /* Whether or not this view should have the on screen keyboard
+ displayed whenever possible. */
+ public boolean isCurrentlyTextEditor;
+
+ /* The associated input connection. */
+ private EmacsInputConnection inputConnection;
+
+ /* The current IC mode. See `android_reset_ic' for more
+ details. */
+ private int icMode;
+
+ public
+ EmacsView (EmacsWindow window)
+ {
+ super (EmacsService.SERVICE);
+
+ Object tem;
+ Context context;
+
+ this.window = window;
+ this.damageRegion = new Region ();
+ this.paint = new Paint ();
+
+ setFocusable (true);
+ setFocusableInTouchMode (true);
+
+ /* Create the surface view. */
+ this.surfaceView = new EmacsSurfaceView (this);
+ addView (this.surfaceView);
+
+ /* Get rid of the default focus highlight. */
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
+ setDefaultFocusHighlightEnabled (false);
+
+ /* Obtain the input method manager. */
+ context = getContext ();
+ tem = context.getSystemService (Context.INPUT_METHOD_SERVICE);
+ imManager = (InputMethodManager) tem;
+
+ /* Add this view as its own global layout listener. */
+ getViewTreeObserver ().addOnGlobalLayoutListener (this);
+ }
+
+ private void
+ handleDirtyBitmap ()
+ {
+ Bitmap oldBitmap;
+
+ if (measuredWidth == 0 || measuredHeight == 0)
+ return;
+
+ if (!isAttachedToWindow)
+ return;
+
+ /* If bitmap is the same width and height as the measured width
+ and height, there is no need to do anything. Avoid allocating
+ the extra bitmap. */
+ if (bitmap != null
+ && (bitmap.getWidth () == measuredWidth
+ && bitmap.getHeight () == measuredHeight))
+ {
+ bitmapDirty = false;
+ return;
+ }
+
+ /* Save the old bitmap. */
+ oldBitmap = bitmap;
+
+ /* Recreate the front and back buffer bitmaps. */
+ bitmap
+ = Bitmap.createBitmap (measuredWidth,
+ measuredHeight,
+ Bitmap.Config.ARGB_8888);
+ bitmap.eraseColor (window.background | 0xff000000);
+
+ /* 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)
+ canvas.drawBitmap (oldBitmap, 0f, 0f, new Paint ());
+
+ bitmapDirty = false;
+
+ /* Explicitly free the old bitmap's memory. */
+
+ if (oldBitmap != null)
+ oldBitmap.recycle ();
+
+ /* Some Android versions still don't free the bitmap until the
+ next GC. */
+ Runtime.getRuntime ().gc ();
+ }
+
+ public synchronized void
+ explicitlyDirtyBitmap ()
+ {
+ bitmapDirty = true;
+ }
+
+ public synchronized Bitmap
+ getBitmap ()
+ {
+ if (bitmapDirty || bitmap == null)
+ handleDirtyBitmap ();
+
+ return bitmap;
+ }
+
+ public synchronized Canvas
+ 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;
+ }
+
+ public void
+ prepareForLayout (int wantedWidth, int wantedHeight)
+ {
+ measuredWidth = wantedWidth;
+ measuredHeight = wantedWidth;
+ }
+
+ @Override
+ protected void
+ onMeasure (int widthMeasureSpec, int heightMeasureSpec)
+ {
+ Rect measurements;
+ int width, height;
+
+ /* Return the width and height of the window regardless of what
+ the parent says. */
+ measurements = window.getGeometry ();
+
+ width = measurements.width ();
+ height = measurements.height ();
+
+ /* Now apply any extra requirements in widthMeasureSpec and
+ heightMeasureSpec. */
+
+ if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.EXACTLY)
+ width = MeasureSpec.getSize (widthMeasureSpec);
+ else if (MeasureSpec.getMode (widthMeasureSpec) == MeasureSpec.AT_MOST
+ && width > MeasureSpec.getSize (widthMeasureSpec))
+ width = MeasureSpec.getSize (widthMeasureSpec);
+
+ if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.EXACTLY)
+ height = MeasureSpec.getSize (heightMeasureSpec);
+ else if (MeasureSpec.getMode (heightMeasureSpec) == MeasureSpec.AT_MOST
+ && height > MeasureSpec.getSize (heightMeasureSpec))
+ height = MeasureSpec.getSize (heightMeasureSpec);
+
+ super.setMeasuredDimension (width, height);
+ }
+
+ /* Note that the monitor lock for the window must never be held from
+ within the lock for the view, because the window also locks the
+ other way around. */
+
+ @Override
+ protected void
+ onLayout (boolean changed, int left, int top, int right,
+ int bottom)
+ {
+ int count, i;
+ View child;
+ Rect windowRect;
+
+ count = getChildCount ();
+
+ measuredWidth = right - left;
+ measuredHeight = bottom - top;
+
+ /* Dirty the back buffer. */
+
+ if (changed)
+ explicitlyDirtyBitmap ();
+
+ for (i = 0; i < count; ++i)
+ {
+ child = getChildAt (i);
+
+ Log.d (TAG, "onLayout: " + child);
+
+ if (child == surfaceView)
+ child.layout (0, 0, right - left, bottom - top);
+ else if (child.getVisibility () != GONE)
+ {
+ if (!(child instanceof EmacsView))
+ continue;
+
+ /* What to do: lay out the view precisely according to its
+ window rect. */
+ windowRect = ((EmacsView) child).window.getGeometry ();
+ child.layout (windowRect.left, windowRect.top,
+ windowRect.right, windowRect.bottom);
+ }
+ }
+
+ /* Now report the layout change to the window. */
+
+ if (changed || mustReportLayout)
+ {
+ mustReportLayout = false;
+ window.viewLayout (left, top, right, bottom);
+ }
+ }
+
+ public void
+ damageRect (Rect damageRect)
+ {
+ synchronized (damageRegion)
+ {
+ damageRegion.union (damageRect);
+ }
+ }
+
+ /* This method is called from both the UI thread and the Emacs
+ thread. */
+
+ public void
+ swapBuffers ()
+ {
+ Canvas canvas;
+ Rect damageRect;
+ Bitmap bitmap;
+
+ damageRect = null;
+
+ synchronized (damageRegion)
+ {
+ if (damageRegion.isEmpty ())
+ return;
+
+ bitmap = getBitmap ();
+
+ /* Transfer the bitmap to the surface view, then invalidate
+ it. */
+ surfaceView.setBitmap (bitmap, damageRect);
+ }
+ }
+
+ @Override
+ public boolean
+ onKeyDown (int keyCode, KeyEvent event)
+ {
+ if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
+ || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
+ && !EmacsNative.shouldForwardMultimediaButtons ())
+ return false;
+
+ window.onKeyDown (keyCode, event);
+ return true;
+ }
+
+ @Override
+ public boolean
+ onKeyMultiple (int keyCode, int repeatCount, KeyEvent event)
+ {
+ if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
+ || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
+ && !EmacsNative.shouldForwardMultimediaButtons ())
+ return false;
+
+ window.onKeyDown (keyCode, event);
+ return true;
+ }
+
+ @Override
+ public boolean
+ onKeyUp (int keyCode, KeyEvent event)
+ {
+ if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP
+ || keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
+ || keyCode == KeyEvent.KEYCODE_VOLUME_MUTE)
+ && !EmacsNative.shouldForwardMultimediaButtons ())
+ return false;
+
+ window.onKeyUp (keyCode, event);
+ return true;
+ }
+
+ @Override
+ public void
+ onFocusChanged (boolean gainFocus, int direction,
+ Rect previouslyFocusedRect)
+ {
+ window.onFocusChanged (gainFocus);
+ super.onFocusChanged (gainFocus, direction,
+ previouslyFocusedRect);
+ }
+
+ @Override
+ public boolean
+ onGenericMotionEvent (MotionEvent motion)
+ {
+ return window.onSomeKindOfMotionEvent (motion);
+ }
+
+ @Override
+ public boolean
+ onTouchEvent (MotionEvent motion)
+ {
+ return window.onTouchEvent (motion);
+ }
+
+ private void
+ moveChildToBack (View child)
+ {
+ int index;
+
+ index = indexOfChild (child);
+
+ if (index > 0)
+ {
+ detachViewFromParent (index);
+
+ /* The view at 0 is the surface view. */
+ attachViewToParent (child, 1,
+ child.getLayoutParams());
+ }
+ }
+
+ /* The following two functions must not be called if the view has no
+ parent, or is parented to an activity. */
+
+ public void
+ raise ()
+ {
+ EmacsView parent;
+
+ parent = (EmacsView) getParent ();
+
+ Log.d (TAG, "raise: parent " + parent);
+
+ if (parent.indexOfChild (this)
+ == parent.getChildCount () - 1)
+ return;
+
+ parent.bringChildToFront (this);
+ }
+
+ public void
+ lower ()
+ {
+ EmacsView parent;
+
+ parent = (EmacsView) getParent ();
+
+ Log.d (TAG, "lower: parent " + parent);
+
+ if (parent.indexOfChild (this) == 1)
+ return;
+
+ parent.moveChildToBack (this);
+ }
+
+ @Override
+ protected void
+ onCreateContextMenu (ContextMenu menu)
+ {
+ if (contextMenu == null)
+ return;
+
+ contextMenu.expandTo (menu, this);
+ }
+
+ public boolean
+ popupMenu (EmacsContextMenu menu, int xPosition,
+ int yPosition, boolean force)
+ {
+ if (popupActive && !force)
+ return false;
+
+ contextMenu = menu;
+ popupActive = true;
+
+ Log.d (TAG, "popupMenu: " + menu + " @" + xPosition
+ + ", " + yPosition + " " + force);
+
+ /* Use showContextMenu (float, float) on N to get actual popup
+ behavior. */
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ return showContextMenu ((float) xPosition, (float) yPosition);
+ else
+ return showContextMenu ();
+ }
+
+ public void
+ cancelPopupMenu ()
+ {
+ if (!popupActive)
+ throw new IllegalStateException ("cancelPopupMenu called without"
+ + " popupActive set");
+
+ contextMenu = null;
+ popupActive = false;
+
+ /* It is not possible to know with 100% certainty which activity
+ is currently displaying the context menu. Loop through each
+ activity and call `closeContextMenu' instead. */
+
+ for (EmacsWindowAttachmentManager.WindowConsumer consumer
+ : EmacsWindowAttachmentManager.MANAGER.consumers)
+ {
+ if (consumer instanceof EmacsActivity)
+ ((EmacsActivity) consumer).closeContextMenu ();
+ }
+ }
+
+ @Override
+ public synchronized void
+ onDetachedFromWindow ()
+ {
+ isAttachedToWindow = false;
+
+ synchronized (this)
+ {
+ /* Recycle the bitmap and call GC. */
+
+ if (bitmap != null)
+ bitmap.recycle ();
+
+ bitmap = null;
+ canvas = null;
+ surfaceView.setBitmap (null, null);
+
+ /* Collect the bitmap storage; it could be large. */
+ Runtime.getRuntime ().gc ();
+ }
+
+ super.onDetachedFromWindow ();
+ }
+
+ @Override
+ public synchronized void
+ onAttachedToWindow ()
+ {
+ isAttachedToWindow = true;
+
+ /* Dirty the bitmap, as it was destroyed when onDetachedFromWindow
+ was called. */
+ bitmapDirty = true;
+
+ /* Now expose the view contents again. */
+ EmacsNative.sendExpose (this.window.handle, 0, 0,
+ measuredWidth, measuredHeight);
+
+ super.onAttachedToWindow ();
+ }
+
+ public void
+ showOnScreenKeyboard ()
+ {
+ /* Specifying no flags at all tells the system the user asked for
+ the input method to be displayed. */
+ imManager.showSoftInput (this, 0);
+ isCurrentlyTextEditor = true;
+ }
+
+ public void
+ hideOnScreenKeyboard ()
+ {
+ imManager.hideSoftInputFromWindow (this.getWindowToken (),
+ 0);
+ isCurrentlyTextEditor = false;
+ }
+
+ @Override
+ public InputConnection
+ onCreateInputConnection (EditorInfo info)
+ {
+ int mode;
+ int[] selection;
+
+ /* Figure out what kind of IME behavior Emacs wants. */
+ mode = getICMode ();
+
+ /* Make sure the input method never displays a full screen input
+ box that obscures Emacs. */
+ info.imeOptions = EditorInfo.IME_FLAG_NO_FULLSCREEN;
+ info.imeOptions |= EditorInfo.IME_FLAG_NO_EXTRACT_UI;
+
+ /* Set a reasonable inputType. */
+ info.inputType = InputType.TYPE_CLASS_TEXT;
+
+ /* If this fails or ANDROID_IC_MODE_NULL was requested, then don't
+ initialize the input connection. */
+
+ if (mode == EmacsService.IC_MODE_NULL)
+ {
+ info.inputType = InputType.TYPE_NULL;
+ return null;
+ }
+
+ /* Obtain the current position of point and set it as the
+ selection. */
+ selection = EmacsNative.getSelection (window.handle);
+
+ if (selection != null)
+ Log.d (TAG, "onCreateInputConnection: current selection is: "
+ + selection[0] + ", by " + selection[1]);
+ else
+ {
+ Log.d (TAG, "onCreateInputConnection: current selection could"
+ + " not be retrieved.");
+
+ /* If the selection could not be obtained, return 0 by 0.
+ However, ask for the selection position to be updated as
+ soon as possible. */
+
+ selection = new int[] { 0, 0, };
+ EmacsNative.requestSelectionUpdate (window.handle);
+ }
+
+ if (mode == EmacsService.IC_MODE_ACTION)
+ info.imeOptions |= EditorInfo.IME_ACTION_DONE;
+
+ /* Set the initial selection fields. */
+ info.initialSelStart = selection[0];
+ info.initialSelEnd = selection[1];
+
+ /* Create the input connection if necessary. */
+
+ if (inputConnection == null)
+ inputConnection = new EmacsInputConnection (this);
+
+ /* Return the input connection. */
+ return inputConnection;
+ }
+
+ @Override
+ public boolean
+ onCheckIsTextEditor ()
+ {
+ /* If value is true, then the system will display the on screen
+ keyboard. */
+ return isCurrentlyTextEditor;
+ }
+
+ @Override
+ public boolean
+ isOpaque ()
+ {
+ /* Returning true here allows the system to not draw the contents
+ of windows underneath this view, thereby improving
+ performance. */
+ return true;
+ }
+
+ public synchronized void
+ setICMode (int icMode)
+ {
+ this.icMode = icMode;
+ }
+
+ public synchronized int
+ getICMode ()
+ {
+ return icMode;
+ }
+
+ @Override
+ public void
+ onGlobalLayout ()
+ {
+ int[] locations;
+
+ /* Get the absolute offset of this view and specify its left and
+ top position in subsequent ConfigureNotify events. */
+
+ locations = new int[2];
+ getLocationInWindow (locations);
+ window.notifyContentRectPosition (locations[0],
+ locations[1]);
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java
new file mode 100644
index 00000000000..c14bf16b96e
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsWindow.java
@@ -0,0 +1,1300 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.lang.IllegalStateException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import android.content.Context;
+
+import android.graphics.Rect;
+import android.graphics.Canvas;
+import android.graphics.Bitmap;
+import android.graphics.PixelFormat;
+
+import android.view.View;
+import android.view.ViewManager;
+import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.InputDevice;
+import android.view.WindowManager;
+
+import android.util.Log;
+
+import android.os.Build;
+
+/* This defines a window, which is a handle. Windows represent a
+ rectangular subset of the screen with their own contents.
+
+ Windows either have a parent window, in which case their views are
+ attached to the parent's view, or are "floating", in which case
+ their views are attached to the parent activity (if any), else
+ nothing.
+
+ Views are also drawables, meaning they can accept drawing
+ requests. */
+
+public final class EmacsWindow extends EmacsHandleObject
+ implements EmacsDrawable
+{
+ private static final String TAG = "EmacsWindow";
+
+ private static class Coordinate
+ {
+ /* Integral coordinate. */
+ int x, y;
+
+ public
+ Coordinate (int x, int y)
+ {
+ this.x = x;
+ this.y = y;
+ }
+ };
+
+ /* The view associated with the window. */
+ public EmacsView view;
+
+ /* The geometry of the window. */
+ private Rect rect;
+
+ /* The parent window, or null if it is the root window. */
+ public EmacsWindow parent;
+
+ /* List of all children in stacking order. This must be kept
+ consistent with their Z order! */
+ public ArrayList<EmacsWindow> children;
+
+ /* Map between pointer identifiers and last known position. Used to
+ compute which pointer changed upon a touch event. */
+ private HashMap<Integer, Coordinate> pointerMap;
+
+ /* The window consumer currently attached, if it exists. */
+ private EmacsWindowAttachmentManager.WindowConsumer attached;
+
+ /* The window background scratch GC. foreground is always the
+ window background. */
+ private EmacsGC scratchGC;
+
+ /* The button state and keyboard modifier mask at the time of the
+ last button press or release event. */
+ public int lastButtonState, lastModifiers;
+
+ /* Whether or not the window is mapped. */
+ private boolean isMapped;
+
+ /* Whether or not to ask for focus upon being mapped, and whether or
+ not the window should be focusable. */
+ private boolean dontFocusOnMap, dontAcceptFocus;
+
+ /* Whether or not the window is override-redirect. An
+ override-redirect window always has its own system window. */
+ private boolean overrideRedirect;
+
+ /* The window manager that is the parent of this window. NULL if
+ there is no such window manager. */
+ private WindowManager windowManager;
+
+ /* The time of the last KEYCODE_VOLUME_DOWN release. This is used
+ to quit Emacs upon two rapid clicks of the volume down
+ button. */
+ private long lastVolumeButtonRelease;
+
+ /* Linked list of character strings which were recently sent as
+ events. */
+ public LinkedHashMap<Integer, String> eventStrings;
+
+ /* Whether or not this window is fullscreen. */
+ public boolean fullscreen;
+
+ /* The window background pixel. This is used by EmacsView when
+ creating new bitmaps. */
+ public volatile int background;
+
+ /* The position of this window relative to the root window. */
+ public int xPosition, yPosition;
+
+ public
+ EmacsWindow (short handle, final EmacsWindow parent, int x, int y,
+ int width, int height, boolean overrideRedirect)
+ {
+ super (handle);
+
+ rect = new Rect (x, y, x + width, y + height);
+ pointerMap = new HashMap<Integer, Coordinate> ();
+
+ /* Create the view from the context's UI thread. The window is
+ unmapped, so the view is GONE. */
+ view = EmacsService.SERVICE.getEmacsView (this, View.GONE,
+ parent == null);
+ this.parent = parent;
+ this.overrideRedirect = overrideRedirect;
+
+ /* Create the list of children. */
+ children = new ArrayList<EmacsWindow> ();
+
+ if (parent != null)
+ {
+ parent.children.add (this);
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ parent.view.addView (view);
+ }
+ });
+ }
+
+ scratchGC = new EmacsGC ((short) 0);
+
+ /* Create the map of input method-committed strings. Keep at most
+ ten strings in the map. */
+
+ eventStrings
+ = new LinkedHashMap<Integer, String> () {
+ @Override
+ protected boolean
+ removeEldestEntry (Map.Entry<Integer, String> entry)
+ {
+ return size () > 10;
+ }
+ };
+ }
+
+ public void
+ changeWindowBackground (int pixel)
+ {
+ /* scratchGC is used as the argument to a FillRectangles req. */
+ scratchGC.foreground = pixel;
+ scratchGC.markDirty (false);
+
+ /* Make the background known to the view as well. */
+ background = pixel;
+ }
+
+ public synchronized Rect
+ getGeometry ()
+ {
+ return new Rect (rect);
+ }
+
+ @Override
+ public synchronized void
+ destroyHandle () throws IllegalStateException
+ {
+ if (parent != null)
+ parent.children.remove (this);
+
+ EmacsActivity.invalidateFocus ();
+
+ if (!children.isEmpty ())
+ throw new IllegalStateException ("Trying to destroy window with "
+ + "children!");
+
+ /* Remove the view from its parent and make it invisible. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ public void
+ run ()
+ {
+ ViewManager parent;
+ EmacsWindowAttachmentManager manager;
+
+ if (EmacsActivity.focusedWindow == EmacsWindow.this)
+ EmacsActivity.focusedWindow = null;
+
+ manager = EmacsWindowAttachmentManager.MANAGER;
+ view.setVisibility (View.GONE);
+
+ /* If the window manager is set, use that instead. */
+ if (windowManager != null)
+ parent = windowManager;
+ else
+ parent = (ViewManager) view.getParent ();
+ windowManager = null;
+
+ if (parent != null)
+ parent.removeView (view);
+
+ manager.detachWindow (EmacsWindow.this);
+ }
+ });
+
+ super.destroyHandle ();
+ }
+
+ public void
+ setConsumer (EmacsWindowAttachmentManager.WindowConsumer consumer)
+ {
+ attached = consumer;
+ }
+
+ public EmacsWindowAttachmentManager.WindowConsumer
+ getAttachedConsumer ()
+ {
+ return attached;
+ }
+
+ public synchronized long
+ viewLayout (int left, int top, int right, int bottom)
+ {
+ int rectWidth, rectHeight;
+
+ rect.left = left;
+ rect.top = top;
+ rect.right = right;
+ rect.bottom = bottom;
+
+ rectWidth = right - left;
+ rectHeight = bottom - top;
+
+ /* If parent is null, use xPosition and yPosition instead of the
+ geometry rectangle positions. */
+
+ if (parent == null)
+ {
+ left = xPosition;
+ top = yPosition;
+ }
+
+ return EmacsNative.sendConfigureNotify (this.handle,
+ System.currentTimeMillis (),
+ left, top, rectWidth,
+ rectHeight);
+ }
+
+ public void
+ requestViewLayout ()
+ {
+ view.explicitlyDirtyBitmap ();
+
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ if (overrideRedirect)
+ /* Set the layout parameters again. */
+ view.setLayoutParams (getWindowLayoutParams ());
+
+ view.mustReportLayout = true;
+ view.requestLayout ();
+ }
+ });
+ }
+
+ public synchronized void
+ resizeWindow (int width, int height)
+ {
+ rect.right = rect.left + width;
+ rect.bottom = rect.top + height;
+
+ requestViewLayout ();
+ }
+
+ public synchronized void
+ moveWindow (int x, int y)
+ {
+ int width, height;
+
+ width = rect.width ();
+ height = rect.height ();
+
+ rect.left = x;
+ rect.top = y;
+ rect.right = x + width;
+ rect.bottom = y + height;
+
+ requestViewLayout ();
+ }
+
+ private WindowManager.LayoutParams
+ getWindowLayoutParams ()
+ {
+ WindowManager.LayoutParams params;
+ int flags, type;
+ Rect rect;
+
+ flags = 0;
+ rect = getGeometry ();
+ flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+ flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+ type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+
+ params
+ = new WindowManager.LayoutParams (rect.width (), rect.height (),
+ rect.left, rect.top,
+ type, flags,
+ PixelFormat.RGBA_8888);
+ params.gravity = Gravity.TOP | Gravity.LEFT;
+ return params;
+ }
+
+ private Context
+ findSuitableActivityContext ()
+ {
+ /* Find a recently focused activity. */
+ if (!EmacsActivity.focusedActivities.isEmpty ())
+ return EmacsActivity.focusedActivities.get (0);
+
+ /* Return the service context, which probably won't work. */
+ return EmacsService.SERVICE;
+ }
+
+ public synchronized void
+ mapWindow ()
+ {
+ final int width, height;
+
+ if (isMapped)
+ return;
+
+ isMapped = true;
+ width = rect.width ();
+ height = rect.height ();
+
+ if (parent == null)
+ {
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ EmacsWindowAttachmentManager manager;
+ WindowManager windowManager;
+ Context ctx;
+ Object tem;
+ WindowManager.LayoutParams params;
+
+ /* Make the view visible, first of all. */
+ view.setVisibility (View.VISIBLE);
+
+ if (!overrideRedirect)
+ {
+ manager = EmacsWindowAttachmentManager.MANAGER;
+
+ /* If parent is the root window, notice that there are new
+ children available for interested activites to pick
+ up. */
+ manager.registerWindow (EmacsWindow.this);
+
+ if (!getDontFocusOnMap ())
+ /* Eventually this should check no-focus-on-map. */
+ view.requestFocus ();
+ }
+ else
+ {
+ /* But if the window is an override-redirect window,
+ then:
+
+ - Find an activity that is currently active.
+
+ - Map the window as a panel on top of that
+ activity using the system window manager. */
+
+ ctx = findSuitableActivityContext ();
+ tem = ctx.getSystemService (Context.WINDOW_SERVICE);
+ windowManager = (WindowManager) tem;
+
+ /* Calculate layout parameters. */
+ params = getWindowLayoutParams ();
+ view.setLayoutParams (params);
+
+ /* Attach the view. */
+ try
+ {
+ view.prepareForLayout (width, height);
+ windowManager.addView (view, params);
+
+ /* Record the window manager being used in the
+ EmacsWindow object. */
+ EmacsWindow.this.windowManager = windowManager;
+ }
+ catch (Exception e)
+ {
+ Log.w (TAG,
+ "failed to attach override-redirect window, " + e);
+ }
+ }
+ }
+ });
+ }
+ else
+ {
+ /* Do the same thing as above, but don't register this
+ window. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ /* Prior to mapping the view, set its measuredWidth and
+ measuredHeight to some reasonable value, in order to
+ avoid excessive bitmap dirtying. */
+
+ view.prepareForLayout (width, height);
+ view.setVisibility (View.VISIBLE);
+
+ if (!getDontFocusOnMap ())
+ view.requestFocus ();
+ }
+ });
+ }
+ }
+
+ public void
+ unmapWindow ()
+ {
+ if (!isMapped)
+ return;
+
+ isMapped = false;
+
+ view.post (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ EmacsWindowAttachmentManager manager;
+
+ manager = EmacsWindowAttachmentManager.MANAGER;
+
+ view.setVisibility (View.GONE);
+
+ /* Detach the view from the window manager if possible. */
+ if (windowManager != null)
+ windowManager.removeView (view);
+ windowManager = null;
+
+ /* Now that the window is unmapped, unregister it as
+ well. */
+ manager.detachWindow (EmacsWindow.this);
+ }
+ });
+ }
+
+ @Override
+ public Canvas
+ lockCanvas (EmacsGC gc)
+ {
+ return view.getCanvas (gc);
+ }
+
+ @Override
+ public void
+ damageRect (Rect damageRect)
+ {
+ view.damageRect (damageRect);
+ }
+
+ public void
+ swapBuffers ()
+ {
+ view.swapBuffers ();
+ }
+
+ public void
+ clearWindow ()
+ {
+ EmacsService.SERVICE.fillRectangle (this, scratchGC,
+ 0, 0, rect.width (),
+ rect.height ());
+ }
+
+ public void
+ clearArea (int x, int y, int width, int height)
+ {
+ EmacsService.SERVICE.fillRectangle (this, scratchGC,
+ x, y, width, height);
+ }
+
+ @Override
+ public Bitmap
+ getBitmap ()
+ {
+ return view.getBitmap ();
+ }
+
+ /* event.getCharacters is used because older input methods still
+ require it. */
+ @SuppressWarnings ("deprecation")
+ public int
+ getEventUnicodeChar (KeyEvent event, int state)
+ {
+ String characters;
+
+ if (event.getUnicodeChar (state) != 0)
+ return event.getUnicodeChar (state);
+
+ characters = event.getCharacters ();
+
+ if (characters != null && characters.length () == 1)
+ return characters.charAt (0);
+
+ return characters == null ? 0 : -1;
+ }
+
+ public void
+ saveUnicodeString (int serial, String string)
+ {
+ eventStrings.put (serial, string);
+ }
+
+ /* event.getCharacters is used because older input methods still
+ require it. */
+ @SuppressWarnings ("deprecation")
+ public void
+ onKeyDown (int keyCode, KeyEvent event)
+ {
+ int state, state_1;
+ long serial;
+ String characters;
+
+ 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);
+
+ synchronized (eventStrings)
+ {
+ serial
+ = EmacsNative.sendKeyPress (this.handle,
+ event.getEventTime (),
+ state, keyCode,
+ getEventUnicodeChar (event,
+ state_1));
+ lastModifiers = state;
+
+ characters = event.getCharacters ();
+
+ if (characters != null && characters.length () > 1)
+ saveUnicodeString ((int) serial, characters);
+ }
+ }
+
+ public void
+ onKeyUp (int keyCode, KeyEvent event)
+ {
+ int state, state_1;
+ long time, serial;
+
+ 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);
+
+ serial
+ = EmacsNative.sendKeyRelease (this.handle,
+ event.getEventTime (),
+ state, keyCode,
+ getEventUnicodeChar (event,
+ state_1));
+ lastModifiers = state;
+
+ if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)
+ {
+ /* Check if this volume down press should quit Emacs.
+ Most Android devices have no physical keyboard, so it
+ is unreasonably hard to press C-g. */
+
+ time = event.getEventTime ();
+
+ if (time - lastVolumeButtonRelease < 350)
+ EmacsNative.quit ();
+
+ lastVolumeButtonRelease = time;
+ }
+ }
+
+ public void
+ onFocusChanged (boolean gainFocus)
+ {
+ EmacsActivity.invalidateFocus ();
+ }
+
+ /* Notice that the activity has been detached or destroyed.
+
+ ISFINISHING is set if the activity is not the main activity, or
+ if the activity was not destroyed in response to explicit user
+ action. */
+
+ public void
+ onActivityDetached (boolean isFinishing)
+ {
+ /* Destroy the associated frame when the activity is detached in
+ response to explicit user action. */
+
+ if (isFinishing)
+ EmacsNative.sendWindowAction (this.handle, 0);
+ }
+
+ /* Look through the button state to determine what button EVENT was
+ generated from. DOWN is true if EVENT is a button press event,
+ false otherwise. Value is the X number of the button. */
+
+ private int
+ whatButtonWasIt (MotionEvent event, boolean down)
+ {
+ int eventState, notIn;
+
+ if (Build.VERSION.SDK_INT
+ < Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ /* Earlier versions of Android only support one mouse
+ button. */
+ return 1;
+
+ eventState = event.getButtonState ();
+ notIn = (down ? eventState & ~lastButtonState
+ : lastButtonState & ~eventState);
+
+ if ((notIn & MotionEvent.BUTTON_PRIMARY) != 0)
+ return 1;
+
+ if ((notIn & MotionEvent.BUTTON_SECONDARY) != 0)
+ return 3;
+
+ if ((notIn & MotionEvent.BUTTON_TERTIARY) != 0)
+ return 2;
+
+ /* Not a real value. */
+ return 4;
+ }
+
+ /* Return the ID of the pointer which changed in EVENT. Value is -1
+ if it could not be determined, else the pointer that changed, or
+ -2 if -1 would have been returned, but there is also a pointer
+ that is a mouse. */
+
+ private int
+ figureChange (MotionEvent event)
+ {
+ int pointerID, i, truncatedX, truncatedY, pointerIndex;
+ Coordinate coordinate;
+ boolean mouseFlag;
+
+ /* pointerID is always initialized but the Java compiler is too
+ dumb to know that. */
+ pointerID = -1;
+ mouseFlag = false;
+
+ switch (event.getActionMasked ())
+ {
+ case MotionEvent.ACTION_DOWN:
+ /* Primary pointer pressed with index 0. */
+
+ /* Detect mice. If this is a mouse event, give it to
+ onSomeKindOfMotionEvent. */
+ if ((Build.VERSION.SDK_INT
+ >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ && event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE)
+ return -2;
+
+ pointerID = event.getPointerId (0);
+ pointerMap.put (pointerID,
+ new Coordinate ((int) event.getX (0),
+ (int) event.getY (0)));
+ break;
+
+ case MotionEvent.ACTION_UP:
+
+ /* Detect mice. If this is a mouse event, give it to
+ onSomeKindOfMotionEvent. */
+ if ((Build.VERSION.SDK_INT
+ >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ && event.getToolType (0) == MotionEvent.TOOL_TYPE_MOUSE)
+ return -2;
+
+ /* Primary pointer released with index 0. */
+ pointerID = event.getPointerId (0);
+ pointerMap.remove (pointerID);
+ break;
+
+ case MotionEvent.ACTION_POINTER_DOWN:
+ /* New pointer. Find the pointer ID from the index and place
+ it in the map. */
+ pointerIndex = event.getActionIndex ();
+ pointerID = event.getPointerId (pointerIndex);
+ pointerMap.put (pointerID,
+ new Coordinate ((int) event.getX (pointerIndex),
+ (int) event.getY (pointerIndex)));
+ break;
+
+ case MotionEvent.ACTION_POINTER_UP:
+ /* Pointer removed. Remove it from the map. */
+ pointerIndex = event.getActionIndex ();
+ pointerID = event.getPointerId (pointerIndex);
+ pointerMap.remove (pointerID);
+ break;
+
+ default:
+
+ /* Loop through each pointer in the event. */
+ for (i = 0; i < event.getPointerCount (); ++i)
+ {
+ pointerID = event.getPointerId (i);
+
+ /* Look up that pointer in the map. */
+ coordinate = pointerMap.get (pointerID);
+
+ if (coordinate != null)
+ {
+ /* See if coordinates have changed. */
+ truncatedX = (int) event.getX (i);
+ truncatedY = (int) event.getY (i);
+
+ if (truncatedX != coordinate.x
+ || truncatedY != coordinate.y)
+ {
+ /* The pointer changed. Update the coordinate and
+ break out of the loop. */
+ coordinate.x = truncatedX;
+ coordinate.y = truncatedY;
+
+ break;
+ }
+ }
+
+ /* See if this is a mouse. If so, set the mouseFlag. */
+ if ((Build.VERSION.SDK_INT
+ >= Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ && event.getToolType (i) == MotionEvent.TOOL_TYPE_MOUSE)
+ mouseFlag = true;
+ }
+
+ /* Set the pointer ID to -1 if the loop failed to find any
+ changed pointer. If a mouse pointer was found, set it to
+ -2. */
+ if (i == event.getPointerCount ())
+ pointerID = (mouseFlag ? -2 : -1);
+ }
+
+ /* Return the pointer ID. */
+ return pointerID;
+ }
+
+ public boolean
+ onTouchEvent (MotionEvent event)
+ {
+ int pointerID, index;
+
+ /* Extract the ``touch ID'' (or in Android, the ``pointer
+ ID''.) */
+ pointerID = figureChange (event);
+
+ if (pointerID < 0)
+ {
+ /* If this is a mouse event, give it to
+ onSomeKindOfMotionEvent. */
+ if (pointerID == -2)
+ return onSomeKindOfMotionEvent (event);
+
+ return false;
+ }
+
+ /* Find the pointer index corresponding to the event. */
+ index = event.findPointerIndex (pointerID);
+
+ switch (event.getActionMasked ())
+ {
+ case MotionEvent.ACTION_DOWN:
+ case MotionEvent.ACTION_POINTER_DOWN:
+ /* Touch down event. */
+ EmacsNative.sendTouchDown (this.handle, (int) event.getX (index),
+ (int) event.getY (index),
+ event.getEventTime (), pointerID);
+ return true;
+
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_POINTER_UP:
+ case MotionEvent.ACTION_CANCEL:
+ /* Touch up event. Android documentation says ACTION_CANCEL
+ should be treated as more or less equivalent to ACTION_UP,
+ so that is what is done here. */
+ EmacsNative.sendTouchUp (this.handle, (int) event.getX (index),
+ (int) event.getY (index),
+ event.getEventTime (), pointerID);
+ return true;
+
+ case MotionEvent.ACTION_MOVE:
+ /* Pointer motion event. */
+ EmacsNative.sendTouchMove (this.handle, (int) event.getX (index),
+ (int) event.getY (index),
+ event.getEventTime (), pointerID);
+ return true;
+ }
+
+ return false;
+ }
+
+ public boolean
+ onSomeKindOfMotionEvent (MotionEvent event)
+ {
+ /* isFromSource is not available until API level 18. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
+ {
+ if (!event.isFromSource (InputDevice.SOURCE_CLASS_POINTER))
+ return false;
+ }
+ else if (event.getSource () != InputDevice.SOURCE_CLASS_POINTER)
+ return false;
+
+ switch (event.getAction ())
+ {
+ case MotionEvent.ACTION_HOVER_ENTER:
+ EmacsNative.sendEnterNotify (this.handle, (int) event.getX (),
+ (int) event.getY (),
+ event.getEventTime ());
+ return true;
+
+ case MotionEvent.ACTION_MOVE:
+ case MotionEvent.ACTION_HOVER_MOVE:
+ EmacsNative.sendMotionNotify (this.handle, (int) event.getX (),
+ (int) event.getY (),
+ event.getEventTime ());
+ return true;
+
+ case MotionEvent.ACTION_HOVER_EXIT:
+
+ /* If the exit event comes from a button press, its button
+ state will have extra bits compared to the last known
+ button state. Since the exit event will interfere with
+ tool bar button presses, ignore such splurious events. */
+
+ if ((event.getButtonState () & ~lastButtonState) == 0)
+ EmacsNative.sendLeaveNotify (this.handle, (int) event.getX (),
+ (int) event.getY (),
+ event.getEventTime ());
+
+ return true;
+
+ case MotionEvent.ACTION_BUTTON_PRESS:
+ /* Find the button which was pressed. */
+ EmacsNative.sendButtonPress (this.handle, (int) event.getX (),
+ (int) event.getY (),
+ event.getEventTime (),
+ lastModifiers,
+ whatButtonWasIt (event, true));
+
+ if (Build.VERSION.SDK_INT
+ < Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ return true;
+
+ lastButtonState = event.getButtonState ();
+ return true;
+
+ case MotionEvent.ACTION_BUTTON_RELEASE:
+ /* Find the button which was released. */
+ EmacsNative.sendButtonRelease (this.handle, (int) event.getX (),
+ (int) event.getY (),
+ event.getEventTime (),
+ lastModifiers,
+ whatButtonWasIt (event, false));
+
+ if (Build.VERSION.SDK_INT
+ < Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ return true;
+
+ lastButtonState = event.getButtonState ();
+ return true;
+
+ case MotionEvent.ACTION_DOWN:
+ /* Emacs must return true even though touch events are not
+ handled here, because the value of this function is used by
+ the system to decide whether or not Emacs gets ACTION_MOVE
+ events. */
+ return true;
+
+ case MotionEvent.ACTION_UP:
+ /* However, if ACTION_UP reports a different button state from
+ the last known state, look up which button was released and
+ send a ButtonRelease event; this is to work around a bug in
+ the framework where real ACTION_BUTTON_RELEASE events are
+ not delivered. */
+
+ if (Build.VERSION.SDK_INT
+ < Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ return true;
+
+ if (event.getButtonState () == 0 && lastButtonState != 0)
+ {
+ EmacsNative.sendButtonRelease (this.handle, (int) event.getX (),
+ (int) event.getY (),
+ event.getEventTime (),
+ lastModifiers,
+ whatButtonWasIt (event, false));
+ lastButtonState = event.getButtonState ();
+ }
+
+ return true;
+
+ case MotionEvent.ACTION_SCROLL:
+ /* Send a scroll event with the specified deltas. */
+ EmacsNative.sendWheel (this.handle, (int) event.getX (),
+ (int) event.getY (),
+ event.getEventTime (),
+ lastModifiers,
+ event.getAxisValue (MotionEvent.AXIS_HSCROLL),
+ event.getAxisValue (MotionEvent.AXIS_VSCROLL));
+ return true;
+ }
+
+ return false;
+ }
+
+ public synchronized void
+ reparentTo (final EmacsWindow otherWindow, int x, int y)
+ {
+ int width, height;
+
+ /* Reparent this window to the other window. */
+
+ if (parent != null)
+ parent.children.remove (this);
+
+ if (otherWindow != null)
+ otherWindow.children.add (this);
+
+ parent = otherWindow;
+
+ /* Move this window to the new location. */
+ width = rect.width ();
+ height = rect.height ();
+ rect.left = x;
+ rect.top = y;
+ rect.right = x + width;
+ rect.bottom = y + height;
+
+ /* Now do the work necessary on the UI thread to reparent the
+ window. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ EmacsWindowAttachmentManager manager;
+ ViewManager parent;
+
+ /* First, detach this window if necessary. */
+ manager = EmacsWindowAttachmentManager.MANAGER;
+ manager.detachWindow (EmacsWindow.this);
+
+ /* Also unparent this view. */
+
+ /* If the window manager is set, use that instead. */
+ if (windowManager != null)
+ parent = windowManager;
+ else
+ parent = (ViewManager) view.getParent ();
+ windowManager = null;
+
+ if (parent != null)
+ parent.removeView (view);
+
+ /* Next, either add this window as a child of the new
+ parent's view, or make it available again. */
+ if (otherWindow != null)
+ otherWindow.view.addView (view);
+ else if (EmacsWindow.this.isMapped)
+ manager.registerWindow (EmacsWindow.this);
+
+ /* Request relayout. */
+ view.requestLayout ();
+ }
+ });
+ }
+
+ public void
+ makeInputFocus (long time)
+ {
+ /* TIME is currently ignored. Request the input focus now. */
+
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ view.requestFocus ();
+ }
+ });
+ }
+
+ public synchronized void
+ raise ()
+ {
+ /* This does nothing here. */
+ if (parent == null)
+ return;
+
+ /* Remove and add this view again. */
+ parent.children.remove (this);
+ parent.children.add (this);
+
+ /* Request a relayout. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ view.raise ();
+ }
+ });
+ }
+
+ public synchronized void
+ lower ()
+ {
+ /* This does nothing here. */
+ if (parent == null)
+ return;
+
+ /* Remove and add this view again. */
+ parent.children.remove (this);
+ parent.children.add (this);
+
+ /* Request a relayout. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ view.lower ();
+ }
+ });
+ }
+
+ public synchronized int[]
+ getWindowGeometry ()
+ {
+ int[] array;
+
+ array = new int[4];
+
+ array[0] = parent != null ? rect.left : xPosition;
+ array[1] = parent != null ? rect.top : yPosition;
+ array[2] = rect.width ();
+ array[3] = rect.height ();
+
+ return array;
+ }
+
+ public void
+ noticeIconified ()
+ {
+ EmacsNative.sendIconified (this.handle);
+ }
+
+ public void
+ noticeDeiconified ()
+ {
+ EmacsNative.sendDeiconified (this.handle);
+ }
+
+ public synchronized void
+ setDontAcceptFocus (final boolean dontAcceptFocus)
+ {
+ this.dontAcceptFocus = dontAcceptFocus;
+
+ /* Update the view's focus state. */
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ view.setFocusable (!dontAcceptFocus);
+ view.setFocusableInTouchMode (!dontAcceptFocus);
+ }
+ });
+ }
+
+ public synchronized void
+ setDontFocusOnMap (final boolean dontFocusOnMap)
+ {
+ this.dontFocusOnMap = dontFocusOnMap;
+ }
+
+ public synchronized boolean
+ getDontFocusOnMap ()
+ {
+ return dontFocusOnMap;
+ }
+
+ public int[]
+ translateCoordinates (int x, int y)
+ {
+ int[] array;
+
+ /* This is supposed to translate coordinates to the root
+ window. */
+ array = new int[2];
+ EmacsService.SERVICE.getLocationOnScreen (view, array);
+
+ /* Now, the coordinates of the view should be in array. Offset X
+ and Y by them. */
+ array[0] += x;
+ array[1] += y;
+
+ /* Return the resulting coordinates. */
+ return array;
+ }
+
+ public void
+ toggleOnScreenKeyboard (final boolean on)
+ {
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ if (on)
+ view.showOnScreenKeyboard ();
+ else
+ view.hideOnScreenKeyboard ();
+ }
+ });
+ }
+
+ public String
+ lookupString (int eventSerial)
+ {
+ String any;
+
+ synchronized (eventStrings)
+ {
+ any = eventStrings.remove (eventSerial);
+ }
+
+ return any;
+ }
+
+ public void
+ setFullscreen (final boolean isFullscreen)
+ {
+ EmacsService.SERVICE.runOnUiThread (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ EmacsActivity activity;
+ Object tem;
+
+ fullscreen = isFullscreen;
+ tem = getAttachedConsumer ();
+
+ if (tem != null)
+ {
+ activity = (EmacsActivity) getAttachedConsumer ();
+ activity.syncFullscreenWith (EmacsWindow.this);
+ }
+ }
+ });
+ }
+
+ public void
+ defineCursor (final EmacsCursor cursor)
+ {
+ /* Don't post this message if pointer icons aren't supported. */
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
+ view.post (new Runnable () {
+ @Override
+ public void
+ run ()
+ {
+ if (cursor != null)
+ view.setPointerIcon (cursor.icon);
+ else
+ view.setPointerIcon (null);
+ }
+ });
+ }
+
+ public synchronized void
+ notifyContentRectPosition (int xPosition, int yPosition)
+ {
+ Rect geometry;
+
+ /* Ignore these notifications if not a child of the root
+ window. */
+ if (parent != null)
+ return;
+
+ /* xPosition and yPosition are the position of this window
+ relative to the screen. Set them and request a ConfigureNotify
+ event. */
+
+ if (this.xPosition != xPosition
+ || this.yPosition != yPosition)
+ {
+ this.xPosition = xPosition;
+ this.yPosition = yPosition;
+
+ EmacsNative.sendConfigureNotify (this.handle,
+ System.currentTimeMillis (),
+ xPosition, yPosition,
+ rect.width (), rect.height ());
+ }
+ }
+};
diff --git a/java/org/gnu/emacs/EmacsWindowAttachmentManager.java b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
new file mode 100644
index 00000000000..4fda48616f0
--- /dev/null
+++ b/java/org/gnu/emacs/EmacsWindowAttachmentManager.java
@@ -0,0 +1,225 @@
+/* Communication module for Android terminals. -*- c-file-style: "GNU" -*-
+
+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/>. */
+
+package org.gnu.emacs;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.os.Build;
+import android.util.Log;
+
+/* Code to paper over the differences in lifecycles between
+ "activities" and windows. There are four interfaces to an instance
+ of this class:
+
+ registerWindowConsumer (WindowConsumer)
+ registerWindow (EmacsWindow)
+ removeWindowConsumer (WindowConsumer)
+ removeWindow (EmacsWindow)
+
+ A WindowConsumer is expected to allow an EmacsWindow to be attached
+ to it, and be created or destroyed.
+
+ Every time a window is created, registerWindow checks the list of
+ window consumers. If a consumer exists and does not currently have
+ a window of its own attached, it gets the new window. Otherwise,
+ the window attachment manager starts a new consumer.
+
+ Every time a consumer is registered, registerWindowConsumer checks
+ the list of available windows. If a window exists and is not
+ currently attached to a consumer, then the consumer gets it.
+
+ Finally, every time a window is removed, the consumer is
+ destroyed. */
+
+public final class EmacsWindowAttachmentManager
+{
+ public static EmacsWindowAttachmentManager MANAGER;
+ private final static String TAG = "EmacsWindowAttachmentManager";
+
+ static
+ {
+ MANAGER = new EmacsWindowAttachmentManager ();
+ };
+
+ public interface WindowConsumer
+ {
+ public void attachWindow (EmacsWindow window);
+ public EmacsWindow getAttachedWindow ();
+ public void detachWindow ();
+ public void destroy ();
+ };
+
+ public List<WindowConsumer> consumers;
+ public List<EmacsWindow> windows;
+
+ public
+ EmacsWindowAttachmentManager ()
+ {
+ consumers = new ArrayList<WindowConsumer> ();
+ windows = new ArrayList<EmacsWindow> ();
+ }
+
+ public void
+ registerWindowConsumer (WindowConsumer consumer)
+ {
+ Log.d (TAG, "registerWindowConsumer " + consumer);
+
+ consumers.add (consumer);
+
+ for (EmacsWindow window : windows)
+ {
+ if (window.getAttachedConsumer () == null)
+ {
+ Log.d (TAG, "registerWindowConsumer: attaching " + window);
+ consumer.attachWindow (window);
+ return;
+ }
+ }
+
+ Log.d (TAG, "registerWindowConsumer: sendWindowAction 0, 0");
+ EmacsNative.sendWindowAction ((short) 0, 0);
+ }
+
+ public synchronized void
+ registerWindow (EmacsWindow window)
+ {
+ Intent intent;
+ ActivityOptions options;
+
+ Log.d (TAG, "registerWindow (maybe): " + window);
+
+ if (windows.contains (window))
+ /* The window is already registered. */
+ return;
+
+ Log.d (TAG, "registerWindow: " + window);
+
+ windows.add (window);
+
+ for (WindowConsumer consumer : consumers)
+ {
+ if (consumer.getAttachedWindow () == null)
+ {
+ Log.d (TAG, "registerWindow: attaching " + consumer);
+ consumer.attachWindow (window);
+ return;
+ }
+ }
+
+ intent = new Intent (EmacsService.SERVICE,
+ EmacsMultitaskActivity.class);
+ intent.addFlags (Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+ | Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N)
+ EmacsService.SERVICE.startActivity (intent);
+ else
+ {
+ /* Specify the desired window size. */
+ options = ActivityOptions.makeBasic ();
+ options.setLaunchBounds (window.getGeometry ());
+ EmacsService.SERVICE.startActivity (intent,
+ options.toBundle ());
+ }
+
+ Log.d (TAG, "registerWindow: startActivity");
+ }
+
+ public void
+ removeWindowConsumer (WindowConsumer consumer, boolean isFinishing)
+ {
+ EmacsWindow window;
+
+ Log.d (TAG, "removeWindowConsumer " + consumer);
+
+ window = consumer.getAttachedWindow ();
+
+ if (window != null)
+ {
+ Log.d (TAG, "removeWindowConsumer: detaching " + window);
+
+ consumer.detachWindow ();
+ window.onActivityDetached (isFinishing);
+ }
+
+ Log.d (TAG, "removeWindowConsumer: removing " + consumer);
+ consumers.remove (consumer);
+ }
+
+ public synchronized void
+ detachWindow (EmacsWindow window)
+ {
+ WindowConsumer consumer;
+
+ Log.d (TAG, "detachWindow " + window);
+
+ if (window.getAttachedConsumer () != null)
+ {
+ consumer = window.getAttachedConsumer ();
+
+ Log.d (TAG, "detachWindow: removing" + consumer);
+
+ consumers.remove (consumer);
+ consumer.destroy ();
+ }
+
+ windows.remove (window);
+ }
+
+ public void
+ noticeIconified (WindowConsumer consumer)
+ {
+ EmacsWindow window;
+
+ Log.d (TAG, "noticeIconified " + consumer);
+
+ /* If a window is attached, send the appropriate iconification
+ events. */
+ window = consumer.getAttachedWindow ();
+
+ if (window != null)
+ window.noticeIconified ();
+ }
+
+ public void
+ noticeDeiconified (WindowConsumer consumer)
+ {
+ EmacsWindow window;
+
+ Log.d (TAG, "noticeDeiconified " + consumer);
+
+ /* If a window is attached, send the appropriate iconification
+ events. */
+ window = consumer.getAttachedWindow ();
+
+ if (window != null)
+ window.noticeDeiconified ();
+ }
+
+ public synchronized List<EmacsWindow>
+ copyWindows ()
+ {
+ return new ArrayList<EmacsWindow> (windows);
+ }
+};
diff --git a/java/res/drawable/emacs.png b/java/res/drawable/emacs.png
new file mode 100644
index 00000000000..9ab43d704be
--- /dev/null
+++ b/java/res/drawable/emacs.png
Binary files differ
diff --git a/java/res/values-v11/style.xml b/java/res/values-v11/style.xml
new file mode 100644
index 00000000000..b114758bf0d
--- /dev/null
+++ b/java/res/values-v11/style.xml
@@ -0,0 +1,24 @@
+<!-- Style resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+ <!-- Style used for popup menus and relatives on Android 3.x. -->
+ <style name="EmacsStyle" parent="@android:style/Theme.Holo.NoActionBar"/>
+ <style name="EmacsStyleOpen" parent="@android:style/Theme.Holo"/>
+</resources>
diff --git a/java/res/values-v14/style.xml b/java/res/values-v14/style.xml
new file mode 100644
index 00000000000..2cb54dc301b
--- /dev/null
+++ b/java/res/values-v14/style.xml
@@ -0,0 +1,25 @@
+<!-- Style resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+ <!-- Style used for popup menus and relatives between Android 4.0
+ and Android 10. -->
+ <style name="EmacsStyle" parent="@android:style/Theme.DeviceDefault.NoActionBar"/>
+ <style name="EmacsStyleOpen" parent="@android:style/Theme.DeviceDefault"/>
+</resources>
diff --git a/java/res/values-v19/bool.xml b/java/res/values-v19/bool.xml
new file mode 100644
index 00000000000..a4e3a87ae71
--- /dev/null
+++ b/java/res/values-v19/bool.xml
@@ -0,0 +1,22 @@
+<!-- Boolean resources for GNU Emacs on Android 4.4 or later.
+
+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/>. -->
+
+<resources>
+ <bool name="isAtLeastKitKat">true</bool>
+</resources>
diff --git a/java/res/values-v29/style.xml b/java/res/values-v29/style.xml
new file mode 100644
index 00000000000..ec7b8d14554
--- /dev/null
+++ b/java/res/values-v29/style.xml
@@ -0,0 +1,32 @@
+<!-- Style resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+ <!-- Style used for popup menus and relatives from Android 10.0
+ onwards-->
+ <style name="EmacsStyle" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
+
+ <!-- Required to make sure the status bar text remains legible. -->
+ <item name="android:statusBarColor">@android:color/black</item>
+ </style>
+ <style name="EmacsStyleOpen"
+ parent="@android:style/Theme.DeviceDefault.DayNight"/>
+</resources>
diff --git a/java/res/values/bool.xml b/java/res/values/bool.xml
new file mode 100644
index 00000000000..d37eab745c0
--- /dev/null
+++ b/java/res/values/bool.xml
@@ -0,0 +1,22 @@
+<!-- Boolean resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+ <bool name="isAtLeastKitKat">false</bool>
+</resources>
diff --git a/java/res/values/style.xml b/java/res/values/style.xml
new file mode 100644
index 00000000000..498e844fda0
--- /dev/null
+++ b/java/res/values/style.xml
@@ -0,0 +1,26 @@
+<!-- Style resources for GNU Emacs on Android.
+
+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/>. -->
+
+<resources>
+ <!-- Style used for popup menus and relatives on Android 2.2 and
+ 2.3. Styles used for newer Android versions are defined in
+ the res/values- directories for their respective API levels. -->
+ <style name="EmacsStyle" parent="@android:style/Theme.NoTitleBar"/>
+ <style name="EmacsStyleOpen" parent="@android:style/Theme"/>
+</resources>
diff --git a/java/res/xml/preferences.xml b/java/res/xml/preferences.xml
new file mode 100644
index 00000000000..f0c3abb52e7
--- /dev/null
+++ b/java/res/xml/preferences.xml
@@ -0,0 +1,28 @@
+<!-- Descriptions for the preferences screen for GNU Emacs on Android.
+
+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/>. -->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+ <Preference android:key="start_quick"
+ android:title="Restart Emacs with -Q"
+ android:summary="Restart Emacs, but do not load site lisp or init files."/>
+
+ <Preference android:key="erase_dump"
+ android:title="Delete dump file"
+ android:summary="Remove the dumped state created when Emacs was installed"/>
+</PreferenceScreen>
diff --git a/lib-src/Makefile.in b/lib-src/Makefile.in
index 5b82cf1151c..8a1922703de 100644
--- a/lib-src/Makefile.in
+++ b/lib-src/Makefile.in
@@ -96,6 +96,14 @@ localstatedir=@localstatedir@
srcdir=@srcdir@
VPATH=@srcdir@
+# Cross-compilation setup
+
+XCONFIGURE=@XCONFIGURE@
+
+ifneq ($(XCONFIGURE),)
+vpath $(srcdir)
+endif
+
# The top-level source directory, also set by configure.
top_srcdir=@top_srcdir@
# MinGW CPPFLAGS may use this.
@@ -140,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@
@@ -156,8 +167,13 @@ UTILITIES = hexl${EXEEXT} \
ifeq ($(HAVE_BE_APP),yes)
DONT_INSTALL= make-docfile${EXEEXT} make-fingerprint${EXEEXT} be-resources
else
+ifeq ($(XCONFIGURE)$(HAVE_ANDROID),yes)
+DONT_INSTALL = make-docfile${EXEEXT} make-fingerprint${EXEEXT} \
+ asset-directory-tool${EXEEXT}
+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.
@@ -374,7 +390,7 @@ clean: mostlyclean
rm -f ${EXE_FILES}
distclean: clean
- rm -f TAGS Makefile blessmail
+ rm -f TAGS Makefile blessmail Makefile.android
bootstrap-clean maintainer-clean: distclean
@@ -406,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 00000000000..239ab083b66
--- /dev/null
+++ b/lib-src/asset-directory-tool.c
@@ -0,0 +1,284 @@
+/* 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 <byteswap.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. */
+#ifdef WORDS_BIGENDIAN
+ output = bswap_32 (tree->offset);
+#else
+ output = tree->offset;
+#endif
+ 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-src/emacsclient.c b/lib-src/emacsclient.c
index 698bf9b50ae..a72fced1bf2 100644
--- a/lib-src/emacsclient.c
+++ b/lib-src/emacsclient.c
@@ -626,6 +626,8 @@ decode_options (int argc, char **argv)
alt_display = "w32";
#elif defined (HAVE_HAIKU)
alt_display = "be";
+#elif defined (HAVE_ANDROID)
+ alt_display = "android";
#endif
#ifdef HAVE_PGTK
diff --git a/lib/Makefile.in b/lib/Makefile.in
index 71199c32277..6752f68c50e 100644
--- a/lib/Makefile.in
+++ b/lib/Makefile.in
@@ -20,6 +20,15 @@
srcdir = @srcdir@
VPATH = @srcdir@
+# This is not empty if this is a Makefile that will be copied to
+# cross/lib.
+XCONFIGURE = @XCONFIGURE@
+
+# This is required to make sure symbol visibility is correct and
+# functions like readlinkat do not end up replacing their OS
+# counterparts.
+ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@
+
# Variables substituted by 'configure', and not autogenerated in gnulib.mk,
# or needed before gnulib.mk is included.
abs_top_srcdir = @abs_top_srcdir@
@@ -33,11 +42,11 @@ all:
HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
-ALL_CFLAGS= \
+ALL_CFLAGS = \
$(C_SWITCH_SYSTEM) $(C_SWITCH_MACHINE) $(DEPFLAGS) \
$(GNULIB_WARN_CFLAGS) $(WERROR_CFLAGS) $(PROFILING_CFLAGS) $(CFLAGS) \
- -I. -I../src -I$(srcdir) -I$(srcdir)/../src \
- $(if $(patsubst e-%,,$(notdir $<)),,-Demacs)
+ -I. -I../src -I$(srcdir) -I$(top_srcdir)/src \
+ $(if $(patsubst e-%,,$(notdir $<)),,-Demacs) $(ANDROID_BUILD_CFLAGS)
ifeq ($(HAVE_NATIVE_COMP),yes)
ALL_CFLAGS += -DGL_COMPILE_CRYPTO_STREAM
@@ -52,6 +61,12 @@ ifneq ($(SYSTEM_TYPE),windows-nt)
libgnu_a_SOURCES += openat-die.c save-cwd.c
endif
+ifeq ($(XCONFIGURE),android)
+# The next line is necessary to override -I$(srcdir), which will end
+# up pulling in lots of headers from the host.
+ALL_CFLAGS += -I$(top_srcdir)/cross -I.
+endif
+
DEPDIR = deps
ifeq ($(AUTO_DEPEND),yes)
DEPFLAGS = -MMD -MF $(DEPDIR)/$*.d -MP
@@ -60,11 +75,14 @@ else
DEPFLAGS =
endif
+# This piece of code interferes with cross compilation
+ifeq ($(XCONFIGURE),)
.PRECIOUS: ../config.status Makefile
../config.status: $(top_srcdir)/configure.ac $(top_srcdir)/m4/*.m4
$(MAKE) -C .. $(notdir $@)
Makefile: ../config.status $(srcdir)/Makefile.in
$(MAKE) -C .. lib/$@
+endif
# Object modules that need not be built for Emacs.
# Emacs does not need e-regex.o (it has its own regex-emacs.c),
@@ -111,10 +129,10 @@ clean:
mostlyclean: clean
rm -f $(filter-out %-t,$(MOSTLYCLEANFILES))
distclean bootstrap-clean: mostlyclean
- rm -f Makefile
+ rm -f Makefile Makefile.android
rm -fr $(DEPDIR)
maintainer-clean: distclean
- rm -f TAGS gnulib.mk
+ rm -f TAGS gnulib.mk gnulib.mk.android
-rmdir malloc sys 2>/dev/null || true
.PHONY: mostlyclean clean distclean bootstrap-clean maintainer-clean
diff --git a/lib/asnprintf.c b/lib/asnprintf.c
new file mode 100644
index 00000000000..f4861bf8457
--- /dev/null
+++ b/lib/asnprintf.c
@@ -0,0 +1,34 @@
+/* Formatted output to strings.
+ Copyright (C) 1999, 2002, 2006, 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/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "vasnprintf.h"
+
+#include <stdarg.h>
+
+char *
+asnprintf (char *resultbuf, size_t *lengthp, const char *format, ...)
+{
+ va_list args;
+ char *result;
+
+ va_start (args, format);
+ result = vasnprintf (resultbuf, lengthp, format, args);
+ va_end (args);
+ return result;
+}
diff --git a/lib/asprintf.c b/lib/asprintf.c
new file mode 100644
index 00000000000..ba58e06481f
--- /dev/null
+++ b/lib/asprintf.c
@@ -0,0 +1,39 @@
+/* Formatted output to strings.
+ Copyright (C) 1999, 2002, 2006-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/>. */
+
+#include <config.h>
+
+/* Specification. */
+#ifdef IN_LIBASPRINTF
+# include "vasprintf.h"
+#else
+# include <stdio.h>
+#endif
+
+#include <stdarg.h>
+
+int
+asprintf (char **resultp, const char *format, ...)
+{
+ va_list args;
+ int result;
+
+ va_start (args, format);
+ result = vasprintf (resultp, format, args);
+ va_end (args);
+ return result;
+}
diff --git a/lib/float+.h b/lib/float+.h
new file mode 100644
index 00000000000..e7531e46a38
--- /dev/null
+++ b/lib/float+.h
@@ -0,0 +1,147 @@
+/* Supplemental information about the floating-point formats.
+ Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2007.
+
+ 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/>. */
+
+#ifndef _FLOATPLUS_H
+#define _FLOATPLUS_H
+
+#include <float.h>
+#include <limits.h>
+
+/* Number of bits in the mantissa of a floating-point number, including the
+ "hidden bit". */
+#if FLT_RADIX == 2
+# define FLT_MANT_BIT FLT_MANT_DIG
+# define DBL_MANT_BIT DBL_MANT_DIG
+# define LDBL_MANT_BIT LDBL_MANT_DIG
+#elif FLT_RADIX == 4
+# define FLT_MANT_BIT (FLT_MANT_DIG * 2)
+# define DBL_MANT_BIT (DBL_MANT_DIG * 2)
+# define LDBL_MANT_BIT (LDBL_MANT_DIG * 2)
+#elif FLT_RADIX == 16
+# define FLT_MANT_BIT (FLT_MANT_DIG * 4)
+# define DBL_MANT_BIT (DBL_MANT_DIG * 4)
+# define LDBL_MANT_BIT (LDBL_MANT_DIG * 4)
+#endif
+
+/* Bit mask that can be used to mask the exponent, as an unsigned number. */
+#define FLT_EXP_MASK ((FLT_MAX_EXP - FLT_MIN_EXP) | 7)
+#define DBL_EXP_MASK ((DBL_MAX_EXP - DBL_MIN_EXP) | 7)
+#define LDBL_EXP_MASK ((LDBL_MAX_EXP - LDBL_MIN_EXP) | 7)
+
+/* Number of bits used for the exponent of a floating-point number, including
+ the exponent's sign. */
+#define FLT_EXP_BIT \
+ (FLT_EXP_MASK < 0x100 ? 8 : \
+ FLT_EXP_MASK < 0x200 ? 9 : \
+ FLT_EXP_MASK < 0x400 ? 10 : \
+ FLT_EXP_MASK < 0x800 ? 11 : \
+ FLT_EXP_MASK < 0x1000 ? 12 : \
+ FLT_EXP_MASK < 0x2000 ? 13 : \
+ FLT_EXP_MASK < 0x4000 ? 14 : \
+ FLT_EXP_MASK < 0x8000 ? 15 : \
+ FLT_EXP_MASK < 0x10000 ? 16 : \
+ FLT_EXP_MASK < 0x20000 ? 17 : \
+ FLT_EXP_MASK < 0x40000 ? 18 : \
+ FLT_EXP_MASK < 0x80000 ? 19 : \
+ FLT_EXP_MASK < 0x100000 ? 20 : \
+ FLT_EXP_MASK < 0x200000 ? 21 : \
+ FLT_EXP_MASK < 0x400000 ? 22 : \
+ FLT_EXP_MASK < 0x800000 ? 23 : \
+ FLT_EXP_MASK < 0x1000000 ? 24 : \
+ FLT_EXP_MASK < 0x2000000 ? 25 : \
+ FLT_EXP_MASK < 0x4000000 ? 26 : \
+ FLT_EXP_MASK < 0x8000000 ? 27 : \
+ FLT_EXP_MASK < 0x10000000 ? 28 : \
+ FLT_EXP_MASK < 0x20000000 ? 29 : \
+ FLT_EXP_MASK < 0x40000000 ? 30 : \
+ FLT_EXP_MASK <= 0x7fffffff ? 31 : \
+ 32)
+#define DBL_EXP_BIT \
+ (DBL_EXP_MASK < 0x100 ? 8 : \
+ DBL_EXP_MASK < 0x200 ? 9 : \
+ DBL_EXP_MASK < 0x400 ? 10 : \
+ DBL_EXP_MASK < 0x800 ? 11 : \
+ DBL_EXP_MASK < 0x1000 ? 12 : \
+ DBL_EXP_MASK < 0x2000 ? 13 : \
+ DBL_EXP_MASK < 0x4000 ? 14 : \
+ DBL_EXP_MASK < 0x8000 ? 15 : \
+ DBL_EXP_MASK < 0x10000 ? 16 : \
+ DBL_EXP_MASK < 0x20000 ? 17 : \
+ DBL_EXP_MASK < 0x40000 ? 18 : \
+ DBL_EXP_MASK < 0x80000 ? 19 : \
+ DBL_EXP_MASK < 0x100000 ? 20 : \
+ DBL_EXP_MASK < 0x200000 ? 21 : \
+ DBL_EXP_MASK < 0x400000 ? 22 : \
+ DBL_EXP_MASK < 0x800000 ? 23 : \
+ DBL_EXP_MASK < 0x1000000 ? 24 : \
+ DBL_EXP_MASK < 0x2000000 ? 25 : \
+ DBL_EXP_MASK < 0x4000000 ? 26 : \
+ DBL_EXP_MASK < 0x8000000 ? 27 : \
+ DBL_EXP_MASK < 0x10000000 ? 28 : \
+ DBL_EXP_MASK < 0x20000000 ? 29 : \
+ DBL_EXP_MASK < 0x40000000 ? 30 : \
+ DBL_EXP_MASK <= 0x7fffffff ? 31 : \
+ 32)
+#define LDBL_EXP_BIT \
+ (LDBL_EXP_MASK < 0x100 ? 8 : \
+ LDBL_EXP_MASK < 0x200 ? 9 : \
+ LDBL_EXP_MASK < 0x400 ? 10 : \
+ LDBL_EXP_MASK < 0x800 ? 11 : \
+ LDBL_EXP_MASK < 0x1000 ? 12 : \
+ LDBL_EXP_MASK < 0x2000 ? 13 : \
+ LDBL_EXP_MASK < 0x4000 ? 14 : \
+ LDBL_EXP_MASK < 0x8000 ? 15 : \
+ LDBL_EXP_MASK < 0x10000 ? 16 : \
+ LDBL_EXP_MASK < 0x20000 ? 17 : \
+ LDBL_EXP_MASK < 0x40000 ? 18 : \
+ LDBL_EXP_MASK < 0x80000 ? 19 : \
+ LDBL_EXP_MASK < 0x100000 ? 20 : \
+ LDBL_EXP_MASK < 0x200000 ? 21 : \
+ LDBL_EXP_MASK < 0x400000 ? 22 : \
+ LDBL_EXP_MASK < 0x800000 ? 23 : \
+ LDBL_EXP_MASK < 0x1000000 ? 24 : \
+ LDBL_EXP_MASK < 0x2000000 ? 25 : \
+ LDBL_EXP_MASK < 0x4000000 ? 26 : \
+ LDBL_EXP_MASK < 0x8000000 ? 27 : \
+ LDBL_EXP_MASK < 0x10000000 ? 28 : \
+ LDBL_EXP_MASK < 0x20000000 ? 29 : \
+ LDBL_EXP_MASK < 0x40000000 ? 30 : \
+ LDBL_EXP_MASK <= 0x7fffffff ? 31 : \
+ 32)
+
+/* Number of bits used for a floating-point number: the mantissa (not
+ counting the "hidden bit", since it may or may not be explicit), the
+ exponent, and the sign. */
+#define FLT_TOTAL_BIT ((FLT_MANT_BIT - 1) + FLT_EXP_BIT + 1)
+#define DBL_TOTAL_BIT ((DBL_MANT_BIT - 1) + DBL_EXP_BIT + 1)
+#define LDBL_TOTAL_BIT ((LDBL_MANT_BIT - 1) + LDBL_EXP_BIT + 1)
+
+/* Number of bytes used for a floating-point number.
+ This can be smaller than the 'sizeof'. For example, on i386 systems,
+ 'long double' most often have LDBL_MANT_BIT = 64, LDBL_EXP_BIT = 16, hence
+ LDBL_TOTAL_BIT = 80 bits, i.e. 10 bytes of consecutive memory, but
+ sizeof (long double) = 12 or = 16. */
+#define SIZEOF_FLT ((FLT_TOTAL_BIT + CHAR_BIT - 1) / CHAR_BIT)
+#define SIZEOF_DBL ((DBL_TOTAL_BIT + CHAR_BIT - 1) / CHAR_BIT)
+#define SIZEOF_LDBL ((LDBL_TOTAL_BIT + CHAR_BIT - 1) / CHAR_BIT)
+
+/* Verify that SIZEOF_FLT <= sizeof (float) etc. */
+typedef int verify_sizeof_flt[SIZEOF_FLT <= sizeof (float) ? 1 : -1];
+typedef int verify_sizeof_dbl[SIZEOF_DBL <= sizeof (double) ? 1 : - 1];
+typedef int verify_sizeof_ldbl[SIZEOF_LDBL <= sizeof (long double) ? 1 : - 1];
+
+#endif /* _FLOATPLUS_H */
diff --git a/lib/float.c b/lib/float.c
new file mode 100644
index 00000000000..f81ff33d3b7
--- /dev/null
+++ b/lib/float.c
@@ -0,0 +1,33 @@
+/* Auxiliary definitions for <float.h>.
+ Copyright (C) 2011-2023 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2011.
+
+ 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/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include <float.h>
+
+#if (defined _ARCH_PPC || defined _POWER) && (defined _AIX || defined __linux__) && (LDBL_MANT_DIG == 106) && defined __GNUC__
+const union gl_long_double_union gl_LDBL_MAX =
+ { { DBL_MAX, DBL_MAX / (double)134217728UL / (double)134217728UL } };
+#elif defined __i386__
+const union gl_long_double_union gl_LDBL_MAX =
+ { { 0xFFFFFFFF, 0xFFFFFFFF, 32766 } };
+#else
+/* This declaration is solely to ensure that after preprocessing
+ this file is never empty. */
+typedef int dummy;
+#endif
diff --git a/lib/float.in.h b/lib/float.in.h
new file mode 100644
index 00000000000..bf2c502c7f5
--- /dev/null
+++ b/lib/float.in.h
@@ -0,0 +1,194 @@
+/* A correct <float.h>.
+
+ Copyright (C) 2007-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/>. */
+
+#ifndef _@GUARD_PREFIX@_FLOAT_H
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+/* The include_next requires a split double-inclusion guard. */
+#@INCLUDE_NEXT@ @NEXT_FLOAT_H@
+
+#ifndef _@GUARD_PREFIX@_FLOAT_H
+#define _@GUARD_PREFIX@_FLOAT_H
+
+/* 'long double' properties. */
+
+#if defined __i386__ && (defined __BEOS__ || defined __OpenBSD__)
+/* Number of mantissa units, in base FLT_RADIX. */
+# undef LDBL_MANT_DIG
+# define LDBL_MANT_DIG 64
+/* Number of decimal digits that is sufficient for representing a number. */
+# undef LDBL_DIG
+# define LDBL_DIG 18
+/* x-1 where x is the smallest representable number > 1. */
+# undef LDBL_EPSILON
+# define LDBL_EPSILON 1.0842021724855044340E-19L
+/* Minimum e such that FLT_RADIX^(e-1) is a normalized number. */
+# undef LDBL_MIN_EXP
+# define LDBL_MIN_EXP (-16381)
+/* Maximum e such that FLT_RADIX^(e-1) is a representable finite number. */
+# undef LDBL_MAX_EXP
+# define LDBL_MAX_EXP 16384
+/* Minimum positive normalized number. */
+# undef LDBL_MIN
+# define LDBL_MIN 3.3621031431120935063E-4932L
+/* Maximum representable finite number. */
+# undef LDBL_MAX
+# define LDBL_MAX 1.1897314953572317650E+4932L
+/* Minimum e such that 10^e is in the range of normalized numbers. */
+# undef LDBL_MIN_10_EXP
+# define LDBL_MIN_10_EXP (-4931)
+/* Maximum e such that 10^e is in the range of representable finite numbers. */
+# undef LDBL_MAX_10_EXP
+# define LDBL_MAX_10_EXP 4932
+#endif
+
+/* On FreeBSD/x86 6.4, the 'long double' type really has only 53 bits of
+ precision in the compiler but 64 bits of precision at runtime. See
+ <https://lists.gnu.org/r/bug-gnulib/2008-07/msg00063.html>. */
+#if defined __i386__ && (defined __FreeBSD__ || defined __DragonFly__)
+/* Number of mantissa units, in base FLT_RADIX. */
+# undef LDBL_MANT_DIG
+# define LDBL_MANT_DIG 64
+/* Number of decimal digits that is sufficient for representing a number. */
+# undef LDBL_DIG
+# define LDBL_DIG 18
+/* x-1 where x is the smallest representable number > 1. */
+# undef LDBL_EPSILON
+# define LDBL_EPSILON 1.084202172485504434007452800869941711426e-19L /* 2^-63 */
+/* Minimum e such that FLT_RADIX^(e-1) is a normalized number. */
+# undef LDBL_MIN_EXP
+# define LDBL_MIN_EXP (-16381)
+/* Maximum e such that FLT_RADIX^(e-1) is a representable finite number. */
+# undef LDBL_MAX_EXP
+# define LDBL_MAX_EXP 16384
+/* Minimum positive normalized number. */
+# undef LDBL_MIN
+# define LDBL_MIN 3.362103143112093506262677817321752E-4932L /* = 0x1p-16382L */
+/* Maximum representable finite number. */
+# undef LDBL_MAX
+/* LDBL_MAX is represented as { 0xFFFFFFFF, 0xFFFFFFFF, 32766 }.
+ But the largest literal that GCC allows us to write is
+ 0x0.fffffffffffff8p16384L = { 0xFFFFF800, 0xFFFFFFFF, 32766 }.
+ So, define it like this through a reference to an external variable
+
+ const unsigned int LDBL_MAX[3] = { 0xFFFFFFFF, 0xFFFFFFFF, 32766 };
+ extern const long double LDBL_MAX;
+
+ Unfortunately, this is not a constant expression. */
+# if !GNULIB_defined_long_double_union
+union gl_long_double_union
+ {
+ struct { unsigned int lo; unsigned int hi; unsigned int exponent; } xd;
+ long double ld;
+ };
+# define GNULIB_defined_long_double_union 1
+# endif
+extern const union gl_long_double_union gl_LDBL_MAX;
+# define LDBL_MAX (gl_LDBL_MAX.ld)
+/* Minimum e such that 10^e is in the range of normalized numbers. */
+# undef LDBL_MIN_10_EXP
+# define LDBL_MIN_10_EXP (-4931)
+/* Maximum e such that 10^e is in the range of representable finite numbers. */
+# undef LDBL_MAX_10_EXP
+# define LDBL_MAX_10_EXP 4932
+#endif
+
+/* On AIX 7.1 with gcc 4.2, the values of LDBL_MIN_EXP, LDBL_MIN, LDBL_MAX are
+ wrong.
+ On Linux/PowerPC with gcc 4.4, the value of LDBL_MAX is wrong. */
+#if (defined _ARCH_PPC || defined _POWER) && defined _AIX && (LDBL_MANT_DIG == 106) && defined __GNUC__
+# undef LDBL_MIN_EXP
+# define LDBL_MIN_EXP DBL_MIN_EXP
+# undef LDBL_MIN_10_EXP
+# define LDBL_MIN_10_EXP DBL_MIN_10_EXP
+# undef LDBL_MIN
+# define LDBL_MIN 2.22507385850720138309023271733240406422e-308L /* DBL_MIN = 2^-1022 */
+#endif
+#if (defined _ARCH_PPC || defined _POWER) && (defined _AIX || defined __linux__) && (LDBL_MANT_DIG == 106) && defined __GNUC__
+# undef LDBL_MAX
+/* LDBL_MAX is represented as { 0x7FEFFFFF, 0xFFFFFFFF, 0x7C8FFFFF, 0xFFFFFFFF }.
+ It is not easy to define:
+ #define LDBL_MAX 1.79769313486231580793728971405302307166e308L
+ is too small, whereas
+ #define LDBL_MAX 1.79769313486231580793728971405302307167e308L
+ is too large. Apparently a bug in GCC decimal-to-binary conversion.
+ Also, I can't get values larger than
+ #define LDBL63 ((long double) (1ULL << 63))
+ #define LDBL882 (LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63)
+ #define LDBL945 (LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63)
+ #define LDBL1008 (LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63 * LDBL63)
+ #define LDBL_MAX (LDBL1008 * 65535.0L + LDBL945 * (long double) 9223372036821221375ULL + LDBL882 * (long double) 4611686018427387904ULL)
+ which is represented as { 0x7FEFFFFF, 0xFFFFFFFF, 0x7C8FFFFF, 0xF8000000 }.
+ So, define it like this through a reference to an external variable
+
+ const double LDBL_MAX[2] = { DBL_MAX, DBL_MAX / (double)134217728UL / (double)134217728UL };
+ extern const long double LDBL_MAX;
+
+ or through a pointer cast
+
+ #define LDBL_MAX \
+ (*(const long double *) (double[]) { DBL_MAX, DBL_MAX / (double)134217728UL / (double)134217728UL })
+
+ Unfortunately, this is not a constant expression, and the latter expression
+ does not work well when GCC is optimizing.. */
+# if !GNULIB_defined_long_double_union
+union gl_long_double_union
+ {
+ struct { double hi; double lo; } dd;
+ long double ld;
+ };
+# define GNULIB_defined_long_double_union 1
+# endif
+extern const union gl_long_double_union gl_LDBL_MAX;
+# define LDBL_MAX (gl_LDBL_MAX.ld)
+#endif
+
+/* On IRIX 6.5, with cc, the value of LDBL_MANT_DIG is wrong.
+ On IRIX 6.5, with gcc 4.2, the values of LDBL_MIN_EXP, LDBL_MIN, LDBL_EPSILON
+ are wrong. */
+#if defined __sgi && (LDBL_MANT_DIG >= 106)
+# undef LDBL_MANT_DIG
+# define LDBL_MANT_DIG 106
+# if defined __GNUC__
+# undef LDBL_MIN_EXP
+# define LDBL_MIN_EXP DBL_MIN_EXP
+# undef LDBL_MIN_10_EXP
+# define LDBL_MIN_10_EXP DBL_MIN_10_EXP
+# undef LDBL_MIN
+# define LDBL_MIN 2.22507385850720138309023271733240406422e-308L /* DBL_MIN = 2^-1022 */
+# undef LDBL_EPSILON
+# define LDBL_EPSILON 2.46519032881566189191165176650870696773e-32L /* 2^-105 */
+# endif
+#endif
+
+#if @REPLACE_ITOLD@
+/* Pull in a function that fixes the 'int' to 'long double' conversion
+ of glibc 2.7. */
+extern
+# ifdef __cplusplus
+"C"
+# endif
+void _Qp_itoq (long double *, int);
+static void (*_gl_float_fix_itold) (long double *, int) = _Qp_itoq;
+#endif
+
+#endif /* _@GUARD_PREFIX@_FLOAT_H */
+#endif /* _@GUARD_PREFIX@_FLOAT_H */
diff --git a/lib/fpucw.h b/lib/fpucw.h
new file mode 100644
index 00000000000..7dcb310eaf5
--- /dev/null
+++ b/lib/fpucw.h
@@ -0,0 +1,108 @@
+/* Manipulating the FPU control word. -*- coding: utf-8 -*-
+ Copyright (C) 2007-2023 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2007.
+
+ 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/>. */
+
+#ifndef _FPUCW_H
+#define _FPUCW_H
+
+/* The i386 floating point hardware (the 387 compatible FPU, not the modern
+ SSE/SSE2 hardware) has a controllable rounding precision. It is specified
+ through the 'PC' bits in the FPU control word ('fctrl' register). (See
+ the GNU libc i386 <fpu_control.h> header for details.)
+
+ On some platforms, such as Linux or Solaris, the default precision setting
+ is set to "extended precision". This means that 'long double' instructions
+ operate correctly, but 'double' computations often produce slightly
+ different results as on strictly IEEE 754 conforming systems.
+
+ On some platforms, such as NetBSD, the default precision is set to
+ "double precision". This means that 'long double' instructions will operate
+ only as 'double', i.e. lead to wrong results. Similarly on FreeBSD 6.4, at
+ least for the division of 'long double' numbers.
+
+ The FPU control word is under control of the application, i.e. it is
+ not required to be set either way by the ABI. (In fact, the i386 ABI
+ https://www.linux-mips.org/pub/linux/mips/doc/ABI/abi386-4.pdf page 3-12 = page 38
+ is not clear about it. But in any case, gcc treats the control word
+ like a "preserved" register: it emits code that assumes that the control
+ word is preserved across calls, and it restores the control word at the
+ end of functions that modify it.)
+
+ See Vincent Lefèvre's page https://www.vinc17.net/research/extended.en.html
+ for a good explanation.
+ See https://web.archive.org/web/20060905133417/http://www.uwsg.iu.edu/hypermail/linux/kernel/0103.0/0453.html
+ some argumentation which setting should be the default. */
+
+/* This header file provides the following facilities:
+ fpucw_t integral type holding the value of 'fctrl'
+ FPU_PC_MASK bit mask denoting the precision control
+ FPU_PC_DOUBLE precision control for 53 bits mantissa
+ FPU_PC_EXTENDED precision control for 64 bits mantissa
+ GET_FPUCW () yields the current FPU control word
+ SET_FPUCW (word) sets the FPU control word
+ DECL_LONG_DOUBLE_ROUNDING variable declaration for
+ BEGIN/END_LONG_DOUBLE_ROUNDING
+ BEGIN_LONG_DOUBLE_ROUNDING () starts a sequence of instructions with
+ 'long double' safe operation precision
+ END_LONG_DOUBLE_ROUNDING () ends a sequence of instructions with
+ 'long double' safe operation precision
+ */
+
+/* Inline assembler like this works only with GNU C and clang. */
+#if (defined __i386__ || defined __x86_64__) && (defined __GNUC__ || defined __clang__)
+
+typedef unsigned short fpucw_t; /* glibc calls this fpu_control_t */
+
+# define FPU_PC_MASK 0x0300
+# define FPU_PC_DOUBLE 0x200 /* glibc calls this _FPU_DOUBLE */
+# define FPU_PC_EXTENDED 0x300 /* glibc calls this _FPU_EXTENDED */
+
+# define GET_FPUCW() __extension__ \
+ ({ fpucw_t _cw; \
+ __asm__ __volatile__ ("fnstcw %0" : "=m" (*&_cw)); \
+ _cw; \
+ })
+# define SET_FPUCW(word) __extension__ \
+ (void)({ fpucw_t _ncw = (word); \
+ __asm__ __volatile__ ("fldcw %0" : : "m" (*&_ncw)); \
+ })
+
+# define DECL_LONG_DOUBLE_ROUNDING \
+ fpucw_t oldcw;
+# define BEGIN_LONG_DOUBLE_ROUNDING() \
+ (void)(oldcw = GET_FPUCW (), \
+ SET_FPUCW ((oldcw & ~FPU_PC_MASK) | FPU_PC_EXTENDED))
+# define END_LONG_DOUBLE_ROUNDING() \
+ SET_FPUCW (oldcw)
+
+#else
+
+typedef unsigned int fpucw_t;
+
+# define FPU_PC_MASK 0
+# define FPU_PC_DOUBLE 0
+# define FPU_PC_EXTENDED 0
+
+# define GET_FPUCW() 0
+# define SET_FPUCW(word) (void)(word)
+
+# define DECL_LONG_DOUBLE_ROUNDING
+# define BEGIN_LONG_DOUBLE_ROUNDING()
+# define END_LONG_DOUBLE_ROUNDING()
+
+#endif
+
+#endif /* _FPUCW_H */
diff --git a/lib/frexp.c b/lib/frexp.c
new file mode 100644
index 00000000000..78d627b5876
--- /dev/null
+++ b/lib/frexp.c
@@ -0,0 +1,168 @@
+/* Split a double into fraction and mantissa.
+ Copyright (C) 2007-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 Paolo Bonzini <bonzini@gnu.org>, 2003, and
+ Bruno Haible <bruno@clisp.org>, 2007. */
+
+#if ! defined USE_LONG_DOUBLE
+# include <config.h>
+#endif
+
+/* Specification. */
+#include <math.h>
+
+#include <float.h>
+#ifdef USE_LONG_DOUBLE
+# include "isnanl-nolibm.h"
+# include "fpucw.h"
+#else
+# include "isnand-nolibm.h"
+#endif
+
+/* This file assumes FLT_RADIX = 2. If FLT_RADIX is a power of 2 greater
+ than 2, or not even a power of 2, some rounding errors can occur, so that
+ then the returned mantissa is only guaranteed to be <= 1.0, not < 1.0. */
+
+#ifdef USE_LONG_DOUBLE
+# define FUNC frexpl
+# define DOUBLE long double
+# define ISNAN isnanl
+# define DECL_ROUNDING DECL_LONG_DOUBLE_ROUNDING
+# define BEGIN_ROUNDING() BEGIN_LONG_DOUBLE_ROUNDING ()
+# define END_ROUNDING() END_LONG_DOUBLE_ROUNDING ()
+# define L_(literal) literal##L
+#else
+# define FUNC frexp
+# define DOUBLE double
+# define ISNAN isnand
+# define DECL_ROUNDING
+# define BEGIN_ROUNDING()
+# define END_ROUNDING()
+# define L_(literal) literal
+#endif
+
+DOUBLE
+FUNC (DOUBLE x, int *expptr)
+{
+ int sign;
+ int exponent;
+ DECL_ROUNDING
+
+ /* Test for NaN, infinity, and zero. */
+ if (ISNAN (x) || x + x == x)
+ {
+ *expptr = 0;
+ return x;
+ }
+
+ sign = 0;
+ if (x < 0)
+ {
+ x = - x;
+ sign = -1;
+ }
+
+ BEGIN_ROUNDING ();
+
+ {
+ /* Since the exponent is an 'int', it fits in 64 bits. Therefore the
+ loops are executed no more than 64 times. */
+ DOUBLE pow2[64]; /* pow2[i] = 2^2^i */
+ DOUBLE powh[64]; /* powh[i] = 2^-2^i */
+ int i;
+
+ exponent = 0;
+ if (x >= L_(1.0))
+ {
+ /* A positive exponent. */
+ DOUBLE pow2_i; /* = pow2[i] */
+ DOUBLE powh_i; /* = powh[i] */
+
+ /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i,
+ x * 2^exponent = argument, x >= 1.0. */
+ for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5);
+ ;
+ i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i)
+ {
+ if (x >= pow2_i)
+ {
+ exponent += (1 << i);
+ x *= powh_i;
+ }
+ else
+ break;
+
+ pow2[i] = pow2_i;
+ powh[i] = powh_i;
+ }
+ /* Avoid making x too small, as it could become a denormalized
+ number and thus lose precision. */
+ while (i > 0 && x < pow2[i - 1])
+ {
+ i--;
+ powh_i = powh[i];
+ }
+ exponent += (1 << i);
+ x *= powh_i;
+ /* Here 2^-2^i <= x < 1.0. */
+ }
+ else
+ {
+ /* A negative or zero exponent. */
+ DOUBLE pow2_i; /* = pow2[i] */
+ DOUBLE powh_i; /* = powh[i] */
+
+ /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i,
+ x * 2^exponent = argument, x < 1.0. */
+ for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5);
+ ;
+ i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i)
+ {
+ if (x < powh_i)
+ {
+ exponent -= (1 << i);
+ x *= pow2_i;
+ }
+ else
+ break;
+
+ pow2[i] = pow2_i;
+ powh[i] = powh_i;
+ }
+ /* Here 2^-2^i <= x < 1.0. */
+ }
+
+ /* Invariants: x * 2^exponent = argument, and 2^-2^i <= x < 1.0. */
+ while (i > 0)
+ {
+ i--;
+ if (x < powh[i])
+ {
+ exponent -= (1 << i);
+ x *= pow2[i];
+ }
+ }
+ /* Here 0.5 <= x < 1.0. */
+ }
+
+ if (sign < 0)
+ x = - x;
+
+ END_ROUNDING ();
+
+ *expptr = exponent;
+ return x;
+}
diff --git a/lib/frexpl.c b/lib/frexpl.c
new file mode 100644
index 00000000000..8b17094d50c
--- /dev/null
+++ b/lib/frexpl.c
@@ -0,0 +1,35 @@
+/* Split a 'long double' into fraction and mantissa.
+ Copyright (C) 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/>. */
+
+#include <config.h>
+
+#if HAVE_SAME_LONG_DOUBLE_AS_DOUBLE
+
+/* Specification. */
+# include <math.h>
+
+long double
+frexpl (long double x, int *expptr)
+{
+ return frexp (x, expptr);
+}
+
+#else
+
+# define USE_LONG_DOUBLE
+# include "frexp.c"
+
+#endif
diff --git a/lib/fseterr.c b/lib/fseterr.c
new file mode 100644
index 00000000000..9b39305c6d5
--- /dev/null
+++ b/lib/fseterr.c
@@ -0,0 +1,84 @@
+/* Set the error indicator of a stream.
+ Copyright (C) 2007-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/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include "fseterr.h"
+
+#include <errno.h>
+
+#include "stdio-impl.h"
+
+/* This file is not used on systems that have the __fseterr function,
+ namely musl libc. */
+
+void
+fseterr (FILE *fp)
+{
+ /* Most systems provide FILE as a struct and the necessary bitmask in
+ <stdio.h>, because they need it for implementing getc() and putc() as
+ fast macros. */
+#if defined _IO_EOF_SEEN || defined _IO_ftrylockfile || __GNU_LIBRARY__ == 1
+ /* GNU libc, BeOS, Haiku, Linux libc5 */
+ fp->_flags |= _IO_ERR_SEEN;
+#elif defined __sferror || defined __DragonFly__ || defined __ANDROID__
+ /* FreeBSD, NetBSD, OpenBSD, DragonFly, Mac OS X, Cygwin, Minix 3, Android */
+ fp_->_flags |= __SERR;
+#elif defined __EMX__ /* emx+gcc */
+ fp->_flags |= _IOERR;
+#elif defined __minix /* Minix */
+ fp->_flags |= _IOERR;
+#elif defined _IOERR /* AIX, HP-UX, IRIX, OSF/1, Solaris, OpenServer, UnixWare, mingw, MSVC, NonStop Kernel, OpenVMS */
+ fp_->_flag |= _IOERR;
+#elif defined __UCLIBC__ /* uClibc */
+ fp->__modeflags |= __FLAG_ERROR;
+#elif defined __QNX__ /* QNX */
+ fp->_Mode |= 0x200 /* _MERR */;
+#elif defined __MINT__ /* Atari FreeMiNT */
+ fp->__error = 1;
+#elif defined EPLAN9 /* Plan9 */
+ if (fp->state != 0 /* CLOSED */)
+ fp->state = 5 /* ERR */;
+#elif 0 /* unknown */
+ /* Portable fallback, based on an idea by Rich Felker.
+ Wow! 6 system calls for something that is just a bit operation!
+ Not activated on any system, because there is no way to repair FP when
+ the sequence of system calls fails, and library code should not call
+ abort(). */
+ int saved_errno;
+ int fd;
+ int fd2;
+
+ saved_errno = errno;
+ fflush (fp);
+ fd = fileno (fp);
+ fd2 = dup (fd);
+ if (fd2 >= 0)
+ {
+ close (fd);
+ fputc ('\0', fp); /* This should set the error indicator. */
+ fflush (fp); /* Or this. */
+ if (dup2 (fd2, fd) < 0)
+ /* Whee... we botched the stream and now cannot restore it! */
+ abort ();
+ close (fd2);
+ }
+ errno = saved_errno;
+#else
+ #error "Please port gnulib fseterr.c to your platform! Look at the definitions of ferror and clearerr on your system, then report this to bug-gnulib."
+#endif
+}
diff --git a/lib/fseterr.h b/lib/fseterr.h
new file mode 100644
index 00000000000..0e9d2060aef
--- /dev/null
+++ b/lib/fseterr.h
@@ -0,0 +1,45 @@
+/* Set the error indicator of a stream.
+ Copyright (C) 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/>. */
+
+#ifndef _FSETERR_H
+#define _FSETERR_H
+
+#include <stdio.h>
+
+/* Set the error indicator of the stream FP.
+ The "error indicator" is set when an I/O operation on the stream fails, and
+ is cleared (together with the "end-of-file" indicator) by clearerr (FP). */
+
+#if HAVE___FSETERR /* musl libc */
+
+# include <stdio_ext.h>
+# define fseterr(fp) __fseterr (fp)
+
+#else
+
+# ifdef __cplusplus
+extern "C" {
+# endif
+
+extern void fseterr (FILE *fp);
+
+# ifdef __cplusplus
+}
+# endif
+
+#endif
+
+#endif /* _FSETERR_H */
diff --git a/lib/getdelim.c b/lib/getdelim.c
new file mode 100644
index 00000000000..79ec3dd12a3
--- /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 00000000000..85f16ab8bac
--- /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 5722e2588eb..e447a2c4054 100644
--- a/lib/gnulib.mk.in
+++ b/lib/gnulib.mk.in
@@ -110,6 +110,7 @@
# fsusage \
# fsync \
# futimens \
+# getline \
# getloadavg \
# getopt-gnu \
# getrandom \
@@ -135,6 +136,7 @@
# nstrftime \
# pathmax \
# pipe2 \
+# printf-posix \
# pselect \
# pthread_sigmask \
# qcopy-acl \
@@ -150,6 +152,7 @@
# stddef \
# stdio \
# stpcpy \
+# stpncpy \
# strnlen \
# strtoimax \
# symlink \
@@ -166,6 +169,7 @@
# unlocked-io \
# update-copyright \
# utimensat \
+# vasprintf-posix \
# vla \
# warnings \
# year2038
@@ -173,14 +177,28 @@
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_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@
+ANDROID_DEBUGGABLE = @ANDROID_DEBUGGABLE@
+ANDROID_JAR = @ANDROID_JAR@
+ANDROID_LDFLAGS = @ANDROID_LDFLAGS@
+ANDROID_LIBS = @ANDROID_LIBS@
+ANDROID_MIN_SDK = @ANDROID_MIN_SDK@
+ANDROID_OBJ = @ANDROID_OBJ@
+ANDROID_SDK_18_OR_EARLIER = @ANDROID_SDK_18_OR_EARLIER@
+ANDROID_SDK_8_OR_EARLIER = @ANDROID_SDK_8_OR_EARLIER@
+APKSIGNER = @APKSIGNER@
APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@
AR = @AR@
ARFLAGS = @ARFLAGS@
+ASM_SYMBOL_PREFIX = @ASM_SYMBOL_PREFIX@
ASSERT_H = @ASSERT_H@
AUTO_DEPEND = @AUTO_DEPEND@
AWK = @AWK@
@@ -217,6 +235,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@
@@ -247,6 +266,7 @@ EXEEXT = @EXEEXT@
FILE_HAS_ACL_LIB = @FILE_HAS_ACL_LIB@
FIND_DELETE = @FIND_DELETE@
FIRSTFILE_OBJ = @FIRSTFILE_OBJ@
+FLOAT_H = @FLOAT_H@
FONTCONFIG_CFLAGS = @FONTCONFIG_CFLAGS@
FONTCONFIG_LIBS = @FONTCONFIG_LIBS@
FONT_OBJ = @FONT_OBJ@
@@ -261,6 +281,7 @@ GETOPT_H = @GETOPT_H@
GETRANDOM_LIB = @GETRANDOM_LIB@
GFILENOTIFY_CFLAGS = @GFILENOTIFY_CFLAGS@
GFILENOTIFY_LIBS = @GFILENOTIFY_LIBS@
+GIF_CFLAGS = @GIF_CFLAGS@
GL_CFLAG_ALLOW_WARNINGS = @GL_CFLAG_ALLOW_WARNINGS@
GL_CFLAG_GNULIB_WARNINGS = @GL_CFLAG_GNULIB_WARNINGS@
GL_COND_LIBTOOL_CONDITION = @GL_COND_LIBTOOL_CONDITION@
@@ -274,19 +295,24 @@ GL_COND_OBJ_FACCESSAT_CONDITION = @GL_COND_OBJ_FACCESSAT_CONDITION@
GL_COND_OBJ_FCHMODAT_CONDITION = @GL_COND_OBJ_FCHMODAT_CONDITION@
GL_COND_OBJ_FCNTL_CONDITION = @GL_COND_OBJ_FCNTL_CONDITION@
GL_COND_OBJ_FDOPENDIR_CONDITION = @GL_COND_OBJ_FDOPENDIR_CONDITION@
+GL_COND_OBJ_FLOAT_CONDITION = @GL_COND_OBJ_FLOAT_CONDITION@
GL_COND_OBJ_FPENDING_CONDITION = @GL_COND_OBJ_FPENDING_CONDITION@
GL_COND_OBJ_FREE_CONDITION = @GL_COND_OBJ_FREE_CONDITION@
+GL_COND_OBJ_FSETERR_CONDITION = @GL_COND_OBJ_FSETERR_CONDITION@
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@
GL_COND_OBJ_GETTIMEOFDAY_CONDITION = @GL_COND_OBJ_GETTIMEOFDAY_CONDITION@
GL_COND_OBJ_GROUP_MEMBER_CONDITION = @GL_COND_OBJ_GROUP_MEMBER_CONDITION@
+GL_COND_OBJ_ITOLD_CONDITION = @GL_COND_OBJ_ITOLD_CONDITION@
GL_COND_OBJ_LCHMOD_CONDITION = @GL_COND_OBJ_LCHMOD_CONDITION@
GL_COND_OBJ_LSTAT_CONDITION = @GL_COND_OBJ_LSTAT_CONDITION@
GL_COND_OBJ_MEMPCPY_CONDITION = @GL_COND_OBJ_MEMPCPY_CONDITION@
@@ -304,9 +330,11 @@ GL_COND_OBJ_READLINK_CONDITION = @GL_COND_OBJ_READLINK_CONDITION@
GL_COND_OBJ_REGEX_CONDITION = @GL_COND_OBJ_REGEX_CONDITION@
GL_COND_OBJ_SIG2STR_CONDITION = @GL_COND_OBJ_SIG2STR_CONDITION@
GL_COND_OBJ_SIGDESCR_NP_CONDITION = @GL_COND_OBJ_SIGDESCR_NP_CONDITION@
+GL_COND_OBJ_SIGNBIT3_CONDITION = @GL_COND_OBJ_SIGNBIT3_CONDITION@
GL_COND_OBJ_STDIO_READ_CONDITION = @GL_COND_OBJ_STDIO_READ_CONDITION@
GL_COND_OBJ_STDIO_WRITE_CONDITION = @GL_COND_OBJ_STDIO_WRITE_CONDITION@
GL_COND_OBJ_STPCPY_CONDITION = @GL_COND_OBJ_STPCPY_CONDITION@
+GL_COND_OBJ_STPNCPY_CONDITION = @GL_COND_OBJ_STPNCPY_CONDITION@
GL_COND_OBJ_STRNLEN_CONDITION = @GL_COND_OBJ_STRNLEN_CONDITION@
GL_COND_OBJ_STRTOIMAX_CONDITION = @GL_COND_OBJ_STRTOIMAX_CONDITION@
GL_COND_OBJ_STRTOLL_CONDITION = @GL_COND_OBJ_STRTOLL_CONDITION@
@@ -320,6 +348,7 @@ GL_GENERATE_ASSERT_H_CONDITION = @GL_GENERATE_ASSERT_H_CONDITION@
GL_GENERATE_BYTESWAP_H_CONDITION = @GL_GENERATE_BYTESWAP_H_CONDITION@
GL_GENERATE_ERRNO_H_CONDITION = @GL_GENERATE_ERRNO_H_CONDITION@
GL_GENERATE_EXECINFO_H_CONDITION = @GL_GENERATE_EXECINFO_H_CONDITION@
+GL_GENERATE_FLOAT_H_CONDITION = @GL_GENERATE_FLOAT_H_CONDITION@
GL_GENERATE_GETOPT_CDEFS_H_CONDITION = @GL_GENERATE_GETOPT_CDEFS_H_CONDITION@
GL_GENERATE_GETOPT_H_CONDITION = @GL_GENERATE_GETOPT_H_CONDITION@
GL_GENERATE_GMP_GMP_H_CONDITION = @GL_GENERATE_GMP_GMP_H_CONDITION@
@@ -331,18 +360,37 @@ GL_GENERATE_STDCKDINT_H_CONDITION = @GL_GENERATE_STDCKDINT_H_CONDITION@
GL_GENERATE_STDDEF_H_CONDITION = @GL_GENERATE_STDDEF_H_CONDITION@
GL_GENERATE_STDINT_H_CONDITION = @GL_GENERATE_STDINT_H_CONDITION@
GL_GNULIB_ACCESS = @GL_GNULIB_ACCESS@
+GL_GNULIB_ACOSF = @GL_GNULIB_ACOSF@
+GL_GNULIB_ACOSL = @GL_GNULIB_ACOSL@
GL_GNULIB_ALIGNED_ALLOC = @GL_GNULIB_ALIGNED_ALLOC@
GL_GNULIB_ALPHASORT = @GL_GNULIB_ALPHASORT@
+GL_GNULIB_ASINF = @GL_GNULIB_ASINF@
+GL_GNULIB_ASINL = @GL_GNULIB_ASINL@
+GL_GNULIB_ATAN2F = @GL_GNULIB_ATAN2F@
+GL_GNULIB_ATANF = @GL_GNULIB_ATANF@
+GL_GNULIB_ATANL = @GL_GNULIB_ATANL@
GL_GNULIB_ATOLL = @GL_GNULIB_ATOLL@
GL_GNULIB_CALLOC_GNU = @GL_GNULIB_CALLOC_GNU@
GL_GNULIB_CALLOC_POSIX = @GL_GNULIB_CALLOC_POSIX@
GL_GNULIB_CANONICALIZE_FILE_NAME = @GL_GNULIB_CANONICALIZE_FILE_NAME@
+GL_GNULIB_CBRT = @GL_GNULIB_CBRT@
+GL_GNULIB_CBRTF = @GL_GNULIB_CBRTF@
+GL_GNULIB_CBRTL = @GL_GNULIB_CBRTL@
+GL_GNULIB_CEIL = @GL_GNULIB_CEIL@
+GL_GNULIB_CEILF = @GL_GNULIB_CEILF@
+GL_GNULIB_CEILL = @GL_GNULIB_CEILL@
GL_GNULIB_CHDIR = @GL_GNULIB_CHDIR@
GL_GNULIB_CHMOD = @GL_GNULIB_CHMOD@
GL_GNULIB_CHOWN = @GL_GNULIB_CHOWN@
GL_GNULIB_CLOSE = @GL_GNULIB_CLOSE@
GL_GNULIB_CLOSEDIR = @GL_GNULIB_CLOSEDIR@
+GL_GNULIB_COPYSIGN = @GL_GNULIB_COPYSIGN@
+GL_GNULIB_COPYSIGNF = @GL_GNULIB_COPYSIGNF@
+GL_GNULIB_COPYSIGNL = @GL_GNULIB_COPYSIGNL@
GL_GNULIB_COPY_FILE_RANGE = @GL_GNULIB_COPY_FILE_RANGE@
+GL_GNULIB_COSF = @GL_GNULIB_COSF@
+GL_GNULIB_COSHF = @GL_GNULIB_COSHF@
+GL_GNULIB_COSL = @GL_GNULIB_COSL@
GL_GNULIB_CREAT = @GL_GNULIB_CREAT@
GL_GNULIB_CTIME = @GL_GNULIB_CTIME@
GL_GNULIB_DIRFD = @GL_GNULIB_DIRFD@
@@ -359,7 +407,17 @@ GL_GNULIB_EXECV = @GL_GNULIB_EXECV@
GL_GNULIB_EXECVE = @GL_GNULIB_EXECVE@
GL_GNULIB_EXECVP = @GL_GNULIB_EXECVP@
GL_GNULIB_EXECVPE = @GL_GNULIB_EXECVPE@
+GL_GNULIB_EXP2 = @GL_GNULIB_EXP2@
+GL_GNULIB_EXP2F = @GL_GNULIB_EXP2F@
+GL_GNULIB_EXP2L = @GL_GNULIB_EXP2L@
+GL_GNULIB_EXPF = @GL_GNULIB_EXPF@
+GL_GNULIB_EXPL = @GL_GNULIB_EXPL@
GL_GNULIB_EXPLICIT_BZERO = @GL_GNULIB_EXPLICIT_BZERO@
+GL_GNULIB_EXPM1 = @GL_GNULIB_EXPM1@
+GL_GNULIB_EXPM1F = @GL_GNULIB_EXPM1F@
+GL_GNULIB_EXPM1L = @GL_GNULIB_EXPM1L@
+GL_GNULIB_FABSF = @GL_GNULIB_FABSF@
+GL_GNULIB_FABSL = @GL_GNULIB_FABSL@
GL_GNULIB_FACCESSAT = @GL_GNULIB_FACCESSAT@
GL_GNULIB_FCHDIR = @GL_GNULIB_FCHDIR@
GL_GNULIB_FCHMODAT = @GL_GNULIB_FCHMODAT@
@@ -374,6 +432,15 @@ GL_GNULIB_FFSL = @GL_GNULIB_FFSL@
GL_GNULIB_FFSLL = @GL_GNULIB_FFSLL@
GL_GNULIB_FGETC = @GL_GNULIB_FGETC@
GL_GNULIB_FGETS = @GL_GNULIB_FGETS@
+GL_GNULIB_FLOOR = @GL_GNULIB_FLOOR@
+GL_GNULIB_FLOORF = @GL_GNULIB_FLOORF@
+GL_GNULIB_FLOORL = @GL_GNULIB_FLOORL@
+GL_GNULIB_FMA = @GL_GNULIB_FMA@
+GL_GNULIB_FMAF = @GL_GNULIB_FMAF@
+GL_GNULIB_FMAL = @GL_GNULIB_FMAL@
+GL_GNULIB_FMOD = @GL_GNULIB_FMOD@
+GL_GNULIB_FMODF = @GL_GNULIB_FMODF@
+GL_GNULIB_FMODL = @GL_GNULIB_FMODL@
GL_GNULIB_FOPEN = @GL_GNULIB_FOPEN@
GL_GNULIB_FOPEN_GNU = @GL_GNULIB_FOPEN_GNU@
GL_GNULIB_FPRINTF = @GL_GNULIB_FPRINTF@
@@ -384,6 +451,9 @@ GL_GNULIB_FPUTS = @GL_GNULIB_FPUTS@
GL_GNULIB_FREAD = @GL_GNULIB_FREAD@
GL_GNULIB_FREE_POSIX = @GL_GNULIB_FREE_POSIX@
GL_GNULIB_FREOPEN = @GL_GNULIB_FREOPEN@
+GL_GNULIB_FREXP = @GL_GNULIB_FREXP@
+GL_GNULIB_FREXPF = @GL_GNULIB_FREXPF@
+GL_GNULIB_FREXPL = @GL_GNULIB_FREXPL@
GL_GNULIB_FSCANF = @GL_GNULIB_FSCANF@
GL_GNULIB_FSEEK = @GL_GNULIB_FSEEK@
GL_GNULIB_FSEEKO = @GL_GNULIB_FSEEKO@
@@ -420,14 +490,43 @@ GL_GNULIB_GETUMASK = @GL_GNULIB_GETUMASK@
GL_GNULIB_GETUSERSHELL = @GL_GNULIB_GETUSERSHELL@
GL_GNULIB_GRANTPT = @GL_GNULIB_GRANTPT@
GL_GNULIB_GROUP_MEMBER = @GL_GNULIB_GROUP_MEMBER@
+GL_GNULIB_HYPOT = @GL_GNULIB_HYPOT@
+GL_GNULIB_HYPOTF = @GL_GNULIB_HYPOTF@
+GL_GNULIB_HYPOTL = @GL_GNULIB_HYPOTL@
+GL_GNULIB_ILOGB = @GL_GNULIB_ILOGB@
+GL_GNULIB_ILOGBF = @GL_GNULIB_ILOGBF@
+GL_GNULIB_ILOGBL = @GL_GNULIB_ILOGBL@
GL_GNULIB_IMAXABS = @GL_GNULIB_IMAXABS@
GL_GNULIB_IMAXDIV = @GL_GNULIB_IMAXDIV@
GL_GNULIB_ISATTY = @GL_GNULIB_ISATTY@
+GL_GNULIB_ISFINITE = @GL_GNULIB_ISFINITE@
+GL_GNULIB_ISINF = @GL_GNULIB_ISINF@
+GL_GNULIB_ISNAN = @GL_GNULIB_ISNAN@
+GL_GNULIB_ISNAND = @GL_GNULIB_ISNAND@
+GL_GNULIB_ISNANF = @GL_GNULIB_ISNANF@
+GL_GNULIB_ISNANL = @GL_GNULIB_ISNANL@
GL_GNULIB_LCHMOD = @GL_GNULIB_LCHMOD@
GL_GNULIB_LCHOWN = @GL_GNULIB_LCHOWN@
+GL_GNULIB_LDEXPF = @GL_GNULIB_LDEXPF@
+GL_GNULIB_LDEXPL = @GL_GNULIB_LDEXPL@
GL_GNULIB_LINK = @GL_GNULIB_LINK@
GL_GNULIB_LINKAT = @GL_GNULIB_LINKAT@
GL_GNULIB_LOCALTIME = @GL_GNULIB_LOCALTIME@
+GL_GNULIB_LOG = @GL_GNULIB_LOG@
+GL_GNULIB_LOG10 = @GL_GNULIB_LOG10@
+GL_GNULIB_LOG10F = @GL_GNULIB_LOG10F@
+GL_GNULIB_LOG10L = @GL_GNULIB_LOG10L@
+GL_GNULIB_LOG1P = @GL_GNULIB_LOG1P@
+GL_GNULIB_LOG1PF = @GL_GNULIB_LOG1PF@
+GL_GNULIB_LOG1PL = @GL_GNULIB_LOG1PL@
+GL_GNULIB_LOG2 = @GL_GNULIB_LOG2@
+GL_GNULIB_LOG2F = @GL_GNULIB_LOG2F@
+GL_GNULIB_LOG2L = @GL_GNULIB_LOG2L@
+GL_GNULIB_LOGB = @GL_GNULIB_LOGB@
+GL_GNULIB_LOGBF = @GL_GNULIB_LOGBF@
+GL_GNULIB_LOGBL = @GL_GNULIB_LOGBL@
+GL_GNULIB_LOGF = @GL_GNULIB_LOGF@
+GL_GNULIB_LOGL = @GL_GNULIB_LOGL@
GL_GNULIB_LSEEK = @GL_GNULIB_LSEEK@
GL_GNULIB_LSTAT = @GL_GNULIB_LSTAT@
GL_GNULIB_MALLOC_GNU = @GL_GNULIB_MALLOC_GNU@
@@ -472,6 +571,9 @@ GL_GNULIB_MDA_GETCWD = @GL_GNULIB_MDA_GETCWD@
GL_GNULIB_MDA_GETPID = @GL_GNULIB_MDA_GETPID@
GL_GNULIB_MDA_GETW = @GL_GNULIB_MDA_GETW@
GL_GNULIB_MDA_ISATTY = @GL_GNULIB_MDA_ISATTY@
+GL_GNULIB_MDA_J0 = @GL_GNULIB_MDA_J0@
+GL_GNULIB_MDA_J1 = @GL_GNULIB_MDA_J1@
+GL_GNULIB_MDA_JN = @GL_GNULIB_MDA_JN@
GL_GNULIB_MDA_LSEEK = @GL_GNULIB_MDA_LSEEK@
GL_GNULIB_MDA_MEMCCPY = @GL_GNULIB_MDA_MEMCCPY@
GL_GNULIB_MDA_MKDIR = @GL_GNULIB_MDA_MKDIR@
@@ -488,6 +590,9 @@ GL_GNULIB_MDA_TZSET = @GL_GNULIB_MDA_TZSET@
GL_GNULIB_MDA_UMASK = @GL_GNULIB_MDA_UMASK@
GL_GNULIB_MDA_UNLINK = @GL_GNULIB_MDA_UNLINK@
GL_GNULIB_MDA_WRITE = @GL_GNULIB_MDA_WRITE@
+GL_GNULIB_MDA_Y0 = @GL_GNULIB_MDA_Y0@
+GL_GNULIB_MDA_Y1 = @GL_GNULIB_MDA_Y1@
+GL_GNULIB_MDA_YN = @GL_GNULIB_MDA_YN@
GL_GNULIB_MEMCHR = @GL_GNULIB_MEMCHR@
GL_GNULIB_MEMMEM = @GL_GNULIB_MEMMEM@
GL_GNULIB_MEMPCPY = @GL_GNULIB_MEMPCPY@
@@ -505,6 +610,9 @@ GL_GNULIB_MKOSTEMPS = @GL_GNULIB_MKOSTEMPS@
GL_GNULIB_MKSTEMP = @GL_GNULIB_MKSTEMP@
GL_GNULIB_MKSTEMPS = @GL_GNULIB_MKSTEMPS@
GL_GNULIB_MKTIME = @GL_GNULIB_MKTIME@
+GL_GNULIB_MODF = @GL_GNULIB_MODF@
+GL_GNULIB_MODFF = @GL_GNULIB_MODFF@
+GL_GNULIB_MODFL = @GL_GNULIB_MODFL@
GL_GNULIB_NANOSLEEP = @GL_GNULIB_NANOSLEEP@
GL_GNULIB_NONBLOCKING = @GL_GNULIB_NONBLOCKING@
GL_GNULIB_OBSTACK_PRINTF = @GL_GNULIB_OBSTACK_PRINTF@
@@ -520,6 +628,7 @@ GL_GNULIB_PIPE2 = @GL_GNULIB_PIPE2@
GL_GNULIB_POPEN = @GL_GNULIB_POPEN@
GL_GNULIB_POSIX_MEMALIGN = @GL_GNULIB_POSIX_MEMALIGN@
GL_GNULIB_POSIX_OPENPT = @GL_GNULIB_POSIX_OPENPT@
+GL_GNULIB_POWF = @GL_GNULIB_POWF@
GL_GNULIB_PREAD = @GL_GNULIB_PREAD@
GL_GNULIB_PRINTF = @GL_GNULIB_PRINTF@
GL_GNULIB_PRINTF_POSIX = @GL_GNULIB_PRINTF_POSIX@
@@ -545,11 +654,20 @@ GL_GNULIB_REALLOCARRAY = @GL_GNULIB_REALLOCARRAY@
GL_GNULIB_REALLOC_GNU = @GL_GNULIB_REALLOC_GNU@
GL_GNULIB_REALLOC_POSIX = @GL_GNULIB_REALLOC_POSIX@
GL_GNULIB_REALPATH = @GL_GNULIB_REALPATH@
+GL_GNULIB_REMAINDER = @GL_GNULIB_REMAINDER@
+GL_GNULIB_REMAINDERF = @GL_GNULIB_REMAINDERF@
+GL_GNULIB_REMAINDERL = @GL_GNULIB_REMAINDERL@
GL_GNULIB_REMOVE = @GL_GNULIB_REMOVE@
GL_GNULIB_RENAME = @GL_GNULIB_RENAME@
GL_GNULIB_RENAMEAT = @GL_GNULIB_RENAMEAT@
GL_GNULIB_REWINDDIR = @GL_GNULIB_REWINDDIR@
+GL_GNULIB_RINT = @GL_GNULIB_RINT@
+GL_GNULIB_RINTF = @GL_GNULIB_RINTF@
+GL_GNULIB_RINTL = @GL_GNULIB_RINTL@
GL_GNULIB_RMDIR = @GL_GNULIB_RMDIR@
+GL_GNULIB_ROUND = @GL_GNULIB_ROUND@
+GL_GNULIB_ROUNDF = @GL_GNULIB_ROUNDF@
+GL_GNULIB_ROUNDL = @GL_GNULIB_ROUNDL@
GL_GNULIB_RPMATCH = @GL_GNULIB_RPMATCH@
GL_GNULIB_SCANDIR = @GL_GNULIB_SCANDIR@
GL_GNULIB_SCANF = @GL_GNULIB_SCANF@
@@ -561,10 +679,16 @@ GL_GNULIB_SIGABBREV_NP = @GL_GNULIB_SIGABBREV_NP@
GL_GNULIB_SIGACTION = @GL_GNULIB_SIGACTION@
GL_GNULIB_SIGDESCR_NP = @GL_GNULIB_SIGDESCR_NP@
GL_GNULIB_SIGNAL_H_SIGPIPE = @GL_GNULIB_SIGNAL_H_SIGPIPE@
+GL_GNULIB_SIGNBIT = @GL_GNULIB_SIGNBIT@
GL_GNULIB_SIGPROCMASK = @GL_GNULIB_SIGPROCMASK@
+GL_GNULIB_SINF = @GL_GNULIB_SINF@
+GL_GNULIB_SINHF = @GL_GNULIB_SINHF@
+GL_GNULIB_SINL = @GL_GNULIB_SINL@
GL_GNULIB_SLEEP = @GL_GNULIB_SLEEP@
GL_GNULIB_SNPRINTF = @GL_GNULIB_SNPRINTF@
GL_GNULIB_SPRINTF_POSIX = @GL_GNULIB_SPRINTF_POSIX@
+GL_GNULIB_SQRTF = @GL_GNULIB_SQRTF@
+GL_GNULIB_SQRTL = @GL_GNULIB_SQRTL@
GL_GNULIB_STAT = @GL_GNULIB_STAT@
GL_GNULIB_STDIO_H_NONBLOCKING = @GL_GNULIB_STDIO_H_NONBLOCKING@
GL_GNULIB_STDIO_H_SIGPIPE = @GL_GNULIB_STDIO_H_SIGPIPE@
@@ -598,6 +722,9 @@ GL_GNULIB_STRVERSCMP = @GL_GNULIB_STRVERSCMP@
GL_GNULIB_SYMLINK = @GL_GNULIB_SYMLINK@
GL_GNULIB_SYMLINKAT = @GL_GNULIB_SYMLINKAT@
GL_GNULIB_SYSTEM_POSIX = @GL_GNULIB_SYSTEM_POSIX@
+GL_GNULIB_TANF = @GL_GNULIB_TANF@
+GL_GNULIB_TANHF = @GL_GNULIB_TANHF@
+GL_GNULIB_TANL = @GL_GNULIB_TANL@
GL_GNULIB_TIME = @GL_GNULIB_TIME@
GL_GNULIB_TIMEGM = @GL_GNULIB_TIMEGM@
GL_GNULIB_TIMESPEC_GET = @GL_GNULIB_TIMESPEC_GET@
@@ -605,7 +732,10 @@ GL_GNULIB_TIMESPEC_GETRES = @GL_GNULIB_TIMESPEC_GETRES@
GL_GNULIB_TIME_R = @GL_GNULIB_TIME_R@
GL_GNULIB_TIME_RZ = @GL_GNULIB_TIME_RZ@
GL_GNULIB_TMPFILE = @GL_GNULIB_TMPFILE@
+GL_GNULIB_TRUNC = @GL_GNULIB_TRUNC@
GL_GNULIB_TRUNCATE = @GL_GNULIB_TRUNCATE@
+GL_GNULIB_TRUNCF = @GL_GNULIB_TRUNCF@
+GL_GNULIB_TRUNCL = @GL_GNULIB_TRUNCL@
GL_GNULIB_TTYNAME_R = @GL_GNULIB_TTYNAME_R@
GL_GNULIB_TZSET = @GL_GNULIB_TZSET@
GL_GNULIB_UNISTD_H_GETOPT = @GL_GNULIB_UNISTD_H_GETOPT@
@@ -652,26 +782,58 @@ HAIKU_LIBS = @HAIKU_LIBS@
HAIKU_OBJ = @HAIKU_OBJ@
HARFBUZZ_CFLAGS = @HARFBUZZ_CFLAGS@
HARFBUZZ_LIBS = @HARFBUZZ_LIBS@
+HAVE_ACOSF = @HAVE_ACOSF@
+HAVE_ACOSL = @HAVE_ACOSL@
HAVE_ALIGNED_ALLOC = @HAVE_ALIGNED_ALLOC@
HAVE_ALLOCA_H = @HAVE_ALLOCA_H@
HAVE_ALPHASORT = @HAVE_ALPHASORT@
+HAVE_ASINF = @HAVE_ASINF@
+HAVE_ASINL = @HAVE_ASINL@
+HAVE_ATAN2F = @HAVE_ATAN2F@
+HAVE_ATANF = @HAVE_ATANF@
+HAVE_ATANL = @HAVE_ATANL@
HAVE_ATOLL = @HAVE_ATOLL@
HAVE_BE_APP = @HAVE_BE_APP@
HAVE_C99_STDINT_H = @HAVE_C99_STDINT_H@
HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@
+HAVE_CBRT = @HAVE_CBRT@
+HAVE_CBRTF = @HAVE_CBRTF@
+HAVE_CBRTL = @HAVE_CBRTL@
HAVE_CHOWN = @HAVE_CHOWN@
HAVE_CLOSEDIR = @HAVE_CLOSEDIR@
+HAVE_COPYSIGN = @HAVE_COPYSIGN@
+HAVE_COPYSIGNL = @HAVE_COPYSIGNL@
HAVE_COPY_FILE_RANGE = @HAVE_COPY_FILE_RANGE@
+HAVE_COSF = @HAVE_COSF@
+HAVE_COSHF = @HAVE_COSHF@
+HAVE_COSL = @HAVE_COSL@
+HAVE_DECL_ACOSL = @HAVE_DECL_ACOSL@
+HAVE_DECL_ASINL = @HAVE_DECL_ASINL@
+HAVE_DECL_ATANL = @HAVE_DECL_ATANL@
+HAVE_DECL_CBRTF = @HAVE_DECL_CBRTF@
+HAVE_DECL_CBRTL = @HAVE_DECL_CBRTL@
+HAVE_DECL_CEILF = @HAVE_DECL_CEILF@
+HAVE_DECL_CEILL = @HAVE_DECL_CEILL@
+HAVE_DECL_COPYSIGNF = @HAVE_DECL_COPYSIGNF@
+HAVE_DECL_COSL = @HAVE_DECL_COSL@
HAVE_DECL_DIRFD = @HAVE_DECL_DIRFD@
HAVE_DECL_ECVT = @HAVE_DECL_ECVT@
HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@
HAVE_DECL_EXECVPE = @HAVE_DECL_EXECVPE@
+HAVE_DECL_EXP2 = @HAVE_DECL_EXP2@
+HAVE_DECL_EXP2F = @HAVE_DECL_EXP2F@
+HAVE_DECL_EXP2L = @HAVE_DECL_EXP2L@
+HAVE_DECL_EXPL = @HAVE_DECL_EXPL@
+HAVE_DECL_EXPM1L = @HAVE_DECL_EXPM1L@
HAVE_DECL_FCHDIR = @HAVE_DECL_FCHDIR@
HAVE_DECL_FCLOSEALL = @HAVE_DECL_FCLOSEALL@
HAVE_DECL_FCVT = @HAVE_DECL_FCVT@
HAVE_DECL_FDATASYNC = @HAVE_DECL_FDATASYNC@
HAVE_DECL_FDOPENDIR = @HAVE_DECL_FDOPENDIR@
+HAVE_DECL_FLOORF = @HAVE_DECL_FLOORF@
+HAVE_DECL_FLOORL = @HAVE_DECL_FLOORL@
HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@
+HAVE_DECL_FREXPL = @HAVE_DECL_FREXPL@
HAVE_DECL_FSEEKO = @HAVE_DECL_FSEEKO@
HAVE_DECL_FTELLO = @HAVE_DECL_FTELLO@
HAVE_DECL_GCVT = @HAVE_DECL_GCVT@
@@ -687,17 +849,32 @@ HAVE_DECL_GETW = @HAVE_DECL_GETW@
HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@
HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@
HAVE_DECL_INITSTATE = @HAVE_DECL_INITSTATE@
+HAVE_DECL_LDEXPL = @HAVE_DECL_LDEXPL@
HAVE_DECL_LOCALTIME_R = @HAVE_DECL_LOCALTIME_R@
+HAVE_DECL_LOG10L = @HAVE_DECL_LOG10L@
+HAVE_DECL_LOG2 = @HAVE_DECL_LOG2@
+HAVE_DECL_LOG2F = @HAVE_DECL_LOG2F@
+HAVE_DECL_LOG2L = @HAVE_DECL_LOG2L@
+HAVE_DECL_LOGB = @HAVE_DECL_LOGB@
+HAVE_DECL_LOGL = @HAVE_DECL_LOGL@
HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@
HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@
HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@
HAVE_DECL_POSIX_SPAWN_SETSID = @HAVE_DECL_POSIX_SPAWN_SETSID@
HAVE_DECL_PROGRAM_INVOCATION_NAME = @HAVE_DECL_PROGRAM_INVOCATION_NAME@
HAVE_DECL_PUTW = @HAVE_DECL_PUTW@
+HAVE_DECL_REMAINDER = @HAVE_DECL_REMAINDER@
+HAVE_DECL_REMAINDERL = @HAVE_DECL_REMAINDERL@
+HAVE_DECL_RINTF = @HAVE_DECL_RINTF@
+HAVE_DECL_ROUND = @HAVE_DECL_ROUND@
+HAVE_DECL_ROUNDF = @HAVE_DECL_ROUNDF@
+HAVE_DECL_ROUNDL = @HAVE_DECL_ROUNDL@
HAVE_DECL_SETENV = @HAVE_DECL_SETENV@
HAVE_DECL_SETHOSTNAME = @HAVE_DECL_SETHOSTNAME@
HAVE_DECL_SETSTATE = @HAVE_DECL_SETSTATE@
+HAVE_DECL_SINL = @HAVE_DECL_SINL@
HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@
+HAVE_DECL_SQRTL = @HAVE_DECL_SQRTL@
HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@
HAVE_DECL_STRERROR_R = @HAVE_DECL_STRERROR_R@
HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@
@@ -706,7 +883,11 @@ HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@
HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@
HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@
HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@
+HAVE_DECL_TANL = @HAVE_DECL_TANL@
+HAVE_DECL_TRUNC = @HAVE_DECL_TRUNC@
HAVE_DECL_TRUNCATE = @HAVE_DECL_TRUNCATE@
+HAVE_DECL_TRUNCF = @HAVE_DECL_TRUNCF@
+HAVE_DECL_TRUNCL = @HAVE_DECL_TRUNCL@
HAVE_DECL_TTYNAME_R = @HAVE_DECL_TTYNAME_R@
HAVE_DECL_UNSETENV = @HAVE_DECL_UNSETENV@
HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@
@@ -715,7 +896,13 @@ HAVE_DPRINTF = @HAVE_DPRINTF@
HAVE_DUP3 = @HAVE_DUP3@
HAVE_EUIDACCESS = @HAVE_EUIDACCESS@
HAVE_EXECVPE = @HAVE_EXECVPE@
+HAVE_EXPF = @HAVE_EXPF@
+HAVE_EXPL = @HAVE_EXPL@
HAVE_EXPLICIT_BZERO = @HAVE_EXPLICIT_BZERO@
+HAVE_EXPM1 = @HAVE_EXPM1@
+HAVE_EXPM1F = @HAVE_EXPM1F@
+HAVE_FABSF = @HAVE_FABSF@
+HAVE_FABSL = @HAVE_FABSL@
HAVE_FACCESSAT = @HAVE_FACCESSAT@
HAVE_FCHDIR = @HAVE_FCHDIR@
HAVE_FCHMODAT = @HAVE_FCHMODAT@
@@ -723,8 +910,15 @@ HAVE_FCHOWNAT = @HAVE_FCHOWNAT@
HAVE_FCNTL = @HAVE_FCNTL@
HAVE_FDATASYNC = @HAVE_FDATASYNC@
HAVE_FDOPENDIR = @HAVE_FDOPENDIR@
+HAVE_FEATURES_H = @HAVE_FEATURES_H@
HAVE_FFSL = @HAVE_FFSL@
HAVE_FFSLL = @HAVE_FFSLL@
+HAVE_FMA = @HAVE_FMA@
+HAVE_FMAF = @HAVE_FMAF@
+HAVE_FMAL = @HAVE_FMAL@
+HAVE_FMODF = @HAVE_FMODF@
+HAVE_FMODL = @HAVE_FMODL@
+HAVE_FREXPF = @HAVE_FREXPF@
HAVE_FSEEKO = @HAVE_FSEEKO@
HAVE_FSTATAT = @HAVE_FSTATAT@
HAVE_FSYNC = @HAVE_FSYNC@
@@ -747,17 +941,35 @@ HAVE_GETUMASK = @HAVE_GETUMASK@
HAVE_GRANTPT = @HAVE_GRANTPT@
HAVE_GROUP_MEMBER = @HAVE_GROUP_MEMBER@
HAVE_GSETTINGS = @HAVE_GSETTINGS@
+HAVE_HYPOTF = @HAVE_HYPOTF@
+HAVE_HYPOTL = @HAVE_HYPOTL@
+HAVE_ILOGB = @HAVE_ILOGB@
+HAVE_ILOGBF = @HAVE_ILOGBF@
+HAVE_ILOGBL = @HAVE_ILOGBL@
HAVE_IMAXABS = @HAVE_IMAXABS@
HAVE_IMAXDIV = @HAVE_IMAXDIV@
HAVE_IMAXDIV_T = @HAVE_IMAXDIV_T@
HAVE_INITSTATE = @HAVE_INITSTATE@
HAVE_INTTYPES_H = @HAVE_INTTYPES_H@
+HAVE_ISNAND = @HAVE_ISNAND@
+HAVE_ISNANF = @HAVE_ISNANF@
+HAVE_ISNANL = @HAVE_ISNANL@
HAVE_LCHMOD = @HAVE_LCHMOD@
HAVE_LCHOWN = @HAVE_LCHOWN@
+HAVE_LDEXPF = @HAVE_LDEXPF@
HAVE_LIBGMP = @HAVE_LIBGMP@
HAVE_LIBSECCOMP = @HAVE_LIBSECCOMP@
HAVE_LINK = @HAVE_LINK@
HAVE_LINKAT = @HAVE_LINKAT@
+HAVE_LOG10F = @HAVE_LOG10F@
+HAVE_LOG10L = @HAVE_LOG10L@
+HAVE_LOG1P = @HAVE_LOG1P@
+HAVE_LOG1PF = @HAVE_LOG1PF@
+HAVE_LOG1PL = @HAVE_LOG1PL@
+HAVE_LOGBF = @HAVE_LOGBF@
+HAVE_LOGBL = @HAVE_LOGBL@
+HAVE_LOGF = @HAVE_LOGF@
+HAVE_LOGL = @HAVE_LOGL@
HAVE_LSTAT = @HAVE_LSTAT@
HAVE_MACPORTS = @HAVE_MACPORTS@
HAVE_MAX_ALIGN_T = @HAVE_MAX_ALIGN_T@
@@ -775,6 +987,8 @@ HAVE_MKOSTEMP = @HAVE_MKOSTEMP@
HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@
HAVE_MKSTEMP = @HAVE_MKSTEMP@
HAVE_MKSTEMPS = @HAVE_MKSTEMPS@
+HAVE_MODFF = @HAVE_MODFF@
+HAVE_MODFL = @HAVE_MODFL@
HAVE_MODULES = @HAVE_MODULES@
HAVE_NANOSLEEP = @HAVE_NANOSLEEP@
HAVE_NATIVE_COMP = @HAVE_NATIVE_COMP@
@@ -794,6 +1008,7 @@ HAVE_POSIX_SPAWN = @HAVE_POSIX_SPAWN@
HAVE_POSIX_SPAWNATTR_SETFLAGS = @HAVE_POSIX_SPAWNATTR_SETFLAGS@
HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR = @HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR@
HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP = @HAVE_POSIX_SPAWN_FILE_ACTIONS_ADDCHDIR_NP@
+HAVE_POWF = @HAVE_POWF@
HAVE_PREAD = @HAVE_PREAD@
HAVE_PSELECT = @HAVE_PSELECT@
HAVE_PTHREAD_SIGMASK = @HAVE_PTHREAD_SIGMASK@
@@ -811,9 +1026,14 @@ HAVE_READLINK = @HAVE_READLINK@
HAVE_READLINKAT = @HAVE_READLINKAT@
HAVE_REALLOCARRAY = @HAVE_REALLOCARRAY@
HAVE_REALPATH = @HAVE_REALPATH@
+HAVE_REMAINDER = @HAVE_REMAINDER@
+HAVE_REMAINDERF = @HAVE_REMAINDERF@
HAVE_RENAMEAT = @HAVE_RENAMEAT@
HAVE_REWINDDIR = @HAVE_REWINDDIR@
+HAVE_RINT = @HAVE_RINT@
+HAVE_RINTL = @HAVE_RINTL@
HAVE_RPMATCH = @HAVE_RPMATCH@
+HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = @HAVE_SAME_LONG_DOUBLE_AS_DOUBLE@
HAVE_SCANDIR = @HAVE_SCANDIR@
HAVE_SECCOMP = @HAVE_SECCOMP@
HAVE_SECURE_GETENV = @HAVE_SECURE_GETENV@
@@ -829,8 +1049,13 @@ HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@
HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@
HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@
HAVE_SIGSET_T = @HAVE_SIGSET_T@
+HAVE_SINF = @HAVE_SINF@
+HAVE_SINHF = @HAVE_SINHF@
+HAVE_SINL = @HAVE_SINL@
HAVE_SLEEP = @HAVE_SLEEP@
HAVE_SPAWN_H = @HAVE_SPAWN_H@
+HAVE_SQRTF = @HAVE_SQRTF@
+HAVE_SQRTL = @HAVE_SQRTL@
HAVE_STDINT_H = @HAVE_STDINT_H@
HAVE_STPCPY = @HAVE_STPCPY@
HAVE_STPNCPY = @HAVE_STPNCPY@
@@ -861,6 +1086,9 @@ HAVE_SYS_RANDOM_H = @HAVE_SYS_RANDOM_H@
HAVE_SYS_SELECT_H = @HAVE_SYS_SELECT_H@
HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@
HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@
+HAVE_TANF = @HAVE_TANF@
+HAVE_TANHF = @HAVE_TANHF@
+HAVE_TANL = @HAVE_TANL@
HAVE_TIMEGM = @HAVE_TIMEGM@
HAVE_TIMESPEC_GET = @HAVE_TIMESPEC_GET@
HAVE_TIMESPEC_GETRES = @HAVE_TIMESPEC_GETRES@
@@ -891,6 +1119,9 @@ 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@
+JPEG_CFLAGS = @JPEG_CFLAGS@
JSON_CFLAGS = @JSON_CFLAGS@
JSON_LIBS = @JSON_LIBS@
JSON_OBJ = @JSON_OBJ@
@@ -909,6 +1140,7 @@ LIBGCCJIT_CFLAGS = @LIBGCCJIT_CFLAGS@
LIBGCCJIT_LIBS = @LIBGCCJIT_LIBS@
LIBGIF = @LIBGIF@
LIBGMP = @LIBGMP@
+LIBGMP_CFLAGS = @LIBGMP_CFLAGS@
LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
LIBGNU_LIBDEPS = @LIBGNU_LIBDEPS@
@@ -926,6 +1158,7 @@ LIBRESOLV = @LIBRESOLV@
LIBS = @LIBS@
LIBSECCOMP_CFLAGS = @LIBSECCOMP_CFLAGS@
LIBSECCOMP_LIBS = @LIBSECCOMP_LIBS@
+LIBSELINUX_CFLAGS = @LIBSELINUX_CFLAGS@
LIBSELINUX_LIBS = @LIBSELINUX_LIBS@
LIBSOUND = @LIBSOUND@
LIBSYSTEMD_CFLAGS = @LIBSYSTEMD_CFLAGS@
@@ -974,14 +1207,28 @@ MODULES_SECONDARY_SUFFIX = @MODULES_SECONDARY_SUFFIX@
MODULES_SUFFIX = @MODULES_SUFFIX@
NANOSLEEP_LIB = @NANOSLEEP_LIB@
NATIVE_COMPILATION_AOT = @NATIVE_COMPILATION_AOT@
+NDK_BUILD_ABI = @NDK_BUILD_ABI@
+NDK_BUILD_ANDROID_MK = @NDK_BUILD_ANDROID_MK@
+NDK_BUILD_ANY_CXX_MODULE = @NDK_BUILD_ANY_CXX_MODULE@
+NDK_BUILD_AR = @NDK_BUILD_AR@
+NDK_BUILD_ARCH = @NDK_BUILD_ARCH@
+NDK_BUILD_CC = @NDK_BUILD_CC@
+NDK_BUILD_CFLAGS = @NDK_BUILD_CFLAGS@
+NDK_BUILD_CXX = @NDK_BUILD_CXX@
+NDK_BUILD_CXX_SHARED = @NDK_BUILD_CXX_SHARED@
+NDK_BUILD_MODULES = @NDK_BUILD_MODULES@
+NDK_BUILD_NASM = @NDK_BUILD_NASM@
+NDK_BUILD_SDK = @NDK_BUILD_SDK@
NEXT_ASSERT_H = @NEXT_ASSERT_H@
NEXT_AS_FIRST_DIRECTIVE_ASSERT_H = @NEXT_AS_FIRST_DIRECTIVE_ASSERT_H@
NEXT_AS_FIRST_DIRECTIVE_DIRENT_H = @NEXT_AS_FIRST_DIRECTIVE_DIRENT_H@
NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@
NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@
+NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@
NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@
NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@
NEXT_AS_FIRST_DIRECTIVE_LIMITS_H = @NEXT_AS_FIRST_DIRECTIVE_LIMITS_H@
+NEXT_AS_FIRST_DIRECTIVE_MATH_H = @NEXT_AS_FIRST_DIRECTIVE_MATH_H@
NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H = @NEXT_AS_FIRST_DIRECTIVE_SIGNAL_H@
NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@
NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@
@@ -998,9 +1245,11 @@ NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@
NEXT_DIRENT_H = @NEXT_DIRENT_H@
NEXT_ERRNO_H = @NEXT_ERRNO_H@
NEXT_FCNTL_H = @NEXT_FCNTL_H@
+NEXT_FLOAT_H = @NEXT_FLOAT_H@
NEXT_GETOPT_H = @NEXT_GETOPT_H@
NEXT_INTTYPES_H = @NEXT_INTTYPES_H@
NEXT_LIMITS_H = @NEXT_LIMITS_H@
+NEXT_MATH_H = @NEXT_MATH_H@
NEXT_SIGNAL_H = @NEXT_SIGNAL_H@
NEXT_STDDEF_H = @NEXT_STDDEF_H@
NEXT_STDINT_H = @NEXT_STDINT_H@
@@ -1055,15 +1304,26 @@ QCOPY_ACL_LIB = @QCOPY_ACL_LIB@
RALLOC_OBJ = @RALLOC_OBJ@
RANLIB = @RANLIB@
REPLACE_ACCESS = @REPLACE_ACCESS@
+REPLACE_ACOSF = @REPLACE_ACOSF@
REPLACE_ALIGNED_ALLOC = @REPLACE_ALIGNED_ALLOC@
+REPLACE_ASINF = @REPLACE_ASINF@
+REPLACE_ATAN2F = @REPLACE_ATAN2F@
+REPLACE_ATANF = @REPLACE_ATANF@
REPLACE_CALLOC_FOR_CALLOC_GNU = @REPLACE_CALLOC_FOR_CALLOC_GNU@
REPLACE_CALLOC_FOR_CALLOC_POSIX = @REPLACE_CALLOC_FOR_CALLOC_POSIX@
REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@
+REPLACE_CBRTF = @REPLACE_CBRTF@
+REPLACE_CBRTL = @REPLACE_CBRTL@
+REPLACE_CEIL = @REPLACE_CEIL@
+REPLACE_CEILF = @REPLACE_CEILF@
+REPLACE_CEILL = @REPLACE_CEILL@
REPLACE_CHMOD = @REPLACE_CHMOD@
REPLACE_CHOWN = @REPLACE_CHOWN@
REPLACE_CLOSE = @REPLACE_CLOSE@
REPLACE_CLOSEDIR = @REPLACE_CLOSEDIR@
REPLACE_COPY_FILE_RANGE = @REPLACE_COPY_FILE_RANGE@
+REPLACE_COSF = @REPLACE_COSF@
+REPLACE_COSHF = @REPLACE_COSHF@
REPLACE_CREAT = @REPLACE_CREAT@
REPLACE_CTIME = @REPLACE_CTIME@
REPLACE_DIRFD = @REPLACE_DIRFD@
@@ -1078,6 +1338,14 @@ REPLACE_EXECV = @REPLACE_EXECV@
REPLACE_EXECVE = @REPLACE_EXECVE@
REPLACE_EXECVP = @REPLACE_EXECVP@
REPLACE_EXECVPE = @REPLACE_EXECVPE@
+REPLACE_EXP2 = @REPLACE_EXP2@
+REPLACE_EXP2L = @REPLACE_EXP2L@
+REPLACE_EXPF = @REPLACE_EXPF@
+REPLACE_EXPL = @REPLACE_EXPL@
+REPLACE_EXPM1 = @REPLACE_EXPM1@
+REPLACE_EXPM1F = @REPLACE_EXPM1F@
+REPLACE_EXPM1L = @REPLACE_EXPM1L@
+REPLACE_FABSL = @REPLACE_FABSL@
REPLACE_FACCESSAT = @REPLACE_FACCESSAT@
REPLACE_FCHMODAT = @REPLACE_FCHMODAT@
REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@
@@ -1088,12 +1356,24 @@ REPLACE_FDOPEN = @REPLACE_FDOPEN@
REPLACE_FDOPENDIR = @REPLACE_FDOPENDIR@
REPLACE_FFLUSH = @REPLACE_FFLUSH@
REPLACE_FFSLL = @REPLACE_FFSLL@
+REPLACE_FLOOR = @REPLACE_FLOOR@
+REPLACE_FLOORF = @REPLACE_FLOORF@
+REPLACE_FLOORL = @REPLACE_FLOORL@
+REPLACE_FMA = @REPLACE_FMA@
+REPLACE_FMAF = @REPLACE_FMAF@
+REPLACE_FMAL = @REPLACE_FMAL@
+REPLACE_FMOD = @REPLACE_FMOD@
+REPLACE_FMODF = @REPLACE_FMODF@
+REPLACE_FMODL = @REPLACE_FMODL@
REPLACE_FOPEN = @REPLACE_FOPEN@
REPLACE_FOPEN_FOR_FOPEN_GNU = @REPLACE_FOPEN_FOR_FOPEN_GNU@
REPLACE_FPRINTF = @REPLACE_FPRINTF@
REPLACE_FPURGE = @REPLACE_FPURGE@
REPLACE_FREE = @REPLACE_FREE@
REPLACE_FREOPEN = @REPLACE_FREOPEN@
+REPLACE_FREXP = @REPLACE_FREXP@
+REPLACE_FREXPF = @REPLACE_FREXPF@
+REPLACE_FREXPL = @REPLACE_FREXPL@
REPLACE_FSEEK = @REPLACE_FSEEK@
REPLACE_FSEEKO = @REPLACE_FSEEKO@
REPLACE_FSTAT = @REPLACE_FSTAT@
@@ -1119,15 +1399,42 @@ REPLACE_GETRANDOM = @REPLACE_GETRANDOM@
REPLACE_GETSUBOPT = @REPLACE_GETSUBOPT@
REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@
REPLACE_GMTIME = @REPLACE_GMTIME@
+REPLACE_HUGE_VAL = @REPLACE_HUGE_VAL@
+REPLACE_HYPOT = @REPLACE_HYPOT@
+REPLACE_HYPOTF = @REPLACE_HYPOTF@
+REPLACE_HYPOTL = @REPLACE_HYPOTL@
+REPLACE_ILOGB = @REPLACE_ILOGB@
+REPLACE_ILOGBF = @REPLACE_ILOGBF@
+REPLACE_ILOGBL = @REPLACE_ILOGBL@
REPLACE_IMAXABS = @REPLACE_IMAXABS@
REPLACE_IMAXDIV = @REPLACE_IMAXDIV@
REPLACE_INITSTATE = @REPLACE_INITSTATE@
REPLACE_ISATTY = @REPLACE_ISATTY@
+REPLACE_ISFINITE = @REPLACE_ISFINITE@
+REPLACE_ISINF = @REPLACE_ISINF@
+REPLACE_ISNAN = @REPLACE_ISNAN@
+REPLACE_ITOLD = @REPLACE_ITOLD@
REPLACE_LCHOWN = @REPLACE_LCHOWN@
+REPLACE_LDEXPL = @REPLACE_LDEXPL@
REPLACE_LINK = @REPLACE_LINK@
REPLACE_LINKAT = @REPLACE_LINKAT@
REPLACE_LOCALTIME = @REPLACE_LOCALTIME@
REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@
+REPLACE_LOG = @REPLACE_LOG@
+REPLACE_LOG10 = @REPLACE_LOG10@
+REPLACE_LOG10F = @REPLACE_LOG10F@
+REPLACE_LOG10L = @REPLACE_LOG10L@
+REPLACE_LOG1P = @REPLACE_LOG1P@
+REPLACE_LOG1PF = @REPLACE_LOG1PF@
+REPLACE_LOG1PL = @REPLACE_LOG1PL@
+REPLACE_LOG2 = @REPLACE_LOG2@
+REPLACE_LOG2F = @REPLACE_LOG2F@
+REPLACE_LOG2L = @REPLACE_LOG2L@
+REPLACE_LOGB = @REPLACE_LOGB@
+REPLACE_LOGBF = @REPLACE_LOGBF@
+REPLACE_LOGBL = @REPLACE_LOGBL@
+REPLACE_LOGF = @REPLACE_LOGF@
+REPLACE_LOGL = @REPLACE_LOGL@
REPLACE_LSEEK = @REPLACE_LSEEK@
REPLACE_LSTAT = @REPLACE_LSTAT@
REPLACE_MALLOC_FOR_MALLOC_GNU = @REPLACE_MALLOC_FOR_MALLOC_GNU@
@@ -1147,6 +1454,10 @@ REPLACE_MKOSTEMP = @REPLACE_MKOSTEMP@
REPLACE_MKOSTEMPS = @REPLACE_MKOSTEMPS@
REPLACE_MKSTEMP = @REPLACE_MKSTEMP@
REPLACE_MKTIME = @REPLACE_MKTIME@
+REPLACE_MODF = @REPLACE_MODF@
+REPLACE_MODFF = @REPLACE_MODFF@
+REPLACE_MODFL = @REPLACE_MODFL@
+REPLACE_NAN = @REPLACE_NAN@
REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@
REPLACE_NULL = @REPLACE_NULL@
REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@
@@ -1178,18 +1489,34 @@ REPLACE_REALLOCARRAY = @REPLACE_REALLOCARRAY@
REPLACE_REALLOC_FOR_REALLOC_GNU = @REPLACE_REALLOC_FOR_REALLOC_GNU@
REPLACE_REALLOC_FOR_REALLOC_POSIX = @REPLACE_REALLOC_FOR_REALLOC_POSIX@
REPLACE_REALPATH = @REPLACE_REALPATH@
+REPLACE_REMAINDER = @REPLACE_REMAINDER@
+REPLACE_REMAINDERF = @REPLACE_REMAINDERF@
+REPLACE_REMAINDERL = @REPLACE_REMAINDERL@
REPLACE_REMOVE = @REPLACE_REMOVE@
REPLACE_RENAME = @REPLACE_RENAME@
REPLACE_RENAMEAT = @REPLACE_RENAMEAT@
+<<<<<<< HEAD
+REPLACE_RINTL = @REPLACE_RINTL@
+=======
REPLACE_REWINDDIR = @REPLACE_REWINDDIR@
+>>>>>>> origin/master
REPLACE_RMDIR = @REPLACE_RMDIR@
+REPLACE_ROUND = @REPLACE_ROUND@
+REPLACE_ROUNDF = @REPLACE_ROUNDF@
+REPLACE_ROUNDL = @REPLACE_ROUNDL@
REPLACE_SELECT = @REPLACE_SELECT@
REPLACE_SETENV = @REPLACE_SETENV@
REPLACE_SETHOSTNAME = @REPLACE_SETHOSTNAME@
REPLACE_SETSTATE = @REPLACE_SETSTATE@
+REPLACE_SIGNBIT = @REPLACE_SIGNBIT@
+REPLACE_SIGNBIT_USING_BUILTINS = @REPLACE_SIGNBIT_USING_BUILTINS@
+REPLACE_SINF = @REPLACE_SINF@
+REPLACE_SINHF = @REPLACE_SINHF@
REPLACE_SLEEP = @REPLACE_SLEEP@
REPLACE_SNPRINTF = @REPLACE_SNPRINTF@
REPLACE_SPRINTF = @REPLACE_SPRINTF@
+REPLACE_SQRTF = @REPLACE_SQRTF@
+REPLACE_SQRTL = @REPLACE_SQRTL@
REPLACE_STAT = @REPLACE_STAT@
REPLACE_STDIO_READ_FUNCS = @REPLACE_STDIO_READ_FUNCS@
REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@
@@ -1219,11 +1546,16 @@ REPLACE_STRTOUMAX = @REPLACE_STRTOUMAX@
REPLACE_STRUCT_TIMEVAL = @REPLACE_STRUCT_TIMEVAL@
REPLACE_SYMLINK = @REPLACE_SYMLINK@
REPLACE_SYMLINKAT = @REPLACE_SYMLINKAT@
+REPLACE_TANF = @REPLACE_TANF@
+REPLACE_TANHF = @REPLACE_TANHF@
REPLACE_TIME = @REPLACE_TIME@
REPLACE_TIMEGM = @REPLACE_TIMEGM@
REPLACE_TIMESPEC_GET = @REPLACE_TIMESPEC_GET@
REPLACE_TMPFILE = @REPLACE_TMPFILE@
+REPLACE_TRUNC = @REPLACE_TRUNC@
REPLACE_TRUNCATE = @REPLACE_TRUNCATE@
+REPLACE_TRUNCF = @REPLACE_TRUNCF@
+REPLACE_TRUNCL = @REPLACE_TRUNCL@
REPLACE_TTYNAME_R = @REPLACE_TTYNAME_R@
REPLACE_TZSET = @REPLACE_TZSET@
REPLACE_UNLINK = @REPLACE_UNLINK@
@@ -1242,6 +1574,7 @@ REPLACE_WRITE = @REPLACE_WRITE@
REPLACE__EXIT = @REPLACE__EXIT@
RSVG_CFLAGS = @RSVG_CFLAGS@
RSVG_LIBS = @RSVG_LIBS@
+SDK_BUILD_TOOLS = @SDK_BUILD_TOOLS@
SEPCHAR = @SEPCHAR@
SETFATTR = @SETFATTR@
SETTINGS_CFLAGS = @SETTINGS_CFLAGS@
@@ -1251,6 +1584,7 @@ SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@
SIZEOF_LONG = @SIZEOF_LONG@
SIZE_T_SUFFIX = @SIZE_T_SUFFIX@
SMALL_JA_DIC = @SMALL_JA_DIC@
+SQLITE3_CFLAGS = @SQLITE3_CFLAGS@
SQLITE3_LIBS = @SQLITE3_LIBS@
STDCKDINT_H = @STDCKDINT_H@
STDDEF_H = @STDDEF_H@
@@ -1259,6 +1593,7 @@ SUBDIR_MAKEFILES_IN = @SUBDIR_MAKEFILES_IN@
SYSTEM_TYPE = @SYSTEM_TYPE@
SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@
TERMCAP_OBJ = @TERMCAP_OBJ@
+TIFF_CFLAGS = @TIFF_CFLAGS@
TIMER_TIME_LIB = @TIMER_TIME_LIB@
TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@
TIME_H_DEFINES_TIME_UTC = @TIME_H_DEFINES_TIME_UTC@
@@ -1280,6 +1615,7 @@ W32_LIBS = @W32_LIBS@
W32_OBJ = @W32_OBJ@
W32_RES_LINK = @W32_RES_LINK@
WARN_CFLAGS = @WARN_CFLAGS@
+WARN_JAVAFLAGS = @WARN_JAVAFLAGS@
WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@
WEBKIT_CFLAGS = @WEBKIT_CFLAGS@
WEBKIT_LIBS = @WEBKIT_LIBS@
@@ -1298,6 +1634,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@
@@ -1322,6 +1659,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@
@@ -1352,6 +1690,7 @@ gamegroup = @gamegroup@
gameuser = @gameuser@
gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7_CONDITION = @gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7_CONDITION@
gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b_CONDITION = @gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b_CONDITION@
+gl_GNULIB_ENABLED_3f0e593033d1fc2c127581960f641b66_CONDITION = @gl_GNULIB_ENABLED_3f0e593033d1fc2c127581960f641b66_CONDITION@
gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31_CONDITION = @gl_GNULIB_ENABLED_5264294aa0a5557541b53c8c741f7f31_CONDITION@
gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c_CONDITION = @gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c_CONDITION@
gl_GNULIB_ENABLED_61bcaca76b3e6f9ae55d57a1c3193bc4_CONDITION = @gl_GNULIB_ENABLED_61bcaca76b3e6f9ae55d57a1c3193bc4_CONDITION@
@@ -1364,16 +1703,22 @@ gl_GNULIB_ENABLED_cloexec_CONDITION = @gl_GNULIB_ENABLED_cloexec_CONDITION@
gl_GNULIB_ENABLED_d3b2383720ee0e541357aa2aac598e2b_CONDITION = @gl_GNULIB_ENABLED_d3b2383720ee0e541357aa2aac598e2b_CONDITION@
gl_GNULIB_ENABLED_dirfd_CONDITION = @gl_GNULIB_ENABLED_dirfd_CONDITION@
gl_GNULIB_ENABLED_e80bf6f757095d2e5fc94dafb8f8fc8b_CONDITION = @gl_GNULIB_ENABLED_e80bf6f757095d2e5fc94dafb8f8fc8b_CONDITION@
+gl_GNULIB_ENABLED_ed5616be3593d355b981ffab56b9f37b_CONDITION = @gl_GNULIB_ENABLED_ed5616be3593d355b981ffab56b9f37b_CONDITION@
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_fseterr_CONDITION = @gl_GNULIB_ENABLED_fseterr_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@
gl_GNULIB_ENABLED_open_CONDITION = @gl_GNULIB_ENABLED_open_CONDITION@
gl_GNULIB_ENABLED_rawmemchr_CONDITION = @gl_GNULIB_ENABLED_rawmemchr_CONDITION@
+gl_GNULIB_ENABLED_size_max_CONDITION = @gl_GNULIB_ENABLED_size_max_CONDITION@
gl_GNULIB_ENABLED_strtoll_CONDITION = @gl_GNULIB_ENABLED_strtoll_CONDITION@
gl_GNULIB_ENABLED_utimens_CONDITION = @gl_GNULIB_ENABLED_utimens_CONDITION@
+gl_GNULIB_ENABLED_vasnprintf_CONDITION = @gl_GNULIB_ENABLED_vasnprintf_CONDITION@
+gl_GNULIB_ENABLED_xsize_CONDITION = @gl_GNULIB_ENABLED_xsize_CONDITION@
gl_LIBOBJDEPS = @gl_LIBOBJDEPS@
gl_LIBOBJS = @gl_LIBOBJS@
gl_LTLIBOBJS = @gl_LTLIBOBJS@
@@ -2041,6 +2386,42 @@ EXTRA_DIST += flexmember.h
endif
## end gnulib module flexmember
+## begin gnulib module float
+ifeq (,$(OMIT_GNULIB_MODULE_float))
+
+BUILT_SOURCES += $(FLOAT_H)
+
+# We need the following in order to create <float.h> when the system
+# doesn't have one that works with the given compiler.
+ifneq (,$(GL_GENERATE_FLOAT_H_CONDITION))
+float.h: float.in.h $(top_builddir)/config.status
+ $(gl_V_at)$(SED_HEADER_STDOUT) \
+ -e 's|@''GUARD_PREFIX''@|GL|g' \
+ -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \
+ -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+ -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+ -e 's|@''NEXT_FLOAT_H''@|$(NEXT_FLOAT_H)|g' \
+ -e 's|@''REPLACE_ITOLD''@|$(REPLACE_ITOLD)|g' \
+ $(srcdir)/float.in.h > $@-t
+ $(AM_V_at)mv $@-t $@
+else
+float.h: $(top_builddir)/config.status
+ rm -f $@
+endif
+MOSTLYCLEANFILES += float.h float.h-t
+
+ifneq (,$(GL_COND_OBJ_FLOAT_CONDITION))
+libgnu_a_SOURCES += float.c
+endif
+ifneq (,$(GL_COND_OBJ_ITOLD_CONDITION))
+libgnu_a_SOURCES += itold.c
+endif
+
+EXTRA_DIST += float.in.h
+
+endif
+## end gnulib module float
+
## begin gnulib module fpending
ifeq (,$(OMIT_GNULIB_MODULE_fpending))
@@ -2053,6 +2434,15 @@ EXTRA_DIST += fpending.h stdio-impl.h
endif
## end gnulib module fpending
+## begin gnulib module fpucw
+ifeq (,$(OMIT_GNULIB_MODULE_fpucw))
+
+
+EXTRA_DIST += fpucw.h
+
+endif
+## end gnulib module fpucw
+
## begin gnulib module free-posix
ifeq (,$(OMIT_GNULIB_MODULE_free-posix))
@@ -2063,6 +2453,42 @@ endif
endif
## end gnulib module free-posix
+## begin gnulib module frexp-nolibm
+ifeq (,$(OMIT_GNULIB_MODULE_frexp-nolibm))
+
+
+EXTRA_DIST += frexp.c
+
+EXTRA_libgnu_a_SOURCES += frexp.c
+
+endif
+## end gnulib module frexp-nolibm
+
+## begin gnulib module frexpl-nolibm
+ifeq (,$(OMIT_GNULIB_MODULE_frexpl-nolibm))
+
+
+EXTRA_DIST += frexp.c frexpl.c
+
+EXTRA_libgnu_a_SOURCES += frexp.c frexpl.c
+
+endif
+## end gnulib module frexpl-nolibm
+
+## begin gnulib module fseterr
+ifeq (,$(OMIT_GNULIB_MODULE_fseterr))
+
+ifneq (,$(gl_GNULIB_ENABLED_fseterr_CONDITION))
+ifneq (,$(GL_COND_OBJ_FSETERR_CONDITION))
+libgnu_a_SOURCES += fseterr.c
+endif
+
+endif
+EXTRA_DIST += fseterr.h stdio-impl.h
+
+endif
+## end gnulib module fseterr
+
## begin gnulib module fstatat
ifeq (,$(OMIT_GNULIB_MODULE_fstatat))
@@ -2130,6 +2556,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))
@@ -2154,6 +2592,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))
@@ -2426,6 +2874,41 @@ EXTRA_DIST += inttypes.in.h
endif
## end gnulib module inttypes-incomplete
+## begin gnulib module isnand-nolibm
+ifeq (,$(OMIT_GNULIB_MODULE_isnand-nolibm))
+
+
+EXTRA_DIST += float+.h isnan.c isnand-nolibm.h isnand.c
+
+EXTRA_libgnu_a_SOURCES += isnan.c isnand.c
+
+endif
+## end gnulib module isnand-nolibm
+
+## begin gnulib module isnanf-nolibm
+ifeq (,$(OMIT_GNULIB_MODULE_isnanf-nolibm))
+
+ifneq (,$(gl_GNULIB_ENABLED_3f0e593033d1fc2c127581960f641b66_CONDITION))
+
+endif
+EXTRA_DIST += float+.h isnan.c isnanf-nolibm.h isnanf.c
+
+EXTRA_libgnu_a_SOURCES += isnan.c isnanf.c
+
+endif
+## end gnulib module isnanf-nolibm
+
+## begin gnulib module isnanl-nolibm
+ifeq (,$(OMIT_GNULIB_MODULE_isnanl-nolibm))
+
+
+EXTRA_DIST += float+.h isnan.c isnanl-nolibm.h isnanl.c
+
+EXTRA_libgnu_a_SOURCES += isnan.c isnanl.c
+
+endif
+## end gnulib module isnanl-nolibm
+
## begin gnulib module lchmod
ifeq (,$(OMIT_GNULIB_MODULE_lchmod))
@@ -2546,6 +3029,313 @@ EXTRA_libgnu_a_SOURCES += malloc.c
endif
## end gnulib module malloc-posix
+## begin gnulib module math
+ifeq (,$(OMIT_GNULIB_MODULE_math))
+
+BUILT_SOURCES += math.h
+libgnu_a_SOURCES += math.c
+
+# We need the following in order to create <math.h> when the system
+# doesn't have one that works with the given compiler.
+math.h: math.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNULL_H) $(WARN_ON_USE_H)
+ $(gl_V_at)$(SED_HEADER_STDOUT) \
+ -e 's|@''GUARD_PREFIX''@|GL|g' \
+ -e 's|@''INCLUDE_NEXT_AS_FIRST_DIRECTIVE''@|$(INCLUDE_NEXT_AS_FIRST_DIRECTIVE)|g' \
+ -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \
+ -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \
+ -e 's|@''NEXT_AS_FIRST_DIRECTIVE_MATH_H''@|$(NEXT_AS_FIRST_DIRECTIVE_MATH_H)|g' \
+ -e 's/@''GNULIB_ACOSF''@/$(GL_GNULIB_ACOSF)/g' \
+ -e 's/@''GNULIB_ACOSL''@/$(GL_GNULIB_ACOSL)/g' \
+ -e 's/@''GNULIB_ASINF''@/$(GL_GNULIB_ASINF)/g' \
+ -e 's/@''GNULIB_ASINL''@/$(GL_GNULIB_ASINL)/g' \
+ -e 's/@''GNULIB_ATANF''@/$(GL_GNULIB_ATANF)/g' \
+ -e 's/@''GNULIB_ATANL''@/$(GL_GNULIB_ATANL)/g' \
+ -e 's/@''GNULIB_ATAN2F''@/$(GL_GNULIB_ATAN2F)/g' \
+ -e 's/@''GNULIB_CBRT''@/$(GL_GNULIB_CBRT)/g' \
+ -e 's/@''GNULIB_CBRTF''@/$(GL_GNULIB_CBRTF)/g' \
+ -e 's/@''GNULIB_CBRTL''@/$(GL_GNULIB_CBRTL)/g' \
+ -e 's/@''GNULIB_CEIL''@/$(GL_GNULIB_CEIL)/g' \
+ -e 's/@''GNULIB_CEILF''@/$(GL_GNULIB_CEILF)/g' \
+ -e 's/@''GNULIB_CEILL''@/$(GL_GNULIB_CEILL)/g' \
+ -e 's/@''GNULIB_COPYSIGN''@/$(GL_GNULIB_COPYSIGN)/g' \
+ -e 's/@''GNULIB_COPYSIGNF''@/$(GL_GNULIB_COPYSIGNF)/g' \
+ -e 's/@''GNULIB_COPYSIGNL''@/$(GL_GNULIB_COPYSIGNL)/g' \
+ -e 's/@''GNULIB_COSF''@/$(GL_GNULIB_COSF)/g' \
+ -e 's/@''GNULIB_COSL''@/$(GL_GNULIB_COSL)/g' \
+ -e 's/@''GNULIB_COSHF''@/$(GL_GNULIB_COSHF)/g' \
+ -e 's/@''GNULIB_EXPF''@/$(GL_GNULIB_EXPF)/g' \
+ -e 's/@''GNULIB_EXPL''@/$(GL_GNULIB_EXPL)/g' \
+ -e 's/@''GNULIB_EXP2''@/$(GL_GNULIB_EXP2)/g' \
+ -e 's/@''GNULIB_EXP2F''@/$(GL_GNULIB_EXP2F)/g' \
+ -e 's/@''GNULIB_EXP2L''@/$(GL_GNULIB_EXP2L)/g' \
+ -e 's/@''GNULIB_EXPM1''@/$(GL_GNULIB_EXPM1)/g' \
+ -e 's/@''GNULIB_EXPM1F''@/$(GL_GNULIB_EXPM1F)/g' \
+ -e 's/@''GNULIB_EXPM1L''@/$(GL_GNULIB_EXPM1L)/g' \
+ -e 's/@''GNULIB_FABSF''@/$(GL_GNULIB_FABSF)/g' \
+ -e 's/@''GNULIB_FABSL''@/$(GL_GNULIB_FABSL)/g' \
+ -e 's/@''GNULIB_FLOOR''@/$(GL_GNULIB_FLOOR)/g' \
+ -e 's/@''GNULIB_FLOORF''@/$(GL_GNULIB_FLOORF)/g' \
+ -e 's/@''GNULIB_FLOORL''@/$(GL_GNULIB_FLOORL)/g' \
+ -e 's/@''GNULIB_FMA''@/$(GL_GNULIB_FMA)/g' \
+ -e 's/@''GNULIB_FMAF''@/$(GL_GNULIB_FMAF)/g' \
+ -e 's/@''GNULIB_FMAL''@/$(GL_GNULIB_FMAL)/g' \
+ -e 's/@''GNULIB_FMOD''@/$(GL_GNULIB_FMOD)/g' \
+ -e 's/@''GNULIB_FMODF''@/$(GL_GNULIB_FMODF)/g' \
+ -e 's/@''GNULIB_FMODL''@/$(GL_GNULIB_FMODL)/g' \
+ -e 's/@''GNULIB_FREXPF''@/$(GL_GNULIB_FREXPF)/g' \
+ -e 's/@''GNULIB_FREXP''@/$(GL_GNULIB_FREXP)/g' \
+ -e 's/@''GNULIB_FREXPL''@/$(GL_GNULIB_FREXPL)/g' \
+ -e 's/@''GNULIB_HYPOT''@/$(GL_GNULIB_HYPOT)/g' \
+ -e 's/@''GNULIB_HYPOTF''@/$(GL_GNULIB_HYPOTF)/g' \
+ -e 's/@''GNULIB_HYPOTL''@/$(GL_GNULIB_HYPOTL)/g' \
+ < $(srcdir)/math.in.h | \
+ sed -e 's/@''GNULIB_ILOGB''@/$(GL_GNULIB_ILOGB)/g' \
+ -e 's/@''GNULIB_ILOGBF''@/$(GL_GNULIB_ILOGBF)/g' \
+ -e 's/@''GNULIB_ILOGBL''@/$(GL_GNULIB_ILOGBL)/g' \
+ -e 's/@''GNULIB_ISFINITE''@/$(GL_GNULIB_ISFINITE)/g' \
+ -e 's/@''GNULIB_ISINF''@/$(GL_GNULIB_ISINF)/g' \
+ -e 's/@''GNULIB_ISNAN''@/$(GL_GNULIB_ISNAN)/g' \
+ -e 's/@''GNULIB_ISNANF''@/$(GL_GNULIB_ISNANF)/g' \
+ -e 's/@''GNULIB_ISNAND''@/$(GL_GNULIB_ISNAND)/g' \
+ -e 's/@''GNULIB_ISNANL''@/$(GL_GNULIB_ISNANL)/g' \
+ -e 's/@''GNULIB_LDEXPF''@/$(GL_GNULIB_LDEXPF)/g' \
+ -e 's/@''GNULIB_LDEXPL''@/$(GL_GNULIB_LDEXPL)/g' \
+ -e 's/@''GNULIB_LOG''@/$(GL_GNULIB_LOG)/g' \
+ -e 's/@''GNULIB_LOGF''@/$(GL_GNULIB_LOGF)/g' \
+ -e 's/@''GNULIB_LOGL''@/$(GL_GNULIB_LOGL)/g' \
+ -e 's/@''GNULIB_LOG10''@/$(GL_GNULIB_LOG10)/g' \
+ -e 's/@''GNULIB_LOG10F''@/$(GL_GNULIB_LOG10F)/g' \
+ -e 's/@''GNULIB_LOG10L''@/$(GL_GNULIB_LOG10L)/g' \
+ -e 's/@''GNULIB_LOG1P''@/$(GL_GNULIB_LOG1P)/g' \
+ -e 's/@''GNULIB_LOG1PF''@/$(GL_GNULIB_LOG1PF)/g' \
+ -e 's/@''GNULIB_LOG1PL''@/$(GL_GNULIB_LOG1PL)/g' \
+ -e 's/@''GNULIB_LOG2''@/$(GL_GNULIB_LOG2)/g' \
+ -e 's/@''GNULIB_LOG2F''@/$(GL_GNULIB_LOG2F)/g' \
+ -e 's/@''GNULIB_LOG2L''@/$(GL_GNULIB_LOG2L)/g' \
+ -e 's/@''GNULIB_LOGB''@/$(GL_GNULIB_LOGB)/g' \
+ -e 's/@''GNULIB_LOGBF''@/$(GL_GNULIB_LOGBF)/g' \
+ -e 's/@''GNULIB_LOGBL''@/$(GL_GNULIB_LOGBL)/g' \
+ -e 's/@''GNULIB_MODF''@/$(GL_GNULIB_MODF)/g' \
+ -e 's/@''GNULIB_MODFF''@/$(GL_GNULIB_MODFF)/g' \
+ -e 's/@''GNULIB_MODFL''@/$(GL_GNULIB_MODFL)/g' \
+ -e 's/@''GNULIB_POWF''@/$(GL_GNULIB_POWF)/g' \
+ -e 's/@''GNULIB_REMAINDER''@/$(GL_GNULIB_REMAINDER)/g' \
+ -e 's/@''GNULIB_REMAINDERF''@/$(GL_GNULIB_REMAINDERF)/g' \
+ -e 's/@''GNULIB_REMAINDERL''@/$(GL_GNULIB_REMAINDERL)/g' \
+ -e 's/@''GNULIB_RINT''@/$(GL_GNULIB_RINT)/g' \
+ -e 's/@''GNULIB_RINTF''@/$(GL_GNULIB_RINTF)/g' \
+ -e 's/@''GNULIB_RINTL''@/$(GL_GNULIB_RINTL)/g' \
+ -e 's/@''GNULIB_ROUND''@/$(GL_GNULIB_ROUND)/g' \
+ -e 's/@''GNULIB_ROUNDF''@/$(GL_GNULIB_ROUNDF)/g' \
+ -e 's/@''GNULIB_ROUNDL''@/$(GL_GNULIB_ROUNDL)/g' \
+ -e 's/@''GNULIB_SIGNBIT''@/$(GL_GNULIB_SIGNBIT)/g' \
+ -e 's/@''GNULIB_SINF''@/$(GL_GNULIB_SINF)/g' \
+ -e 's/@''GNULIB_SINL''@/$(GL_GNULIB_SINL)/g' \
+ -e 's/@''GNULIB_SINHF''@/$(GL_GNULIB_SINHF)/g' \
+ -e 's/@''GNULIB_SQRTF''@/$(GL_GNULIB_SQRTF)/g' \
+ -e 's/@''GNULIB_SQRTL''@/$(GL_GNULIB_SQRTL)/g' \
+ -e 's/@''GNULIB_TANF''@/$(GL_GNULIB_TANF)/g' \
+ -e 's/@''GNULIB_TANL''@/$(GL_GNULIB_TANL)/g' \
+ -e 's/@''GNULIB_TANHF''@/$(GL_GNULIB_TANHF)/g' \
+ -e 's/@''GNULIB_TRUNC''@/$(GL_GNULIB_TRUNC)/g' \
+ -e 's/@''GNULIB_TRUNCF''@/$(GL_GNULIB_TRUNCF)/g' \
+ -e 's/@''GNULIB_TRUNCL''@/$(GL_GNULIB_TRUNCL)/g' \
+ -e 's/@''GNULIB_MDA_J0''@/$(GL_GNULIB_MDA_J0)/g' \
+ -e 's/@''GNULIB_MDA_J1''@/$(GL_GNULIB_MDA_J1)/g' \
+ -e 's/@''GNULIB_MDA_JN''@/$(GL_GNULIB_MDA_JN)/g' \
+ -e 's/@''GNULIB_MDA_Y0''@/$(GL_GNULIB_MDA_Y0)/g' \
+ -e 's/@''GNULIB_MDA_Y1''@/$(GL_GNULIB_MDA_Y1)/g' \
+ -e 's/@''GNULIB_MDA_YN''@/$(GL_GNULIB_MDA_YN)/g' \
+ | \
+ sed -e 's|@''HAVE_ACOSF''@|$(HAVE_ACOSF)|g' \
+ -e 's|@''HAVE_ACOSL''@|$(HAVE_ACOSL)|g' \
+ -e 's|@''HAVE_ASINF''@|$(HAVE_ASINF)|g' \
+ -e 's|@''HAVE_ASINL''@|$(HAVE_ASINL)|g' \
+ -e 's|@''HAVE_ATANF''@|$(HAVE_ATANF)|g' \
+ -e 's|@''HAVE_ATANL''@|$(HAVE_ATANL)|g' \
+ -e 's|@''HAVE_ATAN2F''@|$(HAVE_ATAN2F)|g' \
+ -e 's|@''HAVE_CBRT''@|$(HAVE_CBRT)|g' \
+ -e 's|@''HAVE_CBRTF''@|$(HAVE_CBRTF)|g' \
+ -e 's|@''HAVE_CBRTL''@|$(HAVE_CBRTL)|g' \
+ -e 's|@''HAVE_COPYSIGN''@|$(HAVE_COPYSIGN)|g' \
+ -e 's|@''HAVE_COPYSIGNL''@|$(HAVE_COPYSIGNL)|g' \
+ -e 's|@''HAVE_COSF''@|$(HAVE_COSF)|g' \
+ -e 's|@''HAVE_COSL''@|$(HAVE_COSL)|g' \
+ -e 's|@''HAVE_COSHF''@|$(HAVE_COSHF)|g' \
+ -e 's|@''HAVE_EXPF''@|$(HAVE_EXPF)|g' \
+ -e 's|@''HAVE_EXPL''@|$(HAVE_EXPL)|g' \
+ -e 's|@''HAVE_EXPM1''@|$(HAVE_EXPM1)|g' \
+ -e 's|@''HAVE_EXPM1F''@|$(HAVE_EXPM1F)|g' \
+ -e 's|@''HAVE_FABSF''@|$(HAVE_FABSF)|g' \
+ -e 's|@''HAVE_FABSL''@|$(HAVE_FABSL)|g' \
+ -e 's|@''HAVE_FMA''@|$(HAVE_FMA)|g' \
+ -e 's|@''HAVE_FMAF''@|$(HAVE_FMAF)|g' \
+ -e 's|@''HAVE_FMAL''@|$(HAVE_FMAL)|g' \
+ -e 's|@''HAVE_FMODF''@|$(HAVE_FMODF)|g' \
+ -e 's|@''HAVE_FMODL''@|$(HAVE_FMODL)|g' \
+ -e 's|@''HAVE_FREXPF''@|$(HAVE_FREXPF)|g' \
+ -e 's|@''HAVE_HYPOTF''@|$(HAVE_HYPOTF)|g' \
+ -e 's|@''HAVE_HYPOTL''@|$(HAVE_HYPOTL)|g' \
+ -e 's|@''HAVE_ILOGB''@|$(HAVE_ILOGB)|g' \
+ -e 's|@''HAVE_ILOGBF''@|$(HAVE_ILOGBF)|g' \
+ -e 's|@''HAVE_ILOGBL''@|$(HAVE_ILOGBL)|g' \
+ -e 's|@''HAVE_ISNANF''@|$(HAVE_ISNANF)|g' \
+ -e 's|@''HAVE_ISNAND''@|$(HAVE_ISNAND)|g' \
+ -e 's|@''HAVE_ISNANL''@|$(HAVE_ISNANL)|g' \
+ -e 's|@''HAVE_LDEXPF''@|$(HAVE_LDEXPF)|g' \
+ -e 's|@''HAVE_LOGF''@|$(HAVE_LOGF)|g' \
+ -e 's|@''HAVE_LOGL''@|$(HAVE_LOGL)|g' \
+ -e 's|@''HAVE_LOG10F''@|$(HAVE_LOG10F)|g' \
+ -e 's|@''HAVE_LOG10L''@|$(HAVE_LOG10L)|g' \
+ -e 's|@''HAVE_LOG1P''@|$(HAVE_LOG1P)|g' \
+ -e 's|@''HAVE_LOG1PF''@|$(HAVE_LOG1PF)|g' \
+ -e 's|@''HAVE_LOG1PL''@|$(HAVE_LOG1PL)|g' \
+ -e 's|@''HAVE_LOGBF''@|$(HAVE_LOGBF)|g' \
+ -e 's|@''HAVE_LOGBL''@|$(HAVE_LOGBL)|g' \
+ -e 's|@''HAVE_MODFF''@|$(HAVE_MODFF)|g' \
+ -e 's|@''HAVE_MODFL''@|$(HAVE_MODFL)|g' \
+ -e 's|@''HAVE_POWF''@|$(HAVE_POWF)|g' \
+ -e 's|@''HAVE_REMAINDER''@|$(HAVE_REMAINDER)|g' \
+ -e 's|@''HAVE_REMAINDERF''@|$(HAVE_REMAINDERF)|g' \
+ -e 's|@''HAVE_RINT''@|$(HAVE_RINT)|g' \
+ -e 's|@''HAVE_RINTL''@|$(HAVE_RINTL)|g' \
+ -e 's|@''HAVE_SINF''@|$(HAVE_SINF)|g' \
+ -e 's|@''HAVE_SINL''@|$(HAVE_SINL)|g' \
+ -e 's|@''HAVE_SINHF''@|$(HAVE_SINHF)|g' \
+ -e 's|@''HAVE_SQRTF''@|$(HAVE_SQRTF)|g' \
+ -e 's|@''HAVE_SQRTL''@|$(HAVE_SQRTL)|g' \
+ -e 's|@''HAVE_TANF''@|$(HAVE_TANF)|g' \
+ -e 's|@''HAVE_TANL''@|$(HAVE_TANL)|g' \
+ -e 's|@''HAVE_TANHF''@|$(HAVE_TANHF)|g' \
+ -e 's|@''HAVE_DECL_ACOSL''@|$(HAVE_DECL_ACOSL)|g' \
+ -e 's|@''HAVE_DECL_ASINL''@|$(HAVE_DECL_ASINL)|g' \
+ -e 's|@''HAVE_DECL_ATANL''@|$(HAVE_DECL_ATANL)|g' \
+ -e 's|@''HAVE_DECL_CBRTF''@|$(HAVE_DECL_CBRTF)|g' \
+ -e 's|@''HAVE_DECL_CBRTL''@|$(HAVE_DECL_CBRTL)|g' \
+ -e 's|@''HAVE_DECL_CEILF''@|$(HAVE_DECL_CEILF)|g' \
+ -e 's|@''HAVE_DECL_CEILL''@|$(HAVE_DECL_CEILL)|g' \
+ -e 's|@''HAVE_DECL_COPYSIGNF''@|$(HAVE_DECL_COPYSIGNF)|g' \
+ -e 's|@''HAVE_DECL_COSL''@|$(HAVE_DECL_COSL)|g' \
+ -e 's|@''HAVE_DECL_EXPL''@|$(HAVE_DECL_EXPL)|g' \
+ -e 's|@''HAVE_DECL_EXP2''@|$(HAVE_DECL_EXP2)|g' \
+ -e 's|@''HAVE_DECL_EXP2F''@|$(HAVE_DECL_EXP2F)|g' \
+ -e 's|@''HAVE_DECL_EXP2L''@|$(HAVE_DECL_EXP2L)|g' \
+ -e 's|@''HAVE_DECL_EXPM1L''@|$(HAVE_DECL_EXPM1L)|g' \
+ -e 's|@''HAVE_DECL_FLOORF''@|$(HAVE_DECL_FLOORF)|g' \
+ -e 's|@''HAVE_DECL_FLOORL''@|$(HAVE_DECL_FLOORL)|g' \
+ -e 's|@''HAVE_DECL_FREXPL''@|$(HAVE_DECL_FREXPL)|g' \
+ -e 's|@''HAVE_DECL_LDEXPL''@|$(HAVE_DECL_LDEXPL)|g' \
+ -e 's|@''HAVE_DECL_LOGL''@|$(HAVE_DECL_LOGL)|g' \
+ -e 's|@''HAVE_DECL_LOG10L''@|$(HAVE_DECL_LOG10L)|g' \
+ -e 's|@''HAVE_DECL_LOG2''@|$(HAVE_DECL_LOG2)|g' \
+ -e 's|@''HAVE_DECL_LOG2F''@|$(HAVE_DECL_LOG2F)|g' \
+ -e 's|@''HAVE_DECL_LOG2L''@|$(HAVE_DECL_LOG2L)|g' \
+ -e 's|@''HAVE_DECL_LOGB''@|$(HAVE_DECL_LOGB)|g' \
+ -e 's|@''HAVE_DECL_REMAINDER''@|$(HAVE_DECL_REMAINDER)|g' \
+ -e 's|@''HAVE_DECL_REMAINDERL''@|$(HAVE_DECL_REMAINDERL)|g' \
+ -e 's|@''HAVE_DECL_RINTF''@|$(HAVE_DECL_RINTF)|g' \
+ -e 's|@''HAVE_DECL_ROUND''@|$(HAVE_DECL_ROUND)|g' \
+ -e 's|@''HAVE_DECL_ROUNDF''@|$(HAVE_DECL_ROUNDF)|g' \
+ -e 's|@''HAVE_DECL_ROUNDL''@|$(HAVE_DECL_ROUNDL)|g' \
+ -e 's|@''HAVE_DECL_SINL''@|$(HAVE_DECL_SINL)|g' \
+ -e 's|@''HAVE_DECL_SQRTL''@|$(HAVE_DECL_SQRTL)|g' \
+ -e 's|@''HAVE_DECL_TANL''@|$(HAVE_DECL_TANL)|g' \
+ -e 's|@''HAVE_DECL_TRUNC''@|$(HAVE_DECL_TRUNC)|g' \
+ -e 's|@''HAVE_DECL_TRUNCF''@|$(HAVE_DECL_TRUNCF)|g' \
+ -e 's|@''HAVE_DECL_TRUNCL''@|$(HAVE_DECL_TRUNCL)|g' \
+ | \
+ sed -e 's|@''REPLACE_ACOSF''@|$(REPLACE_ACOSF)|g' \
+ -e 's|@''REPLACE_ASINF''@|$(REPLACE_ASINF)|g' \
+ -e 's|@''REPLACE_ATANF''@|$(REPLACE_ATANF)|g' \
+ -e 's|@''REPLACE_ATAN2F''@|$(REPLACE_ATAN2F)|g' \
+ -e 's|@''REPLACE_CBRTF''@|$(REPLACE_CBRTF)|g' \
+ -e 's|@''REPLACE_CBRTL''@|$(REPLACE_CBRTL)|g' \
+ -e 's|@''REPLACE_CEIL''@|$(REPLACE_CEIL)|g' \
+ -e 's|@''REPLACE_CEILF''@|$(REPLACE_CEILF)|g' \
+ -e 's|@''REPLACE_CEILL''@|$(REPLACE_CEILL)|g' \
+ -e 's|@''REPLACE_COSF''@|$(REPLACE_COSF)|g' \
+ -e 's|@''REPLACE_COSHF''@|$(REPLACE_COSHF)|g' \
+ -e 's|@''REPLACE_EXPF''@|$(REPLACE_EXPF)|g' \
+ -e 's|@''REPLACE_EXPL''@|$(REPLACE_EXPL)|g' \
+ -e 's|@''REPLACE_EXPM1''@|$(REPLACE_EXPM1)|g' \
+ -e 's|@''REPLACE_EXPM1F''@|$(REPLACE_EXPM1F)|g' \
+ -e 's|@''REPLACE_EXPM1L''@|$(REPLACE_EXPM1L)|g' \
+ -e 's|@''REPLACE_EXP2''@|$(REPLACE_EXP2)|g' \
+ -e 's|@''REPLACE_EXP2L''@|$(REPLACE_EXP2L)|g' \
+ -e 's|@''REPLACE_FABSL''@|$(REPLACE_FABSL)|g' \
+ -e 's|@''REPLACE_FLOOR''@|$(REPLACE_FLOOR)|g' \
+ -e 's|@''REPLACE_FLOORF''@|$(REPLACE_FLOORF)|g' \
+ -e 's|@''REPLACE_FLOORL''@|$(REPLACE_FLOORL)|g' \
+ -e 's|@''REPLACE_FMA''@|$(REPLACE_FMA)|g' \
+ -e 's|@''REPLACE_FMAF''@|$(REPLACE_FMAF)|g' \
+ -e 's|@''REPLACE_FMAL''@|$(REPLACE_FMAL)|g' \
+ -e 's|@''REPLACE_FMOD''@|$(REPLACE_FMOD)|g' \
+ -e 's|@''REPLACE_FMODF''@|$(REPLACE_FMODF)|g' \
+ -e 's|@''REPLACE_FMODL''@|$(REPLACE_FMODL)|g' \
+ -e 's|@''REPLACE_FREXPF''@|$(REPLACE_FREXPF)|g' \
+ -e 's|@''REPLACE_FREXP''@|$(REPLACE_FREXP)|g' \
+ -e 's|@''REPLACE_FREXPL''@|$(REPLACE_FREXPL)|g' \
+ -e 's|@''REPLACE_HUGE_VAL''@|$(REPLACE_HUGE_VAL)|g' \
+ -e 's|@''REPLACE_HYPOT''@|$(REPLACE_HYPOT)|g' \
+ -e 's|@''REPLACE_HYPOTF''@|$(REPLACE_HYPOTF)|g' \
+ -e 's|@''REPLACE_HYPOTL''@|$(REPLACE_HYPOTL)|g' \
+ -e 's|@''REPLACE_ILOGB''@|$(REPLACE_ILOGB)|g' \
+ -e 's|@''REPLACE_ILOGBF''@|$(REPLACE_ILOGBF)|g' \
+ -e 's|@''REPLACE_ILOGBL''@|$(REPLACE_ILOGBL)|g' \
+ -e 's|@''REPLACE_ISFINITE''@|$(REPLACE_ISFINITE)|g' \
+ -e 's|@''REPLACE_ISINF''@|$(REPLACE_ISINF)|g' \
+ -e 's|@''REPLACE_ISNAN''@|$(REPLACE_ISNAN)|g' \
+ -e 's|@''REPLACE_ITOLD''@|$(REPLACE_ITOLD)|g' \
+ -e 's|@''REPLACE_LDEXPL''@|$(REPLACE_LDEXPL)|g' \
+ -e 's|@''REPLACE_LOG''@|$(REPLACE_LOG)|g' \
+ -e 's|@''REPLACE_LOGF''@|$(REPLACE_LOGF)|g' \
+ -e 's|@''REPLACE_LOGL''@|$(REPLACE_LOGL)|g' \
+ -e 's|@''REPLACE_LOG10''@|$(REPLACE_LOG10)|g' \
+ -e 's|@''REPLACE_LOG10F''@|$(REPLACE_LOG10F)|g' \
+ -e 's|@''REPLACE_LOG10L''@|$(REPLACE_LOG10L)|g' \
+ -e 's|@''REPLACE_LOG1P''@|$(REPLACE_LOG1P)|g' \
+ -e 's|@''REPLACE_LOG1PF''@|$(REPLACE_LOG1PF)|g' \
+ -e 's|@''REPLACE_LOG1PL''@|$(REPLACE_LOG1PL)|g' \
+ -e 's|@''REPLACE_LOG2''@|$(REPLACE_LOG2)|g' \
+ -e 's|@''REPLACE_LOG2F''@|$(REPLACE_LOG2F)|g' \
+ -e 's|@''REPLACE_LOG2L''@|$(REPLACE_LOG2L)|g' \
+ -e 's|@''REPLACE_LOGB''@|$(REPLACE_LOGB)|g' \
+ -e 's|@''REPLACE_LOGBF''@|$(REPLACE_LOGBF)|g' \
+ -e 's|@''REPLACE_LOGBL''@|$(REPLACE_LOGBL)|g' \
+ -e 's|@''REPLACE_MODF''@|$(REPLACE_MODF)|g' \
+ -e 's|@''REPLACE_MODFF''@|$(REPLACE_MODFF)|g' \
+ -e 's|@''REPLACE_MODFL''@|$(REPLACE_MODFL)|g' \
+ -e 's|@''REPLACE_NAN''@|$(REPLACE_NAN)|g' \
+ -e 's|@''REPLACE_REMAINDER''@|$(REPLACE_REMAINDER)|g' \
+ -e 's|@''REPLACE_REMAINDERF''@|$(REPLACE_REMAINDERF)|g' \
+ -e 's|@''REPLACE_REMAINDERL''@|$(REPLACE_REMAINDERL)|g' \
+ -e 's|@''REPLACE_RINTL''@|$(REPLACE_RINTL)|g' \
+ -e 's|@''REPLACE_ROUND''@|$(REPLACE_ROUND)|g' \
+ -e 's|@''REPLACE_ROUNDF''@|$(REPLACE_ROUNDF)|g' \
+ -e 's|@''REPLACE_ROUNDL''@|$(REPLACE_ROUNDL)|g' \
+ -e 's|@''REPLACE_SIGNBIT''@|$(REPLACE_SIGNBIT)|g' \
+ -e 's|@''REPLACE_SIGNBIT_USING_BUILTINS''@|$(REPLACE_SIGNBIT_USING_BUILTINS)|g' \
+ -e 's|@''REPLACE_SINF''@|$(REPLACE_SINF)|g' \
+ -e 's|@''REPLACE_SINHF''@|$(REPLACE_SINHF)|g' \
+ -e 's|@''REPLACE_SQRTF''@|$(REPLACE_SQRTF)|g' \
+ -e 's|@''REPLACE_SQRTL''@|$(REPLACE_SQRTL)|g' \
+ -e 's|@''REPLACE_TANF''@|$(REPLACE_TANF)|g' \
+ -e 's|@''REPLACE_TANHF''@|$(REPLACE_TANHF)|g' \
+ -e 's|@''REPLACE_TRUNC''@|$(REPLACE_TRUNC)|g' \
+ -e 's|@''REPLACE_TRUNCF''@|$(REPLACE_TRUNCF)|g' \
+ -e 's|@''REPLACE_TRUNCL''@|$(REPLACE_TRUNCL)|g' \
+ -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \
+ -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \
+ -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \
+ > $@-t
+ $(AM_V_at)mv $@-t $@
+MOSTLYCLEANFILES += math.h math.h-t
+
+EXTRA_DIST += math.in.h
+
+endif
+## end gnulib module math
+
## begin gnulib module memmem-simple
ifeq (,$(OMIT_GNULIB_MODULE_memmem-simple))
@@ -2699,6 +3489,39 @@ libgnu_a_SOURCES += pipe2.c
endif
## end gnulib module pipe2
+## begin gnulib module printf-frexp
+ifeq (,$(OMIT_GNULIB_MODULE_printf-frexp))
+
+libgnu_a_SOURCES += printf-frexp.c
+
+EXTRA_DIST += printf-frexp.h
+
+endif
+## end gnulib module printf-frexp
+
+## begin gnulib module printf-frexpl
+ifeq (,$(OMIT_GNULIB_MODULE_printf-frexpl))
+
+libgnu_a_SOURCES += printf-frexpl.c
+
+EXTRA_DIST += printf-frexp.c printf-frexpl.h
+
+EXTRA_libgnu_a_SOURCES += printf-frexp.c
+
+endif
+## end gnulib module printf-frexpl
+
+## begin gnulib module printf-posix
+ifeq (,$(OMIT_GNULIB_MODULE_printf-posix))
+
+
+EXTRA_DIST += printf.c
+
+EXTRA_libgnu_a_SOURCES += printf.c
+
+endif
+## end gnulib module printf-posix
+
## begin gnulib module pselect
ifeq (,$(OMIT_GNULIB_MODULE_pselect))
@@ -2880,6 +3703,28 @@ EXTRA_DIST += signal.in.h
endif
## end gnulib module signal-h
+## begin gnulib module signbit
+ifeq (,$(OMIT_GNULIB_MODULE_signbit))
+
+ifneq (,$(GL_COND_OBJ_SIGNBIT3_CONDITION))
+libgnu_a_SOURCES += signbitf.c signbitd.c signbitl.c
+endif
+
+EXTRA_DIST += float+.h
+
+endif
+## end gnulib module signbit
+
+## begin gnulib module size_max
+ifeq (,$(OMIT_GNULIB_MODULE_size_max))
+
+ifneq (,$(gl_GNULIB_ENABLED_size_max_CONDITION))
+libgnu_a_SOURCES += size_max.h
+
+endif
+endif
+## end gnulib module size_max
+
## begin gnulib module snippet/_Noreturn
ifeq (,$(OMIT_GNULIB_MODULE_snippet/_Noreturn))
@@ -3371,6 +4216,16 @@ endif
endif
## end gnulib module stpcpy
+## begin gnulib module stpncpy
+ifeq (,$(OMIT_GNULIB_MODULE_stpncpy))
+
+ifneq (,$(GL_COND_OBJ_STPNCPY_CONDITION))
+libgnu_a_SOURCES += stpncpy.c
+endif
+
+endif
+## end gnulib module stpncpy
+
## begin gnulib module string
ifeq (,$(OMIT_GNULIB_MODULE_string))
@@ -4136,6 +4991,30 @@ EXTRA_libgnu_a_SOURCES += at-func.c
endif
## end gnulib module utimensat
+## begin gnulib module vasnprintf
+ifeq (,$(OMIT_GNULIB_MODULE_vasnprintf))
+
+ifneq (,$(gl_GNULIB_ENABLED_vasnprintf_CONDITION))
+
+endif
+EXTRA_DIST += asnprintf.c float+.h printf-args.c printf-args.h printf-parse.c printf-parse.h vasnprintf.c vasnprintf.h
+
+EXTRA_libgnu_a_SOURCES += asnprintf.c printf-args.c printf-parse.c vasnprintf.c
+
+endif
+## end gnulib module vasnprintf
+
+## begin gnulib module vasprintf
+ifeq (,$(OMIT_GNULIB_MODULE_vasprintf))
+
+
+EXTRA_DIST += asprintf.c vasprintf.c
+
+EXTRA_libgnu_a_SOURCES += asprintf.c vasprintf.c
+
+endif
+## end gnulib module vasprintf
+
## begin gnulib module verify
ifeq (,$(OMIT_GNULIB_MODULE_verify))
@@ -4145,6 +5024,19 @@ EXTRA_DIST += verify.h
endif
## end gnulib module verify
+## begin gnulib module vfprintf-posix
+ifeq (,$(OMIT_GNULIB_MODULE_vfprintf-posix))
+
+ifneq (,$(gl_GNULIB_ENABLED_ed5616be3593d355b981ffab56b9f37b_CONDITION))
+
+endif
+EXTRA_DIST += vfprintf.c
+
+EXTRA_libgnu_a_SOURCES += vfprintf.c
+
+endif
+## end gnulib module vfprintf-posix
+
## begin gnulib module vla
ifeq (,$(OMIT_GNULIB_MODULE_vla))
@@ -4165,6 +5057,16 @@ EXTRA_DIST += xalloc-oversized.h
endif
## end gnulib module xalloc-oversized
+## begin gnulib module xsize
+ifeq (,$(OMIT_GNULIB_MODULE_xsize))
+
+ifneq (,$(gl_GNULIB_ENABLED_xsize_CONDITION))
+libgnu_a_SOURCES += xsize.h xsize.c
+
+endif
+endif
+## end gnulib module xsize
+
mostlyclean-local: mostlyclean-generic
@for dir in '' $(MOSTLYCLEANDIRS); do \
diff --git a/lib/isnan.c b/lib/isnan.c
new file mode 100644
index 00000000000..39e193b9606
--- /dev/null
+++ b/lib/isnan.c
@@ -0,0 +1,190 @@
+/* Test for NaN that does not need libm.
+ Copyright (C) 2007-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 Bruno Haible <bruno@clisp.org>, 2007. */
+
+#include <config.h>
+
+/* Specification. */
+#ifdef USE_LONG_DOUBLE
+/* Specification found in math.h or isnanl-nolibm.h. */
+extern int rpl_isnanl (long double x) _GL_ATTRIBUTE_CONST;
+#elif ! defined USE_FLOAT
+/* Specification found in math.h or isnand-nolibm.h. */
+extern int rpl_isnand (double x);
+#else /* defined USE_FLOAT */
+/* Specification found in math.h or isnanf-nolibm.h. */
+extern int rpl_isnanf (float x);
+#endif
+
+#include <float.h>
+#include <string.h>
+
+#include "float+.h"
+
+#ifdef USE_LONG_DOUBLE
+# define FUNC rpl_isnanl
+# define DOUBLE long double
+# define MAX_EXP LDBL_MAX_EXP
+# define MIN_EXP LDBL_MIN_EXP
+# if defined LDBL_EXPBIT0_WORD && defined LDBL_EXPBIT0_BIT
+# define KNOWN_EXPBIT0_LOCATION
+# define EXPBIT0_WORD LDBL_EXPBIT0_WORD
+# define EXPBIT0_BIT LDBL_EXPBIT0_BIT
+# endif
+# define SIZE SIZEOF_LDBL
+# define L_(literal) literal##L
+#elif ! defined USE_FLOAT
+# define FUNC rpl_isnand
+# define DOUBLE double
+# define MAX_EXP DBL_MAX_EXP
+# define MIN_EXP DBL_MIN_EXP
+# if defined DBL_EXPBIT0_WORD && defined DBL_EXPBIT0_BIT
+# define KNOWN_EXPBIT0_LOCATION
+# define EXPBIT0_WORD DBL_EXPBIT0_WORD
+# define EXPBIT0_BIT DBL_EXPBIT0_BIT
+# endif
+# define SIZE SIZEOF_DBL
+# define L_(literal) literal
+#else /* defined USE_FLOAT */
+# define FUNC rpl_isnanf
+# define DOUBLE float
+# define MAX_EXP FLT_MAX_EXP
+# define MIN_EXP FLT_MIN_EXP
+# if defined FLT_EXPBIT0_WORD && defined FLT_EXPBIT0_BIT
+# define KNOWN_EXPBIT0_LOCATION
+# define EXPBIT0_WORD FLT_EXPBIT0_WORD
+# define EXPBIT0_BIT FLT_EXPBIT0_BIT
+# endif
+# define SIZE SIZEOF_FLT
+# define L_(literal) literal##f
+#endif
+
+#define EXP_MASK ((MAX_EXP - MIN_EXP) | 7)
+
+#define NWORDS \
+ ((sizeof (DOUBLE) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+typedef union { DOUBLE value; unsigned int word[NWORDS]; } memory_double;
+
+/* Most hosts nowadays use IEEE floating point, so they use IEC 60559
+ representations, have infinities and NaNs, and do not trap on
+ exceptions. Define IEEE_FLOATING_POINT if this host is one of the
+ typical ones. The C23 macro __STDC_IEC_60559_BFP__ macro (or its cousin,
+ the now-obsolescent C11 macro __STDC_IEC_559__) is close to what is
+ wanted here, but is not quite right because this file does not require
+ all the features of C23 Annex F (and works even with pre-C11 platforms,
+ for that matter). */
+
+#define IEEE_FLOATING_POINT (FLT_RADIX == 2 && FLT_MANT_DIG == 24 \
+ && FLT_MIN_EXP == -125 && FLT_MAX_EXP == 128)
+
+int
+FUNC (DOUBLE x)
+{
+#if defined KNOWN_EXPBIT0_LOCATION && IEEE_FLOATING_POINT
+# if defined USE_LONG_DOUBLE && ((defined __ia64 && LDBL_MANT_DIG == 64) || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_)) && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE
+ /* Special CPU dependent code is needed to treat bit patterns outside the
+ IEEE 754 specification (such as Pseudo-NaNs, Pseudo-Infinities,
+ Pseudo-Zeroes, Unnormalized Numbers, and Pseudo-Denormals) as NaNs.
+ These bit patterns are:
+ - exponent = 0x0001..0x7FFF, mantissa bit 63 = 0,
+ - exponent = 0x0000, mantissa bit 63 = 1.
+ The NaN bit pattern is:
+ - exponent = 0x7FFF, mantissa >= 0x8000000000000001. */
+ memory_double m;
+ unsigned int exponent;
+
+ m.value = x;
+ exponent = (m.word[EXPBIT0_WORD] >> EXPBIT0_BIT) & EXP_MASK;
+# ifdef WORDS_BIGENDIAN
+ /* Big endian: EXPBIT0_WORD = 0, EXPBIT0_BIT = 16. */
+ if (exponent == 0)
+ return 1 & (m.word[0] >> 15);
+ else if (exponent == EXP_MASK)
+ return (((m.word[0] ^ 0x8000U) << 16) | m.word[1] | (m.word[2] >> 16)) != 0;
+ else
+ return 1 & ~(m.word[0] >> 15);
+# else
+ /* Little endian: EXPBIT0_WORD = 2, EXPBIT0_BIT = 0. */
+ if (exponent == 0)
+ return (m.word[1] >> 31);
+ else if (exponent == EXP_MASK)
+ return ((m.word[1] ^ 0x80000000U) | m.word[0]) != 0;
+ else
+ return (m.word[1] >> 31) ^ 1;
+# endif
+# else
+ /* Be careful to not do any floating-point operation on x, such as x == x,
+ because x may be a signaling NaN. */
+# if defined __SUNPRO_C || defined __ICC || defined _MSC_VER \
+ || defined __DECC || defined __TINYC__ \
+ || (defined __sgi && !defined __GNUC__)
+ /* The Sun C 5.0, Intel ICC 10.0, Microsoft Visual C/C++ 9.0, Compaq (ex-DEC)
+ 6.4, and TinyCC compilers don't recognize the initializers as constant
+ expressions. The Compaq compiler also fails when constant-folding
+ 0.0 / 0.0 even when constant-folding is not required. The Microsoft
+ Visual C/C++ compiler also fails when constant-folding 1.0 / 0.0 even
+ when constant-folding is not required. The SGI MIPSpro C compiler
+ complains about "floating-point operation result is out of range". */
+ static DOUBLE zero = L_(0.0);
+ memory_double nan;
+ DOUBLE plus_inf = L_(1.0) / zero;
+ DOUBLE minus_inf = -L_(1.0) / zero;
+ nan.value = zero / zero;
+# else
+ static memory_double nan = { L_(0.0) / L_(0.0) };
+ static DOUBLE plus_inf = L_(1.0) / L_(0.0);
+ static DOUBLE minus_inf = -L_(1.0) / L_(0.0);
+# endif
+ {
+ memory_double m;
+
+ /* A NaN can be recognized through its exponent. But exclude +Infinity and
+ -Infinity, which have the same exponent. */
+ m.value = x;
+ if (((m.word[EXPBIT0_WORD] ^ nan.word[EXPBIT0_WORD])
+ & (EXP_MASK << EXPBIT0_BIT))
+ == 0)
+ return (memcmp (&m.value, &plus_inf, SIZE) != 0
+ && memcmp (&m.value, &minus_inf, SIZE) != 0);
+ else
+ return 0;
+ }
+# endif
+#else
+ /* The configuration did not find sufficient information, or does
+ not use IEEE floating point. Give up about the signaling NaNs;
+ handle only the quiet NaNs. */
+ if (x == x)
+ {
+# if defined USE_LONG_DOUBLE && ((defined __ia64 && LDBL_MANT_DIG == 64) || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_)) && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE
+ /* Detect any special bit patterns that pass ==; see comment above. */
+ memory_double m1;
+ memory_double m2;
+
+ memset (&m1.value, 0, SIZE);
+ memset (&m2.value, 0, SIZE);
+ m1.value = x;
+ m2.value = x + (x ? 0.0L : -0.0L);
+ if (memcmp (&m1.value, &m2.value, SIZE) != 0)
+ return 1;
+# endif
+ return 0;
+ }
+ else
+ return 1;
+#endif
+}
diff --git a/lib/isnand-nolibm.h b/lib/isnand-nolibm.h
new file mode 100644
index 00000000000..9c75a8c47bf
--- /dev/null
+++ b/lib/isnand-nolibm.h
@@ -0,0 +1,33 @@
+/* Test for NaN that does not need libm.
+ Copyright (C) 2007-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/>. */
+
+#if HAVE_ISNAND_IN_LIBC
+/* Get declaration of isnan macro. */
+# include <math.h>
+# if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ /* GCC >= 4.0 and clang provide a type-generic built-in for isnan. */
+# undef isnand
+# define isnand(x) __builtin_isnan ((double)(x))
+# else
+# undef isnand
+# define isnand(x) isnan ((double)(x))
+# endif
+#else
+/* Test whether X is a NaN. */
+# undef isnand
+# define isnand rpl_isnand
+extern int isnand (double x);
+#endif
diff --git a/lib/isnand.c b/lib/isnand.c
new file mode 100644
index 00000000000..e14d2e6fb29
--- /dev/null
+++ b/lib/isnand.c
@@ -0,0 +1,19 @@
+/* Test for NaN that does not need libm.
+ Copyright (C) 2008-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 Bruno Haible <bruno@clisp.org>, 2008. */
+
+#include "isnan.c"
diff --git a/lib/isnanf-nolibm.h b/lib/isnanf-nolibm.h
new file mode 100644
index 00000000000..cc6b4198ff1
--- /dev/null
+++ b/lib/isnanf-nolibm.h
@@ -0,0 +1,41 @@
+/* Test for NaN that does not need libm.
+ Copyright (C) 2007-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/>. */
+
+#if HAVE_ISNANF_IN_LIBC
+/* Get declaration of isnan macro or (older) isnanf function. */
+# include <math.h>
+# if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ /* GCC >= 4.0 and clang provide a type-generic built-in for isnan.
+ GCC >= 4.0 also provides __builtin_isnanf, but clang doesn't. */
+# undef isnanf
+# define isnanf(x) __builtin_isnan ((float)(x))
+# elif defined isnan
+# undef isnanf
+# define isnanf(x) isnan ((float)(x))
+# else
+ /* Get declaration of isnanf(), if not declared in <math.h>. */
+# if defined __sgi
+ /* We can't include <ieeefp.h>, because it conflicts with our definition of
+ isnand. Therefore declare isnanf separately. */
+extern int isnanf (float x);
+# endif
+# endif
+#else
+/* Test whether X is a NaN. */
+# undef isnanf
+# define isnanf rpl_isnanf
+extern int isnanf (float x);
+#endif
diff --git a/lib/isnanf.c b/lib/isnanf.c
new file mode 100644
index 00000000000..55252ca6d6e
--- /dev/null
+++ b/lib/isnanf.c
@@ -0,0 +1,20 @@
+/* Test for NaN that does not need libm.
+ Copyright (C) 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 Bruno Haible <bruno@clisp.org>, 2007. */
+
+#define USE_FLOAT
+#include "isnan.c"
diff --git a/lib/isnanl-nolibm.h b/lib/isnanl-nolibm.h
new file mode 100644
index 00000000000..f04c489bb8e
--- /dev/null
+++ b/lib/isnanl-nolibm.h
@@ -0,0 +1,34 @@
+/* Test for NaN that does not need libm.
+ Copyright (C) 2007-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/>. */
+
+#if HAVE_ISNANL_IN_LIBC
+/* Get declaration of isnan macro or (older) isnanl function. */
+# include <math.h>
+# if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ /* GCC >= 4.0 and clang provide a type-generic built-in for isnan.
+ GCC >= 4.0 also provides __builtin_isnanl, but clang doesn't. */
+# undef isnanl
+# define isnanl(x) __builtin_isnan ((long double)(x))
+# elif defined isnan
+# undef isnanl
+# define isnanl(x) isnan ((long double)(x))
+# endif
+#else
+/* Test whether X is a NaN. */
+# undef isnanl
+# define isnanl rpl_isnanl
+extern int isnanl (long double x);
+#endif
diff --git a/lib/isnanl.c b/lib/isnanl.c
new file mode 100644
index 00000000000..286f245097e
--- /dev/null
+++ b/lib/isnanl.c
@@ -0,0 +1,20 @@
+/* Test for NaN that does not need libm.
+ Copyright (C) 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 Bruno Haible <bruno@clisp.org>, 2007. */
+
+#define USE_LONG_DOUBLE
+#include "isnan.c"
diff --git a/lib/itold.c b/lib/itold.c
new file mode 100644
index 00000000000..0ef4464eead
--- /dev/null
+++ b/lib/itold.c
@@ -0,0 +1,28 @@
+/* Replacement for 'int' to 'long double' conversion routine.
+ Copyright (C) 2011-2023 Free Software Foundation, Inc.
+ Written by Bruno Haible <bruno@clisp.org>, 2011.
+
+ 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/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include <float.h>
+
+void
+_Qp_itoq (long double *result, int a)
+{
+ /* Convert from 'int' to 'double', then from 'double' to 'long double'. */
+ *result = (double) a;
+}
diff --git a/lib/math.c b/lib/math.c
new file mode 100644
index 00000000000..67cabbcf8f2
--- /dev/null
+++ b/lib/math.c
@@ -0,0 +1,22 @@
+/* Inline functions for <math.h>.
+
+ Copyright (C) 2012-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/>. */
+
+#include <config.h>
+
+#define _GL_MATH_INLINE _GL_EXTERN_INLINE
+#include "math.h"
+typedef int dummy;
diff --git a/lib/math.in.h b/lib/math.in.h
new file mode 100644
index 00000000000..76d48a44437
--- /dev/null
+++ b/lib/math.in.h
@@ -0,0 +1,2735 @@
+/* A GNU-like <math.h>.
+
+ Copyright (C) 2002-2003, 2007-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/>. */
+
+/* On Android, in C++ mode, when /usr/include/c++/v1/math.h is being included
+ and /usr/include/math.h has not yet been included, skip this file, since it
+ would lead to many syntax errors. */
+#if !(defined __ANDROID__ && defined _LIBCPP_MATH_H && !defined INFINITY)
+
+#ifndef _@GUARD_PREFIX@_MATH_H
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+#if defined _GL_INCLUDING_MATH_H
+/* Special invocation convention:
+ - On FreeBSD 12.2 we have a sequence of nested includes
+ <math.h> -> <stdlib.h> -> <sys/wait.h> -> <sys/types.h> -> <sys/select.h>
+ -> <signal.h> -> <pthread.h> -> <stdlib.h> -> <math.h>
+ In this situation, the functions are not yet declared, therefore we cannot
+ provide the C++ aliases. */
+
+#@INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ @NEXT_AS_FIRST_DIRECTIVE_MATH_H@
+
+#else
+/* Normal invocation convention. */
+
+/* The include_next requires a split double-inclusion guard. */
+#define _GL_INCLUDING_MATH_H
+#@INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ @NEXT_AS_FIRST_DIRECTIVE_MATH_H@
+#undef _GL_INCLUDING_MATH_H
+
+#ifndef _@GUARD_PREFIX@_MATH_H
+#define _@GUARD_PREFIX@_MATH_H
+
+/* On OpenVMS, NAN, INFINITY, and HUGEVAL macros are defined in <fp.h>. */
+#if defined __VMS && ! defined NAN
+# include <fp.h>
+#endif
+
+#ifndef _GL_INLINE_HEADER_BEGIN
+ #error "Please include config.h first."
+#endif
+_GL_INLINE_HEADER_BEGIN
+#ifndef _GL_MATH_INLINE
+# define _GL_MATH_INLINE _GL_INLINE
+#endif
+
+/* The __attribute__ feature is available in gcc versions 2.5 and later.
+ The attribute __const__ was added in gcc 2.95. */
+#ifndef _GL_ATTRIBUTE_CONST
+# if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 95) || defined __clang__
+# define _GL_ATTRIBUTE_CONST __attribute__ ((__const__))
+# else
+# define _GL_ATTRIBUTE_CONST /* empty */
+# endif
+#endif
+
+/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */
+
+/* The definition of _GL_ARG_NONNULL is copied here. */
+
+/* The definition of _GL_WARN_ON_USE is copied here. */
+
+#ifdef __cplusplus
+/* Helper macros to define type-generic function FUNC as overloaded functions,
+ rather than as macros like in C. POSIX declares these with an argument of
+ real-floating (that is, one of float, double, or long double). */
+# define _GL_MATH_CXX_REAL_FLOATING_DECL_1(func) \
+static inline int \
+_gl_cxx_ ## func ## f (float f) \
+{ \
+ return func (f); \
+} \
+static inline int \
+_gl_cxx_ ## func ## d (double d) \
+{ \
+ return func (d); \
+} \
+static inline int \
+_gl_cxx_ ## func ## l (long double l) \
+{ \
+ return func (l); \
+}
+# define _GL_MATH_CXX_REAL_FLOATING_DECL_2(func,rpl_func,rettype) \
+_GL_BEGIN_NAMESPACE \
+inline rettype \
+rpl_func (float f) \
+{ \
+ return _gl_cxx_ ## func ## f (f); \
+} \
+inline rettype \
+rpl_func (double d) \
+{ \
+ return _gl_cxx_ ## func ## d (d); \
+} \
+inline rettype \
+rpl_func (long double l) \
+{ \
+ return _gl_cxx_ ## func ## l (l); \
+} \
+_GL_END_NAMESPACE
+#endif
+
+/* Helper macros to define a portability warning for the
+ classification macro FUNC called with VALUE. POSIX declares the
+ classification macros with an argument of real-floating (that is,
+ one of float, double, or long double). */
+#define _GL_WARN_REAL_FLOATING_DECL(func) \
+_GL_MATH_INLINE int \
+_GL_WARN_ON_USE_ATTRIBUTE (#func " is unportable - " \
+ "use gnulib module " #func " for portability") \
+rpl_ ## func ## f (float f) \
+{ \
+ return func (f); \
+} \
+_GL_MATH_INLINE int \
+_GL_WARN_ON_USE_ATTRIBUTE (#func " is unportable - " \
+ "use gnulib module " #func " for portability") \
+rpl_ ## func ## d (double d) \
+{ \
+ return func (d); \
+} \
+_GL_MATH_INLINE int \
+_GL_WARN_ON_USE_ATTRIBUTE (#func " is unportable - " \
+ "use gnulib module " #func " for portability") \
+rpl_ ## func ## l (long double l) \
+{ \
+ return func (l); \
+}
+#define _GL_WARN_REAL_FLOATING_IMPL(func, value) \
+ (sizeof (value) == sizeof (float) ? rpl_ ## func ## f (value) \
+ : sizeof (value) == sizeof (double) ? rpl_ ## func ## d (value) \
+ : rpl_ ## func ## l (value))
+
+
+#if @REPLACE_ITOLD@
+/* Pull in a function that fixes the 'int' to 'long double' conversion
+ of glibc 2.7. */
+_GL_EXTERN_C void _Qp_itoq (long double *, int);
+static void (*_gl_math_fix_itold) (long double *, int) = _Qp_itoq;
+#endif
+
+
+/* POSIX allows platforms that don't support NAN. But all major
+ machines in the past 15 years have supported something close to
+ IEEE NaN, so we define this unconditionally. We also must define
+ it on platforms like Solaris 10, where NAN is present but defined
+ as a function pointer rather than a floating point constant. */
+#if !defined NAN || @REPLACE_NAN@
+# if !GNULIB_defined_NAN
+# undef NAN
+ /* The Compaq (ex-DEC) C 6.4 compiler and the Microsoft MSVC 9 compiler
+ choke on the expression 0.0 / 0.0. */
+# if defined __DECC || defined _MSC_VER
+_GL_MATH_INLINE float
+_NaN ()
+{
+ static float zero = 0.0f;
+ return zero / zero;
+}
+# define NAN (_NaN())
+# else
+# define NAN (0.0f / 0.0f)
+# endif
+# define GNULIB_defined_NAN 1
+# endif
+#endif
+
+/* Solaris 10 defines HUGE_VAL, but as a function pointer rather
+ than a floating point constant. */
+#if @REPLACE_HUGE_VAL@
+# undef HUGE_VALF
+# define HUGE_VALF (1.0f / 0.0f)
+# undef HUGE_VAL
+# define HUGE_VAL (1.0 / 0.0)
+# undef HUGE_VALL
+# define HUGE_VALL (1.0L / 0.0L)
+#endif
+
+/* HUGE_VALF is a 'float' Infinity. */
+#ifndef HUGE_VALF
+# if defined _MSC_VER
+/* The Microsoft MSVC 9 compiler chokes on the expression 1.0f / 0.0f. */
+# define HUGE_VALF (1e25f * 1e25f)
+# else
+# define HUGE_VALF (1.0f / 0.0f)
+# endif
+#endif
+
+/* HUGE_VAL is a 'double' Infinity. */
+#ifndef HUGE_VAL
+# if defined _MSC_VER
+/* The Microsoft MSVC 9 compiler chokes on the expression 1.0 / 0.0. */
+# define HUGE_VAL (1e250 * 1e250)
+# else
+# define HUGE_VAL (1.0 / 0.0)
+# endif
+#endif
+
+/* HUGE_VALL is a 'long double' Infinity. */
+#ifndef HUGE_VALL
+# if defined _MSC_VER
+/* The Microsoft MSVC 9 compiler chokes on the expression 1.0L / 0.0L. */
+# define HUGE_VALL (1e250L * 1e250L)
+# else
+# define HUGE_VALL (1.0L / 0.0L)
+# endif
+#endif
+
+
+#if defined FP_ILOGB0 && defined FP_ILOGBNAN
+ /* Ensure FP_ILOGB0 and FP_ILOGBNAN are correct. */
+# if defined __HAIKU__
+ /* Haiku: match what ilogb() does */
+# undef FP_ILOGB0
+# undef FP_ILOGBNAN
+# define FP_ILOGB0 (- 2147483647 - 1) /* INT_MIN */
+# define FP_ILOGBNAN (- 2147483647 - 1) /* INT_MIN */
+# endif
+#else
+ /* Ensure FP_ILOGB0 and FP_ILOGBNAN are defined. */
+# if defined __NetBSD__ || defined __sgi
+ /* NetBSD, IRIX 6.5: match what ilogb() does */
+# define FP_ILOGB0 (- 2147483647 - 1) /* INT_MIN */
+# define FP_ILOGBNAN (- 2147483647 - 1) /* INT_MIN */
+# elif defined _AIX
+ /* AIX 5.1: match what ilogb() does in AIX >= 5.2 */
+# define FP_ILOGB0 (- 2147483647 - 1) /* INT_MIN */
+# define FP_ILOGBNAN 2147483647 /* INT_MAX */
+# elif defined __sun
+ /* Solaris 9: match what ilogb() does */
+# define FP_ILOGB0 (- 2147483647) /* - INT_MAX */
+# define FP_ILOGBNAN 2147483647 /* INT_MAX */
+# else
+ /* Gnulib defined values. */
+# define FP_ILOGB0 (- 2147483647) /* - INT_MAX */
+# define FP_ILOGBNAN (- 2147483647 - 1) /* INT_MIN */
+# endif
+#endif
+
+
+#if @GNULIB_ACOSF@
+# if @REPLACE_ACOSF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef acosf
+# define acosf rpl_acosf
+# endif
+_GL_FUNCDECL_RPL (acosf, float, (float x));
+_GL_CXXALIAS_RPL (acosf, float, (float x));
+# else
+# if !@HAVE_ACOSF@
+# undef acosf
+_GL_FUNCDECL_SYS (acosf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (acosf, float, (float x));
+# endif
+_GL_CXXALIASWARN (acosf);
+#elif defined GNULIB_POSIXCHECK
+# undef acosf
+# if HAVE_RAW_DECL_ACOSF
+_GL_WARN_ON_USE (acosf, "acosf is unportable - "
+ "use gnulib module acosf for portability");
+# endif
+#endif
+
+#if @GNULIB_ACOSL@
+# if !@HAVE_ACOSL@ || !@HAVE_DECL_ACOSL@
+# undef acosl
+_GL_FUNCDECL_SYS (acosl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (acosl, long double, (long double x));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (acosl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef acosl
+# if HAVE_RAW_DECL_ACOSL
+_GL_WARN_ON_USE (acosl, "acosl is unportable - "
+ "use gnulib module acosl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_ASINF@
+# if @REPLACE_ASINF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef asinf
+# define asinf rpl_asinf
+# endif
+_GL_FUNCDECL_RPL (asinf, float, (float x));
+_GL_CXXALIAS_RPL (asinf, float, (float x));
+# else
+# if !@HAVE_ASINF@
+# undef asinf
+_GL_FUNCDECL_SYS (asinf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (asinf, float, (float x));
+# endif
+_GL_CXXALIASWARN (asinf);
+#elif defined GNULIB_POSIXCHECK
+# undef asinf
+# if HAVE_RAW_DECL_ASINF
+_GL_WARN_ON_USE (asinf, "asinf is unportable - "
+ "use gnulib module asinf for portability");
+# endif
+#endif
+
+#if @GNULIB_ASINL@
+# if !@HAVE_ASINL@ || !@HAVE_DECL_ASINL@
+# undef asinl
+_GL_FUNCDECL_SYS (asinl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (asinl, long double, (long double x));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (asinl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef asinl
+# if HAVE_RAW_DECL_ASINL
+_GL_WARN_ON_USE (asinl, "asinl is unportable - "
+ "use gnulib module asinl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_ATANF@
+# if @REPLACE_ATANF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef atanf
+# define atanf rpl_atanf
+# endif
+_GL_FUNCDECL_RPL (atanf, float, (float x));
+_GL_CXXALIAS_RPL (atanf, float, (float x));
+# else
+# if !@HAVE_ATANF@
+# undef atanf
+_GL_FUNCDECL_SYS (atanf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (atanf, float, (float x));
+# endif
+_GL_CXXALIASWARN (atanf);
+#elif defined GNULIB_POSIXCHECK
+# undef atanf
+# if HAVE_RAW_DECL_ATANF
+_GL_WARN_ON_USE (atanf, "atanf is unportable - "
+ "use gnulib module atanf for portability");
+# endif
+#endif
+
+#if @GNULIB_ATANL@
+# if !@HAVE_ATANL@ || !@HAVE_DECL_ATANL@
+# undef atanl
+_GL_FUNCDECL_SYS (atanl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (atanl, long double, (long double x));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (atanl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef atanl
+# if HAVE_RAW_DECL_ATANL
+_GL_WARN_ON_USE (atanl, "atanl is unportable - "
+ "use gnulib module atanl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_ATAN2F@
+# if @REPLACE_ATAN2F@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef atan2f
+# define atan2f rpl_atan2f
+# endif
+_GL_FUNCDECL_RPL (atan2f, float, (float y, float x));
+_GL_CXXALIAS_RPL (atan2f, float, (float y, float x));
+# else
+# if !@HAVE_ATAN2F@
+# undef atan2f
+_GL_FUNCDECL_SYS (atan2f, float, (float y, float x));
+# endif
+_GL_CXXALIAS_SYS (atan2f, float, (float y, float x));
+# endif
+_GL_CXXALIASWARN (atan2f);
+#elif defined GNULIB_POSIXCHECK
+# undef atan2f
+# if HAVE_RAW_DECL_ATAN2F
+_GL_WARN_ON_USE (atan2f, "atan2f is unportable - "
+ "use gnulib module atan2f for portability");
+# endif
+#endif
+
+
+#if @GNULIB_CBRTF@
+# if @REPLACE_CBRTF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef cbrtf
+# define cbrtf rpl_cbrtf
+# endif
+_GL_FUNCDECL_RPL (cbrtf, float, (float x));
+_GL_CXXALIAS_RPL (cbrtf, float, (float x));
+# else
+# if !@HAVE_DECL_CBRTF@
+_GL_FUNCDECL_SYS (cbrtf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (cbrtf, float, (float x));
+# endif
+_GL_CXXALIASWARN (cbrtf);
+#elif defined GNULIB_POSIXCHECK
+# undef cbrtf
+# if HAVE_RAW_DECL_CBRTF
+_GL_WARN_ON_USE (cbrtf, "cbrtf is unportable - "
+ "use gnulib module cbrtf for portability");
+# endif
+#endif
+
+#if @GNULIB_CBRT@
+# if !@HAVE_CBRT@
+_GL_FUNCDECL_SYS (cbrt, double, (double x));
+# endif
+_GL_CXXALIAS_SYS (cbrt, double, (double x));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (cbrt, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef cbrt
+# if HAVE_RAW_DECL_CBRT
+_GL_WARN_ON_USE (cbrt, "cbrt is unportable - "
+ "use gnulib module cbrt for portability");
+# endif
+#endif
+
+#if @GNULIB_CBRTL@
+# if @REPLACE_CBRTL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef cbrtl
+# define cbrtl rpl_cbrtl
+# endif
+_GL_FUNCDECL_RPL (cbrtl, long double, (long double x));
+_GL_CXXALIAS_RPL (cbrtl, long double, (long double x));
+# else
+# if !@HAVE_DECL_CBRTL@
+_GL_FUNCDECL_SYS (cbrtl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (cbrtl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (cbrtl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef cbrtl
+# if HAVE_RAW_DECL_CBRTL
+_GL_WARN_ON_USE (cbrtl, "cbrtl is unportable - "
+ "use gnulib module cbrtl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_CEILF@
+# if @REPLACE_CEILF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef ceilf
+# define ceilf rpl_ceilf
+# endif
+_GL_FUNCDECL_RPL (ceilf, float, (float x));
+_GL_CXXALIAS_RPL (ceilf, float, (float x));
+# else
+# if !@HAVE_DECL_CEILF@
+# undef ceilf
+_GL_FUNCDECL_SYS (ceilf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (ceilf, float, (float x));
+# endif
+_GL_CXXALIASWARN (ceilf);
+#elif defined GNULIB_POSIXCHECK
+# undef ceilf
+# if HAVE_RAW_DECL_CEILF
+_GL_WARN_ON_USE (ceilf, "ceilf is unportable - "
+ "use gnulib module ceilf for portability");
+# endif
+#endif
+
+#if @GNULIB_CEIL@
+# if @REPLACE_CEIL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef ceil
+# define ceil rpl_ceil
+# endif
+_GL_FUNCDECL_RPL (ceil, double, (double x));
+_GL_CXXALIAS_RPL (ceil, double, (double x));
+# else
+_GL_CXXALIAS_SYS (ceil, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (ceil, double, (double x));
+# endif
+#endif
+
+#if @GNULIB_CEILL@
+# if @REPLACE_CEILL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef ceill
+# define ceill rpl_ceill
+# endif
+_GL_FUNCDECL_RPL (ceill, long double, (long double x));
+_GL_CXXALIAS_RPL (ceill, long double, (long double x));
+# else
+# if !@HAVE_DECL_CEILL@
+# undef ceill
+_GL_FUNCDECL_SYS (ceill, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (ceill, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (ceill);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef ceill
+# if HAVE_RAW_DECL_CEILL
+_GL_WARN_ON_USE (ceill, "ceill is unportable - "
+ "use gnulib module ceill for portability");
+# endif
+#endif
+
+
+#if @GNULIB_COPYSIGNF@
+# if !@HAVE_DECL_COPYSIGNF@
+# undef copysignf
+_GL_FUNCDECL_SYS (copysignf, float, (float x, float y));
+# endif
+_GL_CXXALIAS_SYS (copysignf, float, (float x, float y));
+_GL_CXXALIASWARN (copysignf);
+#elif defined GNULIB_POSIXCHECK
+# undef copysignf
+# if HAVE_RAW_DECL_COPYSIGNF
+_GL_WARN_ON_USE (copysignf, "copysignf is unportable - "
+ "use gnulib module copysignf for portability");
+# endif
+#endif
+
+#if @GNULIB_COPYSIGN@
+# if !@HAVE_COPYSIGN@
+_GL_FUNCDECL_SYS (copysign, double, (double x, double y));
+# endif
+_GL_CXXALIAS_SYS (copysign, double, (double x, double y));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (copysign, double, (double x, double y));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef copysign
+# if HAVE_RAW_DECL_COPYSIGN
+_GL_WARN_ON_USE (copysign, "copysign is unportable - "
+ "use gnulib module copysign for portability");
+# endif
+#endif
+
+#if @GNULIB_COPYSIGNL@
+# if !@HAVE_COPYSIGNL@
+_GL_FUNCDECL_SYS (copysignl, long double, (long double x, long double y));
+# endif
+_GL_CXXALIAS_SYS (copysignl, long double, (long double x, long double y));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (copysignl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef copysignl
+# if HAVE_RAW_DECL_COPYSIGNL
+_GL_WARN_ON_USE (copysign, "copysignl is unportable - "
+ "use gnulib module copysignl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_COSF@
+# if @REPLACE_COSF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef cosf
+# define cosf rpl_cosf
+# endif
+_GL_FUNCDECL_RPL (cosf, float, (float x));
+_GL_CXXALIAS_RPL (cosf, float, (float x));
+# else
+# if !@HAVE_COSF@
+# undef cosf
+_GL_FUNCDECL_SYS (cosf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (cosf, float, (float x));
+# endif
+_GL_CXXALIASWARN (cosf);
+#elif defined GNULIB_POSIXCHECK
+# undef cosf
+# if HAVE_RAW_DECL_COSF
+_GL_WARN_ON_USE (cosf, "cosf is unportable - "
+ "use gnulib module cosf for portability");
+# endif
+#endif
+
+#if @GNULIB_COSL@
+# if !@HAVE_COSL@ || !@HAVE_DECL_COSL@
+# undef cosl
+_GL_FUNCDECL_SYS (cosl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (cosl, long double, (long double x));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (cosl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef cosl
+# if HAVE_RAW_DECL_COSL
+_GL_WARN_ON_USE (cosl, "cosl is unportable - "
+ "use gnulib module cosl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_COSHF@
+# if @REPLACE_COSHF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef coshf
+# define coshf rpl_coshf
+# endif
+_GL_FUNCDECL_RPL (coshf, float, (float x));
+_GL_CXXALIAS_RPL (coshf, float, (float x));
+# else
+# if !@HAVE_COSHF@
+# undef coshf
+_GL_FUNCDECL_SYS (coshf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (coshf, float, (float x));
+# endif
+_GL_CXXALIASWARN (coshf);
+#elif defined GNULIB_POSIXCHECK
+# undef coshf
+# if HAVE_RAW_DECL_COSHF
+_GL_WARN_ON_USE (coshf, "coshf is unportable - "
+ "use gnulib module coshf for portability");
+# endif
+#endif
+
+
+#if @GNULIB_EXPF@
+# if @REPLACE_EXPF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef expf
+# define expf rpl_expf
+# endif
+_GL_FUNCDECL_RPL (expf, float, (float x));
+_GL_CXXALIAS_RPL (expf, float, (float x));
+# else
+# if !@HAVE_EXPF@
+# undef expf
+_GL_FUNCDECL_SYS (expf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (expf, float, (float x));
+# endif
+_GL_CXXALIASWARN (expf);
+#elif defined GNULIB_POSIXCHECK
+# undef expf
+# if HAVE_RAW_DECL_EXPF
+_GL_WARN_ON_USE (expf, "expf is unportable - "
+ "use gnulib module expf for portability");
+# endif
+#endif
+
+#if @GNULIB_EXPL@
+# if @REPLACE_EXPL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef expl
+# define expl rpl_expl
+# endif
+_GL_FUNCDECL_RPL (expl, long double, (long double x));
+_GL_CXXALIAS_RPL (expl, long double, (long double x));
+# else
+# if !@HAVE_EXPL@ || !@HAVE_DECL_EXPL@
+# undef expl
+_GL_FUNCDECL_SYS (expl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (expl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (expl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef expl
+# if HAVE_RAW_DECL_EXPL
+_GL_WARN_ON_USE (expl, "expl is unportable - "
+ "use gnulib module expl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_EXP2F@
+# if !@HAVE_DECL_EXP2F@
+_GL_FUNCDECL_SYS (exp2f, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (exp2f, float, (float x));
+_GL_CXXALIASWARN (exp2f);
+#elif defined GNULIB_POSIXCHECK
+# undef exp2f
+# if HAVE_RAW_DECL_EXP2F
+_GL_WARN_ON_USE (exp2f, "exp2f is unportable - "
+ "use gnulib module exp2f for portability");
+# endif
+#endif
+
+#if @GNULIB_EXP2@
+# if @REPLACE_EXP2@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef exp2
+# define exp2 rpl_exp2
+# endif
+_GL_FUNCDECL_RPL (exp2, double, (double x));
+_GL_CXXALIAS_RPL (exp2, double, (double x));
+# else
+# if !@HAVE_DECL_EXP2@
+_GL_FUNCDECL_SYS (exp2, double, (double x));
+# endif
+_GL_CXXALIAS_SYS (exp2, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (exp2, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef exp2
+# if HAVE_RAW_DECL_EXP2
+_GL_WARN_ON_USE (exp2, "exp2 is unportable - "
+ "use gnulib module exp2 for portability");
+# endif
+#endif
+
+#if @GNULIB_EXP2L@
+# if @REPLACE_EXP2L@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef exp2l
+# define exp2l rpl_exp2l
+# endif
+_GL_FUNCDECL_RPL (exp2l, long double, (long double x));
+_GL_CXXALIAS_RPL (exp2l, long double, (long double x));
+# else
+# if !@HAVE_DECL_EXP2L@
+# undef exp2l
+_GL_FUNCDECL_SYS (exp2l, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (exp2l, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (exp2l);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef exp2l
+# if HAVE_RAW_DECL_EXP2L
+_GL_WARN_ON_USE (exp2l, "exp2l is unportable - "
+ "use gnulib module exp2l for portability");
+# endif
+#endif
+
+
+#if @GNULIB_EXPM1F@
+# if @REPLACE_EXPM1F@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef expm1f
+# define expm1f rpl_expm1f
+# endif
+_GL_FUNCDECL_RPL (expm1f, float, (float x));
+_GL_CXXALIAS_RPL (expm1f, float, (float x));
+# else
+# if !@HAVE_EXPM1F@
+_GL_FUNCDECL_SYS (expm1f, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (expm1f, float, (float x));
+# endif
+_GL_CXXALIASWARN (expm1f);
+#elif defined GNULIB_POSIXCHECK
+# undef expm1f
+# if HAVE_RAW_DECL_EXPM1F
+_GL_WARN_ON_USE (expm1f, "expm1f is unportable - "
+ "use gnulib module expm1f for portability");
+# endif
+#endif
+
+#if @GNULIB_EXPM1@
+# if @REPLACE_EXPM1@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef expm1
+# define expm1 rpl_expm1
+# endif
+_GL_FUNCDECL_RPL (expm1, double, (double x));
+_GL_CXXALIAS_RPL (expm1, double, (double x));
+# else
+# if !@HAVE_EXPM1@
+_GL_FUNCDECL_SYS (expm1, double, (double x));
+# endif
+_GL_CXXALIAS_SYS (expm1, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (expm1, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef expm1
+# if HAVE_RAW_DECL_EXPM1
+_GL_WARN_ON_USE (expm1, "expm1 is unportable - "
+ "use gnulib module expm1 for portability");
+# endif
+#endif
+
+#if @GNULIB_EXPM1L@
+# if @REPLACE_EXPM1L@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef expm1l
+# define expm1l rpl_expm1l
+# endif
+_GL_FUNCDECL_RPL (expm1l, long double, (long double x));
+_GL_CXXALIAS_RPL (expm1l, long double, (long double x));
+# else
+# if !@HAVE_DECL_EXPM1L@
+# undef expm1l
+# if !(defined __cplusplus && defined _AIX)
+_GL_FUNCDECL_SYS (expm1l, long double, (long double x));
+# endif
+# endif
+_GL_CXXALIAS_SYS (expm1l, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (expm1l);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef expm1l
+# if HAVE_RAW_DECL_EXPM1L
+_GL_WARN_ON_USE (expm1l, "expm1l is unportable - "
+ "use gnulib module expm1l for portability");
+# endif
+#endif
+
+
+#if @GNULIB_FABSF@
+# if !@HAVE_FABSF@
+# undef fabsf
+_GL_FUNCDECL_SYS (fabsf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (fabsf, float, (float x));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (fabsf);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef fabsf
+# if HAVE_RAW_DECL_FABSF
+_GL_WARN_ON_USE (fabsf, "fabsf is unportable - "
+ "use gnulib module fabsf for portability");
+# endif
+#endif
+
+#if @GNULIB_FABSL@
+# if @REPLACE_FABSL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef fabsl
+# define fabsl rpl_fabsl
+# endif
+_GL_FUNCDECL_RPL (fabsl, long double, (long double x));
+_GL_CXXALIAS_RPL (fabsl, long double, (long double x));
+# else
+# if !@HAVE_FABSL@
+# undef fabsl
+_GL_FUNCDECL_SYS (fabsl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (fabsl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (fabsl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef fabsl
+# if HAVE_RAW_DECL_FABSL
+_GL_WARN_ON_USE (fabsl, "fabsl is unportable - "
+ "use gnulib module fabsl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_FLOORF@
+# if @REPLACE_FLOORF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef floorf
+# define floorf rpl_floorf
+# endif
+_GL_FUNCDECL_RPL (floorf, float, (float x));
+_GL_CXXALIAS_RPL (floorf, float, (float x));
+# else
+# if !@HAVE_DECL_FLOORF@
+# undef floorf
+_GL_FUNCDECL_SYS (floorf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (floorf, float, (float x));
+# endif
+_GL_CXXALIASWARN (floorf);
+#elif defined GNULIB_POSIXCHECK
+# undef floorf
+# if HAVE_RAW_DECL_FLOORF
+_GL_WARN_ON_USE (floorf, "floorf is unportable - "
+ "use gnulib module floorf for portability");
+# endif
+#endif
+
+#if @GNULIB_FLOOR@
+# if @REPLACE_FLOOR@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef floor
+# define floor rpl_floor
+# endif
+_GL_FUNCDECL_RPL (floor, double, (double x));
+_GL_CXXALIAS_RPL (floor, double, (double x));
+# else
+_GL_CXXALIAS_SYS (floor, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (floor, double, (double x));
+# endif
+#endif
+
+#if @GNULIB_FLOORL@
+# if @REPLACE_FLOORL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef floorl
+# define floorl rpl_floorl
+# endif
+_GL_FUNCDECL_RPL (floorl, long double, (long double x));
+_GL_CXXALIAS_RPL (floorl, long double, (long double x));
+# else
+# if !@HAVE_DECL_FLOORL@
+# undef floorl
+_GL_FUNCDECL_SYS (floorl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (floorl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (floorl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef floorl
+# if HAVE_RAW_DECL_FLOORL
+_GL_WARN_ON_USE (floorl, "floorl is unportable - "
+ "use gnulib module floorl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_FMAF@
+# if @REPLACE_FMAF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef fmaf
+# define fmaf rpl_fmaf
+# endif
+_GL_FUNCDECL_RPL (fmaf, float, (float x, float y, float z));
+_GL_CXXALIAS_RPL (fmaf, float, (float x, float y, float z));
+# else
+# if !@HAVE_FMAF@
+# undef fmaf
+_GL_FUNCDECL_SYS (fmaf, float, (float x, float y, float z));
+# endif
+_GL_CXXALIAS_SYS (fmaf, float, (float x, float y, float z));
+# endif
+_GL_CXXALIASWARN (fmaf);
+#elif defined GNULIB_POSIXCHECK
+# undef fmaf
+# if HAVE_RAW_DECL_FMAF
+_GL_WARN_ON_USE (fmaf, "fmaf is unportable - "
+ "use gnulib module fmaf for portability");
+# endif
+#endif
+
+#if @GNULIB_FMA@
+# if @REPLACE_FMA@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef fma
+# define fma rpl_fma
+# endif
+_GL_FUNCDECL_RPL (fma, double, (double x, double y, double z));
+_GL_CXXALIAS_RPL (fma, double, (double x, double y, double z));
+# else
+# if !@HAVE_FMA@
+# undef fma
+_GL_FUNCDECL_SYS (fma, double, (double x, double y, double z));
+# endif
+_GL_CXXALIAS_SYS (fma, double, (double x, double y, double z));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (fma, double, (double x, double y, double z));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef fma
+# if HAVE_RAW_DECL_FMA
+_GL_WARN_ON_USE (fma, "fma is unportable - "
+ "use gnulib module fma for portability");
+# endif
+#endif
+
+#if @GNULIB_FMAL@
+# if @REPLACE_FMAL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef fmal
+# define fmal rpl_fmal
+# endif
+_GL_FUNCDECL_RPL (fmal, long double,
+ (long double x, long double y, long double z));
+_GL_CXXALIAS_RPL (fmal, long double,
+ (long double x, long double y, long double z));
+# else
+# if !@HAVE_FMAL@
+# undef fmal
+# if !(defined __cplusplus && defined _AIX)
+_GL_FUNCDECL_SYS (fmal, long double,
+ (long double x, long double y, long double z));
+# endif
+# endif
+_GL_CXXALIAS_SYS (fmal, long double,
+ (long double x, long double y, long double z));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (fmal);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef fmal
+# if HAVE_RAW_DECL_FMAL
+_GL_WARN_ON_USE (fmal, "fmal is unportable - "
+ "use gnulib module fmal for portability");
+# endif
+#endif
+
+
+#if @GNULIB_FMODF@
+# if @REPLACE_FMODF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef fmodf
+# define fmodf rpl_fmodf
+# endif
+_GL_FUNCDECL_RPL (fmodf, float, (float x, float y));
+_GL_CXXALIAS_RPL (fmodf, float, (float x, float y));
+# else
+# if !@HAVE_FMODF@
+# undef fmodf
+_GL_FUNCDECL_SYS (fmodf, float, (float x, float y));
+# endif
+_GL_CXXALIAS_SYS (fmodf, float, (float x, float y));
+# endif
+_GL_CXXALIASWARN (fmodf);
+#elif defined GNULIB_POSIXCHECK
+# undef fmodf
+# if HAVE_RAW_DECL_FMODF
+_GL_WARN_ON_USE (fmodf, "fmodf is unportable - "
+ "use gnulib module fmodf for portability");
+# endif
+#endif
+
+#if @GNULIB_FMOD@
+# if @REPLACE_FMOD@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef fmod
+# define fmod rpl_fmod
+# endif
+_GL_FUNCDECL_RPL (fmod, double, (double x, double y));
+_GL_CXXALIAS_RPL (fmod, double, (double x, double y));
+# else
+_GL_CXXALIAS_SYS (fmod, double, (double x, double y));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (fmod, double, (double x, double y));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef fmod
+# if HAVE_RAW_DECL_FMOD
+_GL_WARN_ON_USE (fmod, "fmod has portability problems - "
+ "use gnulib module fmod for portability");
+# endif
+#endif
+
+#if @GNULIB_FMODL@
+# if @REPLACE_FMODL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef fmodl
+# define fmodl rpl_fmodl
+# endif
+_GL_FUNCDECL_RPL (fmodl, long double, (long double x, long double y));
+_GL_CXXALIAS_RPL (fmodl, long double, (long double x, long double y));
+# else
+# if !@HAVE_FMODL@
+# undef fmodl
+_GL_FUNCDECL_SYS (fmodl, long double, (long double x, long double y));
+# endif
+_GL_CXXALIAS_SYS (fmodl, long double, (long double x, long double y));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (fmodl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef fmodl
+# if HAVE_RAW_DECL_FMODL
+_GL_WARN_ON_USE (fmodl, "fmodl is unportable - "
+ "use gnulib module fmodl for portability");
+# endif
+#endif
+
+
+/* Write x as
+ x = mantissa * 2^exp
+ where
+ If x finite and nonzero: 0.5 <= |mantissa| < 1.0.
+ If x is zero: mantissa = x, exp = 0.
+ If x is infinite or NaN: mantissa = x, exp unspecified.
+ Store exp in *EXPPTR and return mantissa. */
+#if @GNULIB_FREXPF@
+# if @REPLACE_FREXPF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef frexpf
+# define frexpf rpl_frexpf
+# endif
+_GL_FUNCDECL_RPL (frexpf, float, (float x, int *expptr) _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (frexpf, float, (float x, int *expptr));
+# else
+# if !@HAVE_FREXPF@
+# undef frexpf
+_GL_FUNCDECL_SYS (frexpf, float, (float x, int *expptr) _GL_ARG_NONNULL ((2)));
+# endif
+_GL_CXXALIAS_SYS (frexpf, float, (float x, int *expptr));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (frexpf);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef frexpf
+# if HAVE_RAW_DECL_FREXPF
+_GL_WARN_ON_USE (frexpf, "frexpf is unportable - "
+ "use gnulib module frexpf for portability");
+# endif
+#endif
+
+/* Write x as
+ x = mantissa * 2^exp
+ where
+ If x finite and nonzero: 0.5 <= |mantissa| < 1.0.
+ If x is zero: mantissa = x, exp = 0.
+ If x is infinite or NaN: mantissa = x, exp unspecified.
+ Store exp in *EXPPTR and return mantissa. */
+#if @GNULIB_FREXP@
+# if @REPLACE_FREXP@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef frexp
+# define frexp rpl_frexp
+# endif
+_GL_FUNCDECL_RPL (frexp, double, (double x, int *expptr) _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (frexp, double, (double x, int *expptr));
+# else
+_GL_CXXALIAS_SYS (frexp, double, (double x, int *expptr));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (frexp, double, (double x, int *expptr));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef frexp
+/* Assume frexp is always declared. */
+_GL_WARN_ON_USE (frexp, "frexp is unportable - "
+ "use gnulib module frexp for portability");
+#endif
+
+/* Write x as
+ x = mantissa * 2^exp
+ where
+ If x finite and nonzero: 0.5 <= |mantissa| < 1.0.
+ If x is zero: mantissa = x, exp = 0.
+ If x is infinite or NaN: mantissa = x, exp unspecified.
+ Store exp in *EXPPTR and return mantissa. */
+#if @GNULIB_FREXPL@ && @REPLACE_FREXPL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef frexpl
+# define frexpl rpl_frexpl
+# endif
+_GL_FUNCDECL_RPL (frexpl, long double,
+ (long double x, int *expptr) _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (frexpl, long double, (long double x, int *expptr));
+#else
+# if !@HAVE_DECL_FREXPL@
+_GL_FUNCDECL_SYS (frexpl, long double,
+ (long double x, int *expptr) _GL_ARG_NONNULL ((2)));
+# endif
+# if @GNULIB_FREXPL@
+_GL_CXXALIAS_SYS (frexpl, long double, (long double x, int *expptr));
+# endif
+#endif
+#if @GNULIB_FREXPL@ && !(@REPLACE_FREXPL@ && !@HAVE_DECL_FREXPL@)
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (frexpl);
+# endif
+#endif
+#if !@GNULIB_FREXPL@ && defined GNULIB_POSIXCHECK
+# undef frexpl
+# if HAVE_RAW_DECL_FREXPL
+_GL_WARN_ON_USE (frexpl, "frexpl is unportable - "
+ "use gnulib module frexpl for portability");
+# endif
+#endif
+
+
+/* Return sqrt(x^2+y^2). */
+#if @GNULIB_HYPOTF@
+# if @REPLACE_HYPOTF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef hypotf
+# define hypotf rpl_hypotf
+# endif
+_GL_FUNCDECL_RPL (hypotf, float, (float x, float y));
+_GL_CXXALIAS_RPL (hypotf, float, (float x, float y));
+# else
+# if !@HAVE_HYPOTF@
+_GL_FUNCDECL_SYS (hypotf, float, (float x, float y));
+# endif
+_GL_CXXALIAS_SYS (hypotf, float, (float x, float y));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (hypotf);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef hypotf
+# if HAVE_RAW_DECL_HYPOTF
+_GL_WARN_ON_USE (hypotf, "hypotf is unportable - "
+ "use gnulib module hypotf for portability");
+# endif
+#endif
+
+/* Return sqrt(x^2+y^2). */
+#if @GNULIB_HYPOT@
+# if @REPLACE_HYPOT@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef hypot
+# define hypot rpl_hypot
+# endif
+_GL_FUNCDECL_RPL (hypot, double, (double x, double y));
+_GL_CXXALIAS_RPL (hypot, double, (double x, double y));
+# else
+_GL_CXXALIAS_SYS (hypot, double, (double x, double y));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (hypot, double, (double x, double y));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef hypot
+# if HAVE_RAW_DECL_HYPOT
+_GL_WARN_ON_USE (hypotf, "hypot has portability problems - "
+ "use gnulib module hypot for portability");
+# endif
+#endif
+
+/* Return sqrt(x^2+y^2). */
+#if @GNULIB_HYPOTL@
+# if @REPLACE_HYPOTL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef hypotl
+# define hypotl rpl_hypotl
+# endif
+_GL_FUNCDECL_RPL (hypotl, long double, (long double x, long double y));
+_GL_CXXALIAS_RPL (hypotl, long double, (long double x, long double y));
+# else
+# if !@HAVE_HYPOTL@
+_GL_FUNCDECL_SYS (hypotl, long double, (long double x, long double y));
+# endif
+_GL_CXXALIAS_SYS (hypotl, long double, (long double x, long double y));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (hypotl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef hypotl
+# if HAVE_RAW_DECL_HYPOTL
+_GL_WARN_ON_USE (hypotl, "hypotl is unportable - "
+ "use gnulib module hypotl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_ILOGBF@
+# if @REPLACE_ILOGBF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef ilogbf
+# define ilogbf rpl_ilogbf
+# endif
+_GL_FUNCDECL_RPL (ilogbf, int, (float x));
+_GL_CXXALIAS_RPL (ilogbf, int, (float x));
+# else
+# if !@HAVE_ILOGBF@
+_GL_FUNCDECL_SYS (ilogbf, int, (float x));
+# endif
+_GL_CXXALIAS_SYS (ilogbf, int, (float x));
+# endif
+_GL_CXXALIASWARN (ilogbf);
+#elif defined GNULIB_POSIXCHECK
+# undef ilogbf
+# if HAVE_RAW_DECL_ILOGBF
+_GL_WARN_ON_USE (ilogbf, "ilogbf is unportable - "
+ "use gnulib module ilogbf for portability");
+# endif
+#endif
+
+#if @GNULIB_ILOGB@
+# if @REPLACE_ILOGB@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef ilogb
+# define ilogb rpl_ilogb
+# endif
+_GL_FUNCDECL_RPL (ilogb, int, (double x));
+_GL_CXXALIAS_RPL (ilogb, int, (double x));
+# else
+# if !@HAVE_ILOGB@
+_GL_FUNCDECL_SYS (ilogb, int, (double x));
+# endif
+_GL_CXXALIAS_SYS (ilogb, int, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (ilogb, int, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef ilogb
+# if HAVE_RAW_DECL_ILOGB
+_GL_WARN_ON_USE (ilogb, "ilogb is unportable - "
+ "use gnulib module ilogb for portability");
+# endif
+#endif
+
+#if @GNULIB_ILOGBL@
+# if @REPLACE_ILOGBL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef ilogbl
+# define ilogbl rpl_ilogbl
+# endif
+_GL_FUNCDECL_RPL (ilogbl, int, (long double x));
+_GL_CXXALIAS_RPL (ilogbl, int, (long double x));
+# else
+# if !@HAVE_ILOGBL@
+# undef ilogbl
+_GL_FUNCDECL_SYS (ilogbl, int, (long double x));
+# endif
+_GL_CXXALIAS_SYS (ilogbl, int, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (ilogbl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef ilogbl
+# if HAVE_RAW_DECL_ILOGBL
+_GL_WARN_ON_USE (ilogbl, "ilogbl is unportable - "
+ "use gnulib module ilogbl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_MDA_J0@
+/* On native Windows, map 'j0' to '_j0', so that -loldnames is not
+ required. In C++ with GNULIB_NAMESPACE, avoid differences between
+ platforms by defining GNULIB_NAMESPACE::j0 always. */
+# if defined _WIN32 && !defined __CYGWIN__
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef j0
+# define j0 _j0
+# endif
+_GL_CXXALIAS_MDA (j0, double, (double x));
+# else
+_GL_CXXALIAS_SYS (j0, double, (double x));
+# endif
+_GL_CXXALIASWARN (j0);
+#endif
+
+#if @GNULIB_MDA_J1@
+/* On native Windows, map 'j1' to '_j1', so that -loldnames is not
+ required. In C++ with GNULIB_NAMESPACE, avoid differences between
+ platforms by defining GNULIB_NAMESPACE::j1 always. */
+# if defined _WIN32 && !defined __CYGWIN__
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef j1
+# define j1 _j1
+# endif
+_GL_CXXALIAS_MDA (j1, double, (double x));
+# else
+_GL_CXXALIAS_SYS (j1, double, (double x));
+# endif
+_GL_CXXALIASWARN (j1);
+#endif
+
+#if @GNULIB_MDA_JN@
+/* On native Windows, map 'jn' to '_jn', so that -loldnames is not
+ required. In C++ with GNULIB_NAMESPACE, avoid differences between
+ platforms by defining GNULIB_NAMESPACE::jn always. */
+# if defined _WIN32 && !defined __CYGWIN__
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef jn
+# define jn _jn
+# endif
+_GL_CXXALIAS_MDA (jn, double, (int n, double x));
+# else
+_GL_CXXALIAS_SYS (jn, double, (int n, double x));
+# endif
+_GL_CXXALIASWARN (jn);
+#endif
+
+
+/* Return x * 2^exp. */
+#if @GNULIB_LDEXPF@
+# if !@HAVE_LDEXPF@
+# undef ldexpf
+_GL_FUNCDECL_SYS (ldexpf, float, (float x, int exp));
+# endif
+_GL_CXXALIAS_SYS (ldexpf, float, (float x, int exp));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (ldexpf);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef ldexpf
+# if HAVE_RAW_DECL_LDEXPF
+_GL_WARN_ON_USE (ldexpf, "ldexpf is unportable - "
+ "use gnulib module ldexpf for portability");
+# endif
+#endif
+
+/* Return x * 2^exp. */
+#if @GNULIB_LDEXPL@ && @REPLACE_LDEXPL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef ldexpl
+# define ldexpl rpl_ldexpl
+# endif
+_GL_FUNCDECL_RPL (ldexpl, long double, (long double x, int exp));
+_GL_CXXALIAS_RPL (ldexpl, long double, (long double x, int exp));
+#else
+# if !@HAVE_DECL_LDEXPL@
+_GL_FUNCDECL_SYS (ldexpl, long double, (long double x, int exp));
+# endif
+# if @GNULIB_LDEXPL@
+_GL_CXXALIAS_SYS (ldexpl, long double, (long double x, int exp));
+# endif
+#endif
+#if @GNULIB_LDEXPL@
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (ldexpl);
+# endif
+#endif
+#if !@GNULIB_LDEXPL@ && defined GNULIB_POSIXCHECK
+# undef ldexpl
+# if HAVE_RAW_DECL_LDEXPL
+_GL_WARN_ON_USE (ldexpl, "ldexpl is unportable - "
+ "use gnulib module ldexpl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_LOGF@
+# if @REPLACE_LOGF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef logf
+# define logf rpl_logf
+# endif
+_GL_FUNCDECL_RPL (logf, float, (float x));
+_GL_CXXALIAS_RPL (logf, float, (float x));
+# else
+# if !@HAVE_LOGF@
+# undef logf
+_GL_FUNCDECL_SYS (logf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (logf, float, (float x));
+# endif
+_GL_CXXALIASWARN (logf);
+#elif defined GNULIB_POSIXCHECK
+# undef logf
+# if HAVE_RAW_DECL_LOGF
+_GL_WARN_ON_USE (logf, "logf is unportable - "
+ "use gnulib module logf for portability");
+# endif
+#endif
+
+#if @GNULIB_LOG@
+# if @REPLACE_LOG@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log
+# define log rpl_log
+# endif
+_GL_FUNCDECL_RPL (log, double, (double x));
+_GL_CXXALIAS_RPL (log, double, (double x));
+# else
+_GL_CXXALIAS_SYS (log, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (log, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef log
+# if HAVE_RAW_DECL_LOG
+_GL_WARN_ON_USE (log, "log has portability problems - "
+ "use gnulib module log for portability");
+# endif
+#endif
+
+#if @GNULIB_LOGL@
+# if @REPLACE_LOGL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef logl
+# define logl rpl_logl
+# endif
+_GL_FUNCDECL_RPL (logl, long double, (long double x));
+_GL_CXXALIAS_RPL (logl, long double, (long double x));
+# else
+# if !@HAVE_LOGL@ || !@HAVE_DECL_LOGL@
+# undef logl
+_GL_FUNCDECL_SYS (logl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (logl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (logl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef logl
+# if HAVE_RAW_DECL_LOGL
+_GL_WARN_ON_USE (logl, "logl is unportable - "
+ "use gnulib module logl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_LOG10F@
+# if @REPLACE_LOG10F@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log10f
+# define log10f rpl_log10f
+# endif
+_GL_FUNCDECL_RPL (log10f, float, (float x));
+_GL_CXXALIAS_RPL (log10f, float, (float x));
+# else
+# if !@HAVE_LOG10F@
+# undef log10f
+_GL_FUNCDECL_SYS (log10f, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (log10f, float, (float x));
+# endif
+_GL_CXXALIASWARN (log10f);
+#elif defined GNULIB_POSIXCHECK
+# undef log10f
+# if HAVE_RAW_DECL_LOG10F
+_GL_WARN_ON_USE (log10f, "log10f is unportable - "
+ "use gnulib module log10f for portability");
+# endif
+#endif
+
+#if @GNULIB_LOG10@
+# if @REPLACE_LOG10@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log10
+# define log10 rpl_log10
+# endif
+_GL_FUNCDECL_RPL (log10, double, (double x));
+_GL_CXXALIAS_RPL (log10, double, (double x));
+# else
+_GL_CXXALIAS_SYS (log10, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (log10, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef log10
+# if HAVE_RAW_DECL_LOG10
+_GL_WARN_ON_USE (log10, "log10 has portability problems - "
+ "use gnulib module log10 for portability");
+# endif
+#endif
+
+#if @GNULIB_LOG10L@
+# if @REPLACE_LOG10L@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log10l
+# define log10l rpl_log10l
+# endif
+_GL_FUNCDECL_RPL (log10l, long double, (long double x));
+_GL_CXXALIAS_RPL (log10l, long double, (long double x));
+# else
+# if !@HAVE_LOG10L@ || !@HAVE_DECL_LOG10L@
+# undef log10l
+_GL_FUNCDECL_SYS (log10l, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (log10l, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (log10l);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef log10l
+# if HAVE_RAW_DECL_LOG10L
+_GL_WARN_ON_USE (log10l, "log10l is unportable - "
+ "use gnulib module log10l for portability");
+# endif
+#endif
+
+
+#if @GNULIB_LOG1PF@
+# if @REPLACE_LOG1PF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log1pf
+# define log1pf rpl_log1pf
+# endif
+_GL_FUNCDECL_RPL (log1pf, float, (float x));
+_GL_CXXALIAS_RPL (log1pf, float, (float x));
+# else
+# if !@HAVE_LOG1PF@
+_GL_FUNCDECL_SYS (log1pf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (log1pf, float, (float x));
+# endif
+_GL_CXXALIASWARN (log1pf);
+#elif defined GNULIB_POSIXCHECK
+# undef log1pf
+# if HAVE_RAW_DECL_LOG1PF
+_GL_WARN_ON_USE (log1pf, "log1pf is unportable - "
+ "use gnulib module log1pf for portability");
+# endif
+#endif
+
+#if @GNULIB_LOG1P@
+# if @REPLACE_LOG1P@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log1p
+# define log1p rpl_log1p
+# endif
+_GL_FUNCDECL_RPL (log1p, double, (double x));
+_GL_CXXALIAS_RPL (log1p, double, (double x));
+# else
+# if !@HAVE_LOG1P@
+_GL_FUNCDECL_SYS (log1p, double, (double x));
+# endif
+_GL_CXXALIAS_SYS (log1p, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (log1p, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef log1p
+# if HAVE_RAW_DECL_LOG1P
+_GL_WARN_ON_USE (log1p, "log1p has portability problems - "
+ "use gnulib module log1p for portability");
+# endif
+#endif
+
+#if @GNULIB_LOG1PL@
+# if @REPLACE_LOG1PL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log1pl
+# define log1pl rpl_log1pl
+# endif
+_GL_FUNCDECL_RPL (log1pl, long double, (long double x));
+_GL_CXXALIAS_RPL (log1pl, long double, (long double x));
+# else
+# if !@HAVE_LOG1PL@
+_GL_FUNCDECL_SYS (log1pl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (log1pl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (log1pl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef log1pl
+# if HAVE_RAW_DECL_LOG1PL
+_GL_WARN_ON_USE (log1pl, "log1pl has portability problems - "
+ "use gnulib module log1pl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_LOG2F@
+# if @REPLACE_LOG2F@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log2f
+# define log2f rpl_log2f
+# endif
+_GL_FUNCDECL_RPL (log2f, float, (float x));
+_GL_CXXALIAS_RPL (log2f, float, (float x));
+# else
+# if !@HAVE_DECL_LOG2F@
+# undef log2f
+_GL_FUNCDECL_SYS (log2f, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (log2f, float, (float x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (log2f);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef log2f
+# if HAVE_RAW_DECL_LOG2F
+_GL_WARN_ON_USE (log2f, "log2f is unportable - "
+ "use gnulib module log2f for portability");
+# endif
+#endif
+
+#if @GNULIB_LOG2@
+# if @REPLACE_LOG2@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log2
+# define log2 rpl_log2
+# endif
+_GL_FUNCDECL_RPL (log2, double, (double x));
+_GL_CXXALIAS_RPL (log2, double, (double x));
+# else
+# if !@HAVE_DECL_LOG2@
+# undef log2
+_GL_FUNCDECL_SYS (log2, double, (double x));
+# endif
+_GL_CXXALIAS_SYS (log2, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (log2, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef log2
+# if HAVE_RAW_DECL_LOG2
+_GL_WARN_ON_USE (log2, "log2 is unportable - "
+ "use gnulib module log2 for portability");
+# endif
+#endif
+
+#if @GNULIB_LOG2L@
+# if @REPLACE_LOG2L@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef log2l
+# define log2l rpl_log2l
+# endif
+_GL_FUNCDECL_RPL (log2l, long double, (long double x));
+_GL_CXXALIAS_RPL (log2l, long double, (long double x));
+# else
+# if !@HAVE_DECL_LOG2L@
+_GL_FUNCDECL_SYS (log2l, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (log2l, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (log2l);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef log2l
+# if HAVE_RAW_DECL_LOG2L
+_GL_WARN_ON_USE (log2l, "log2l is unportable - "
+ "use gnulib module log2l for portability");
+# endif
+#endif
+
+
+#if @GNULIB_LOGBF@
+# if @REPLACE_LOGBF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef logbf
+# define logbf rpl_logbf
+# endif
+_GL_FUNCDECL_RPL (logbf, float, (float x));
+_GL_CXXALIAS_RPL (logbf, float, (float x));
+# else
+# if !@HAVE_LOGBF@
+_GL_FUNCDECL_SYS (logbf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (logbf, float, (float x));
+# endif
+_GL_CXXALIASWARN (logbf);
+#elif defined GNULIB_POSIXCHECK
+# undef logbf
+# if HAVE_RAW_DECL_LOGBF
+_GL_WARN_ON_USE (logbf, "logbf is unportable - "
+ "use gnulib module logbf for portability");
+# endif
+#endif
+
+#if @GNULIB_LOGB@
+# if @REPLACE_LOGB@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef logb
+# define logb rpl_logb
+# endif
+_GL_FUNCDECL_RPL (logb, double, (double x));
+_GL_CXXALIAS_RPL (logb, double, (double x));
+# else
+# if !@HAVE_DECL_LOGB@
+_GL_FUNCDECL_SYS (logb, double, (double x));
+# endif
+_GL_CXXALIAS_SYS (logb, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (logb, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef logb
+# if HAVE_RAW_DECL_LOGB
+_GL_WARN_ON_USE (logb, "logb is unportable - "
+ "use gnulib module logb for portability");
+# endif
+#endif
+
+#if @GNULIB_LOGBL@
+# if @REPLACE_LOGBL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef logbl
+# define logbl rpl_logbl
+# endif
+_GL_FUNCDECL_RPL (logbl, long double, (long double x));
+_GL_CXXALIAS_RPL (logbl, long double, (long double x));
+# else
+# if !@HAVE_LOGBL@
+_GL_FUNCDECL_SYS (logbl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (logbl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (logbl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef logbl
+# if HAVE_RAW_DECL_LOGBL
+_GL_WARN_ON_USE (logbl, "logbl is unportable - "
+ "use gnulib module logbl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_MODFF@
+# if @REPLACE_MODFF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef modff
+# define modff rpl_modff
+# endif
+_GL_FUNCDECL_RPL (modff, float, (float x, float *iptr) _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (modff, float, (float x, float *iptr));
+# else
+# if !@HAVE_MODFF@
+# undef modff
+_GL_FUNCDECL_SYS (modff, float, (float x, float *iptr) _GL_ARG_NONNULL ((2)));
+# endif
+_GL_CXXALIAS_SYS (modff, float, (float x, float *iptr));
+# endif
+_GL_CXXALIASWARN (modff);
+#elif defined GNULIB_POSIXCHECK
+# undef modff
+# if HAVE_RAW_DECL_MODFF
+_GL_WARN_ON_USE (modff, "modff is unportable - "
+ "use gnulib module modff for portability");
+# endif
+#endif
+
+#if @GNULIB_MODF@
+# if @REPLACE_MODF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef modf
+# define modf rpl_modf
+# endif
+_GL_FUNCDECL_RPL (modf, double, (double x, double *iptr) _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (modf, double, (double x, double *iptr));
+# else
+_GL_CXXALIAS_SYS (modf, double, (double x, double *iptr));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (modf, double, (double x, double *iptr));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef modf
+# if HAVE_RAW_DECL_MODF
+_GL_WARN_ON_USE (modf, "modf has portability problems - "
+ "use gnulib module modf for portability");
+# endif
+#endif
+
+#if @GNULIB_MODFL@
+# if @REPLACE_MODFL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef modfl
+# define modfl rpl_modfl
+# endif
+_GL_FUNCDECL_RPL (modfl, long double, (long double x, long double *iptr)
+ _GL_ARG_NONNULL ((2)));
+_GL_CXXALIAS_RPL (modfl, long double, (long double x, long double *iptr));
+# else
+# if !@HAVE_MODFL@
+# undef modfl
+_GL_FUNCDECL_SYS (modfl, long double, (long double x, long double *iptr)
+ _GL_ARG_NONNULL ((2)));
+# endif
+_GL_CXXALIAS_SYS (modfl, long double, (long double x, long double *iptr));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (modfl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef modfl
+# if HAVE_RAW_DECL_MODFL
+_GL_WARN_ON_USE (modfl, "modfl is unportable - "
+ "use gnulib module modfl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_POWF@
+# if !@HAVE_POWF@
+# undef powf
+_GL_FUNCDECL_SYS (powf, float, (float x, float y));
+# endif
+_GL_CXXALIAS_SYS (powf, float, (float x, float y));
+_GL_CXXALIASWARN (powf);
+#elif defined GNULIB_POSIXCHECK
+# undef powf
+# if HAVE_RAW_DECL_POWF
+_GL_WARN_ON_USE (powf, "powf is unportable - "
+ "use gnulib module powf for portability");
+# endif
+#endif
+
+
+#if @GNULIB_REMAINDERF@
+# if @REPLACE_REMAINDERF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef remainderf
+# define remainderf rpl_remainderf
+# endif
+_GL_FUNCDECL_RPL (remainderf, float, (float x, float y));
+_GL_CXXALIAS_RPL (remainderf, float, (float x, float y));
+# else
+# if !@HAVE_REMAINDERF@
+_GL_FUNCDECL_SYS (remainderf, float, (float x, float y));
+# endif
+_GL_CXXALIAS_SYS (remainderf, float, (float x, float y));
+# endif
+_GL_CXXALIASWARN (remainderf);
+#elif defined GNULIB_POSIXCHECK
+# undef remainderf
+# if HAVE_RAW_DECL_REMAINDERF
+_GL_WARN_ON_USE (remainderf, "remainderf is unportable - "
+ "use gnulib module remainderf for portability");
+# endif
+#endif
+
+#if @GNULIB_REMAINDER@
+# if @REPLACE_REMAINDER@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef remainder
+# define remainder rpl_remainder
+# endif
+_GL_FUNCDECL_RPL (remainder, double, (double x, double y));
+_GL_CXXALIAS_RPL (remainder, double, (double x, double y));
+# else
+# if !@HAVE_REMAINDER@ || !@HAVE_DECL_REMAINDER@
+_GL_FUNCDECL_SYS (remainder, double, (double x, double y));
+# endif
+_GL_CXXALIAS_SYS (remainder, double, (double x, double y));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (remainder, double, (double x, double y));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef remainder
+# if HAVE_RAW_DECL_REMAINDER
+_GL_WARN_ON_USE (remainder, "remainder is unportable - "
+ "use gnulib module remainder for portability");
+# endif
+#endif
+
+#if @GNULIB_REMAINDERL@
+# if @REPLACE_REMAINDERL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef remainderl
+# define remainderl rpl_remainderl
+# endif
+_GL_FUNCDECL_RPL (remainderl, long double, (long double x, long double y));
+_GL_CXXALIAS_RPL (remainderl, long double, (long double x, long double y));
+# else
+# if !@HAVE_DECL_REMAINDERL@
+# undef remainderl
+# if !(defined __cplusplus && defined _AIX)
+_GL_FUNCDECL_SYS (remainderl, long double, (long double x, long double y));
+# endif
+# endif
+_GL_CXXALIAS_SYS (remainderl, long double, (long double x, long double y));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (remainderl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef remainderl
+# if HAVE_RAW_DECL_REMAINDERL
+_GL_WARN_ON_USE (remainderl, "remainderl is unportable - "
+ "use gnulib module remainderl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_RINTF@
+# if !@HAVE_DECL_RINTF@
+_GL_FUNCDECL_SYS (rintf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (rintf, float, (float x));
+_GL_CXXALIASWARN (rintf);
+#elif defined GNULIB_POSIXCHECK
+# undef rintf
+# if HAVE_RAW_DECL_RINTF
+_GL_WARN_ON_USE (rintf, "rintf is unportable - "
+ "use gnulib module rintf for portability");
+# endif
+#endif
+
+#if @GNULIB_RINT@
+# if !@HAVE_RINT@
+_GL_FUNCDECL_SYS (rint, double, (double x));
+# endif
+_GL_CXXALIAS_SYS (rint, double, (double x));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (rint, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef rint
+# if HAVE_RAW_DECL_RINT
+_GL_WARN_ON_USE (rint, "rint is unportable - "
+ "use gnulib module rint for portability");
+# endif
+#endif
+
+#if @GNULIB_RINTL@
+# if @REPLACE_RINTL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef rintl
+# define rintl rpl_rintl
+# endif
+_GL_FUNCDECL_RPL (rintl, long double, (long double x));
+_GL_CXXALIAS_RPL (rintl, long double, (long double x));
+# else
+# if !@HAVE_RINTL@
+_GL_FUNCDECL_SYS (rintl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (rintl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (rintl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef rintl
+# if HAVE_RAW_DECL_RINTL
+_GL_WARN_ON_USE (rintl, "rintl is unportable - "
+ "use gnulib module rintl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_ROUNDF@
+# if @REPLACE_ROUNDF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef roundf
+# define roundf rpl_roundf
+# endif
+_GL_FUNCDECL_RPL (roundf, float, (float x));
+_GL_CXXALIAS_RPL (roundf, float, (float x));
+# else
+# if !@HAVE_DECL_ROUNDF@
+_GL_FUNCDECL_SYS (roundf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (roundf, float, (float x));
+# endif
+_GL_CXXALIASWARN (roundf);
+#elif defined GNULIB_POSIXCHECK
+# undef roundf
+# if HAVE_RAW_DECL_ROUNDF
+_GL_WARN_ON_USE (roundf, "roundf is unportable - "
+ "use gnulib module roundf for portability");
+# endif
+#endif
+
+#if @GNULIB_ROUND@
+# if @REPLACE_ROUND@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef round
+# define round rpl_round
+# endif
+_GL_FUNCDECL_RPL (round, double, (double x));
+_GL_CXXALIAS_RPL (round, double, (double x));
+# else
+# if !@HAVE_DECL_ROUND@
+_GL_FUNCDECL_SYS (round, double, (double x));
+# endif
+_GL_CXXALIAS_SYS (round, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (round, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef round
+# if HAVE_RAW_DECL_ROUND
+_GL_WARN_ON_USE (round, "round is unportable - "
+ "use gnulib module round for portability");
+# endif
+#endif
+
+#if @GNULIB_ROUNDL@
+# if @REPLACE_ROUNDL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef roundl
+# define roundl rpl_roundl
+# endif
+_GL_FUNCDECL_RPL (roundl, long double, (long double x));
+_GL_CXXALIAS_RPL (roundl, long double, (long double x));
+# else
+# if !@HAVE_DECL_ROUNDL@
+# undef roundl
+# if !(defined __cplusplus && defined _AIX)
+_GL_FUNCDECL_SYS (roundl, long double, (long double x));
+# endif
+# endif
+_GL_CXXALIAS_SYS (roundl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (roundl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef roundl
+# if HAVE_RAW_DECL_ROUNDL
+_GL_WARN_ON_USE (roundl, "roundl is unportable - "
+ "use gnulib module roundl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_SINF@
+# if @REPLACE_SINF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef sinf
+# define sinf rpl_sinf
+# endif
+_GL_FUNCDECL_RPL (sinf, float, (float x));
+_GL_CXXALIAS_RPL (sinf, float, (float x));
+# else
+# if !@HAVE_SINF@
+# undef sinf
+_GL_FUNCDECL_SYS (sinf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (sinf, float, (float x));
+# endif
+_GL_CXXALIASWARN (sinf);
+#elif defined GNULIB_POSIXCHECK
+# undef sinf
+# if HAVE_RAW_DECL_SINF
+_GL_WARN_ON_USE (sinf, "sinf is unportable - "
+ "use gnulib module sinf for portability");
+# endif
+#endif
+
+#if @GNULIB_SINL@
+# if !@HAVE_SINL@ || !@HAVE_DECL_SINL@
+# undef sinl
+_GL_FUNCDECL_SYS (sinl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (sinl, long double, (long double x));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (sinl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef sinl
+# if HAVE_RAW_DECL_SINL
+_GL_WARN_ON_USE (sinl, "sinl is unportable - "
+ "use gnulib module sinl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_SINHF@
+# if @REPLACE_SINHF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef sinhf
+# define sinhf rpl_sinhf
+# endif
+_GL_FUNCDECL_RPL (sinhf, float, (float x));
+_GL_CXXALIAS_RPL (sinhf, float, (float x));
+# else
+# if !@HAVE_SINHF@
+# undef sinhf
+_GL_FUNCDECL_SYS (sinhf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (sinhf, float, (float x));
+# endif
+_GL_CXXALIASWARN (sinhf);
+#elif defined GNULIB_POSIXCHECK
+# undef sinhf
+# if HAVE_RAW_DECL_SINHF
+_GL_WARN_ON_USE (sinhf, "sinhf is unportable - "
+ "use gnulib module sinhf for portability");
+# endif
+#endif
+
+
+#if @GNULIB_SQRTF@
+# if @REPLACE_SQRTF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef sqrtf
+# define sqrtf rpl_sqrtf
+# endif
+_GL_FUNCDECL_RPL (sqrtf, float, (float x));
+_GL_CXXALIAS_RPL (sqrtf, float, (float x));
+# else
+# if !@HAVE_SQRTF@
+# undef sqrtf
+_GL_FUNCDECL_SYS (sqrtf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (sqrtf, float, (float x));
+# endif
+_GL_CXXALIASWARN (sqrtf);
+#elif defined GNULIB_POSIXCHECK
+# undef sqrtf
+# if HAVE_RAW_DECL_SQRTF
+_GL_WARN_ON_USE (sqrtf, "sqrtf is unportable - "
+ "use gnulib module sqrtf for portability");
+# endif
+#endif
+
+#if @GNULIB_SQRTL@
+# if @REPLACE_SQRTL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef sqrtl
+# define sqrtl rpl_sqrtl
+# endif
+_GL_FUNCDECL_RPL (sqrtl, long double, (long double x));
+_GL_CXXALIAS_RPL (sqrtl, long double, (long double x));
+# else
+# if !@HAVE_SQRTL@ || !@HAVE_DECL_SQRTL@
+# undef sqrtl
+_GL_FUNCDECL_SYS (sqrtl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (sqrtl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (sqrtl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef sqrtl
+# if HAVE_RAW_DECL_SQRTL
+_GL_WARN_ON_USE (sqrtl, "sqrtl is unportable - "
+ "use gnulib module sqrtl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_TANF@
+# if @REPLACE_TANF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef tanf
+# define tanf rpl_tanf
+# endif
+_GL_FUNCDECL_RPL (tanf, float, (float x));
+_GL_CXXALIAS_RPL (tanf, float, (float x));
+# else
+# if !@HAVE_TANF@
+# undef tanf
+_GL_FUNCDECL_SYS (tanf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (tanf, float, (float x));
+# endif
+_GL_CXXALIASWARN (tanf);
+#elif defined GNULIB_POSIXCHECK
+# undef tanf
+# if HAVE_RAW_DECL_TANF
+_GL_WARN_ON_USE (tanf, "tanf is unportable - "
+ "use gnulib module tanf for portability");
+# endif
+#endif
+
+#if @GNULIB_TANL@
+# if !@HAVE_TANL@ || !@HAVE_DECL_TANL@
+# undef tanl
+_GL_FUNCDECL_SYS (tanl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (tanl, long double, (long double x));
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (tanl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef tanl
+# if HAVE_RAW_DECL_TANL
+_GL_WARN_ON_USE (tanl, "tanl is unportable - "
+ "use gnulib module tanl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_TANHF@
+# if @REPLACE_TANHF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef tanhf
+# define tanhf rpl_tanhf
+# endif
+_GL_FUNCDECL_RPL (tanhf, float, (float x));
+_GL_CXXALIAS_RPL (tanhf, float, (float x));
+# else
+# if !@HAVE_TANHF@
+# undef tanhf
+_GL_FUNCDECL_SYS (tanhf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (tanhf, float, (float x));
+# endif
+_GL_CXXALIASWARN (tanhf);
+#elif defined GNULIB_POSIXCHECK
+# undef tanhf
+# if HAVE_RAW_DECL_TANHF
+_GL_WARN_ON_USE (tanhf, "tanhf is unportable - "
+ "use gnulib module tanhf for portability");
+# endif
+#endif
+
+
+#if @GNULIB_TRUNCF@
+# if @REPLACE_TRUNCF@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef truncf
+# define truncf rpl_truncf
+# endif
+_GL_FUNCDECL_RPL (truncf, float, (float x));
+_GL_CXXALIAS_RPL (truncf, float, (float x));
+# else
+# if !@HAVE_DECL_TRUNCF@
+_GL_FUNCDECL_SYS (truncf, float, (float x));
+# endif
+_GL_CXXALIAS_SYS (truncf, float, (float x));
+# endif
+_GL_CXXALIASWARN (truncf);
+#elif defined GNULIB_POSIXCHECK
+# undef truncf
+# if HAVE_RAW_DECL_TRUNCF
+_GL_WARN_ON_USE (truncf, "truncf is unportable - "
+ "use gnulib module truncf for portability");
+# endif
+#endif
+
+#if @GNULIB_TRUNC@
+# if @REPLACE_TRUNC@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef trunc
+# define trunc rpl_trunc
+# endif
+_GL_FUNCDECL_RPL (trunc, double, (double x));
+_GL_CXXALIAS_RPL (trunc, double, (double x));
+# else
+# if !@HAVE_DECL_TRUNC@
+_GL_FUNCDECL_SYS (trunc, double, (double x));
+# endif
+_GL_CXXALIAS_SYS (trunc, double, (double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN1 (trunc, double, (double x));
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef trunc
+# if HAVE_RAW_DECL_TRUNC
+_GL_WARN_ON_USE (trunc, "trunc is unportable - "
+ "use gnulib module trunc for portability");
+# endif
+#endif
+
+#if @GNULIB_TRUNCL@
+# if @REPLACE_TRUNCL@
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef truncl
+# define truncl rpl_truncl
+# endif
+_GL_FUNCDECL_RPL (truncl, long double, (long double x));
+_GL_CXXALIAS_RPL (truncl, long double, (long double x));
+# else
+# if !@HAVE_DECL_TRUNCL@
+_GL_FUNCDECL_SYS (truncl, long double, (long double x));
+# endif
+_GL_CXXALIAS_SYS (truncl, long double, (long double x));
+# endif
+# if __GLIBC__ >= 2
+_GL_CXXALIASWARN (truncl);
+# endif
+#elif defined GNULIB_POSIXCHECK
+# undef truncl
+# if HAVE_RAW_DECL_TRUNCL
+_GL_WARN_ON_USE (truncl, "truncl is unportable - "
+ "use gnulib module truncl for portability");
+# endif
+#endif
+
+
+#if @GNULIB_MDA_Y0@
+/* On native Windows, map 'y0' to '_y0', so that -loldnames is not
+ required. In C++ with GNULIB_NAMESPACE, avoid differences between
+ platforms by defining GNULIB_NAMESPACE::y0 always. */
+# if defined _WIN32 && !defined __CYGWIN__
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef y0
+# define y0 _y0
+# endif
+_GL_CXXALIAS_MDA (y0, double, (double x));
+# else
+_GL_CXXALIAS_SYS (y0, double, (double x));
+# endif
+_GL_CXXALIASWARN (y0);
+#endif
+
+#if @GNULIB_MDA_Y1@
+/* On native Windows, map 'y1' to '_y1', so that -loldnames is not
+ required. In C++ with GNULIB_NAMESPACE, avoid differences between
+ platforms by defining GNULIB_NAMESPACE::y1 always. */
+# if defined _WIN32 && !defined __CYGWIN__
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef y1
+# define y1 _y1
+# endif
+_GL_CXXALIAS_MDA (y1, double, (double x));
+# else
+_GL_CXXALIAS_SYS (y1, double, (double x));
+# endif
+_GL_CXXALIASWARN (y1);
+#endif
+
+#if @GNULIB_MDA_YN@
+/* On native Windows, map 'yn' to '_yn', so that -loldnames is not
+ required. In C++ with GNULIB_NAMESPACE, avoid differences between
+ platforms by defining GNULIB_NAMESPACE::yn always. */
+# if defined _WIN32 && !defined __CYGWIN__
+# if !(defined __cplusplus && defined GNULIB_NAMESPACE)
+# undef yn
+# define yn _yn
+# endif
+_GL_CXXALIAS_MDA (yn, double, (int n, double x));
+# else
+_GL_CXXALIAS_SYS (yn, double, (int n, double x));
+# endif
+_GL_CXXALIASWARN (yn);
+#endif
+
+
+/* Definitions of function-like macros come here, after the function
+ declarations. */
+
+
+#if @GNULIB_ISFINITE@
+# if @REPLACE_ISFINITE@
+_GL_EXTERN_C int gl_isfinitef (float x);
+_GL_EXTERN_C int gl_isfinited (double x);
+_GL_EXTERN_C int gl_isfinitel (long double x);
+# undef isfinite
+# define isfinite(x) \
+ (sizeof (x) == sizeof (long double) ? gl_isfinitel (x) : \
+ sizeof (x) == sizeof (double) ? gl_isfinited (x) : \
+ gl_isfinitef (x))
+# endif
+# ifdef __cplusplus
+# if defined isfinite || defined GNULIB_NAMESPACE
+_GL_MATH_CXX_REAL_FLOATING_DECL_1 (isfinite)
+# undef isfinite
+# if __GNUC__ >= 6 || (defined __clang__ && !((defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __OpenBSD__ || defined _AIX || (defined _WIN32 && !defined __CYGWIN__)))
+ /* This platform's <cmath> possibly defines isfinite through a set of inline
+ functions. */
+_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isfinite, rpl_isfinite, bool)
+# define isfinite rpl_isfinite
+# define GNULIB_NAMESPACE_LACKS_ISFINITE 1
+# else
+_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isfinite, isfinite, bool)
+# endif
+# endif
+# endif
+#elif defined GNULIB_POSIXCHECK
+# if defined isfinite
+_GL_WARN_REAL_FLOATING_DECL (isfinite);
+# undef isfinite
+# define isfinite(x) _GL_WARN_REAL_FLOATING_IMPL (isfinite, x)
+# endif
+#endif
+
+
+#if @GNULIB_ISINF@
+# if @REPLACE_ISINF@
+_GL_EXTERN_C int gl_isinff (float x);
+_GL_EXTERN_C int gl_isinfd (double x);
+_GL_EXTERN_C int gl_isinfl (long double x);
+# undef isinf
+# define isinf(x) \
+ (sizeof (x) == sizeof (long double) ? gl_isinfl (x) : \
+ sizeof (x) == sizeof (double) ? gl_isinfd (x) : \
+ gl_isinff (x))
+# endif
+# ifdef __cplusplus
+# if defined isinf || defined GNULIB_NAMESPACE
+_GL_MATH_CXX_REAL_FLOATING_DECL_1 (isinf)
+# undef isinf
+# if __GNUC__ >= 6 || (defined __clang__ && !((defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __OpenBSD__ || (defined _WIN32 && !defined __CYGWIN__)))
+ /* This platform's <cmath> possibly defines isinf through a set of inline
+ functions. */
+_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isinf, rpl_isinf, bool)
+# define isinf rpl_isinf
+# define GNULIB_NAMESPACE_LACKS_ISINF 1
+# else
+_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isinf, isinf, bool)
+# endif
+# endif
+# endif
+#elif defined GNULIB_POSIXCHECK
+# if defined isinf
+_GL_WARN_REAL_FLOATING_DECL (isinf);
+# undef isinf
+# define isinf(x) _GL_WARN_REAL_FLOATING_IMPL (isinf, x)
+# endif
+#endif
+
+
+#if @GNULIB_ISNANF@
+/* Test for NaN for 'float' numbers. */
+# if @HAVE_ISNANF@
+/* The original <math.h> included above provides a declaration of isnan macro
+ or (older) isnanf function. */
+# if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ /* GCC >= 4.0 and clang provide a type-generic built-in for isnan.
+ GCC >= 4.0 also provides __builtin_isnanf, but clang doesn't. */
+# undef isnanf
+# define isnanf(x) __builtin_isnan ((float)(x))
+# elif defined isnan
+# undef isnanf
+# define isnanf(x) isnan ((float)(x))
+# endif
+# else
+/* Test whether X is a NaN. */
+# undef isnanf
+# define isnanf rpl_isnanf
+_GL_EXTERN_C int isnanf (float x);
+# endif
+#endif
+
+#if @GNULIB_ISNAND@
+/* Test for NaN for 'double' numbers.
+ This function is a gnulib extension, unlike isnan() which applied only
+ to 'double' numbers earlier but now is a type-generic macro. */
+# if @HAVE_ISNAND@
+/* The original <math.h> included above provides a declaration of isnan
+ macro. */
+# if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ /* GCC >= 4.0 and clang provide a type-generic built-in for isnan. */
+# undef isnand
+# define isnand(x) __builtin_isnan ((double)(x))
+# else
+# undef isnand
+# define isnand(x) isnan ((double)(x))
+# endif
+# else
+/* Test whether X is a NaN. */
+# undef isnand
+# define isnand rpl_isnand
+_GL_EXTERN_C int isnand (double x);
+# endif
+#endif
+
+#if @GNULIB_ISNANL@
+/* Test for NaN for 'long double' numbers. */
+# if @HAVE_ISNANL@
+/* The original <math.h> included above provides a declaration of isnan
+ macro or (older) isnanl function. */
+# if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ /* GCC >= 4.0 and clang provide a type-generic built-in for isnan.
+ GCC >= 4.0 also provides __builtin_isnanl, but clang doesn't. */
+# undef isnanl
+# define isnanl(x) __builtin_isnan ((long double)(x))
+# elif defined isnan
+# undef isnanl
+# define isnanl(x) isnan ((long double)(x))
+# endif
+# else
+/* Test whether X is a NaN. */
+# undef isnanl
+# define isnanl rpl_isnanl
+_GL_EXTERN_C int isnanl (long double x) _GL_ATTRIBUTE_CONST;
+# endif
+#endif
+
+/* This must come *after* the snippets for GNULIB_ISNANF and GNULIB_ISNANL! */
+#if @GNULIB_ISNAN@
+# if @REPLACE_ISNAN@
+/* We can't just use the isnanf macro (e.g.) as exposed by
+ isnanf.h (e.g.) here, because those may end up being macros
+ that recursively expand back to isnan. So use the gnulib
+ replacements for them directly. */
+# if @HAVE_ISNANF@ && (__GNUC__ >= 4) || (__clang_major__ >= 4)
+# define gl_isnan_f(x) __builtin_isnan ((float)(x))
+# else
+_GL_EXTERN_C int rpl_isnanf (float x);
+# define gl_isnan_f(x) rpl_isnanf (x)
+# endif
+# if @HAVE_ISNAND@ && (__GNUC__ >= 4) || (__clang_major__ >= 4)
+# define gl_isnan_d(x) __builtin_isnan ((double)(x))
+# else
+_GL_EXTERN_C int rpl_isnand (double x);
+# define gl_isnan_d(x) rpl_isnand (x)
+# endif
+# if @HAVE_ISNANL@ && (__GNUC__ >= 4) || (__clang_major__ >= 4)
+# define gl_isnan_l(x) __builtin_isnan ((long double)(x))
+# else
+_GL_EXTERN_C int rpl_isnanl (long double x) _GL_ATTRIBUTE_CONST;
+# define gl_isnan_l(x) rpl_isnanl (x)
+# endif
+# undef isnan
+# define isnan(x) \
+ (sizeof (x) == sizeof (long double) ? gl_isnan_l (x) : \
+ sizeof (x) == sizeof (double) ? gl_isnan_d (x) : \
+ gl_isnan_f (x))
+# elif (__GNUC__ >= 4) || (__clang_major__ >= 4)
+# undef isnan
+# define isnan(x) \
+ (sizeof (x) == sizeof (long double) ? __builtin_isnan ((long double)(x)) : \
+ sizeof (x) == sizeof (double) ? __builtin_isnan ((double)(x)) : \
+ __builtin_isnan ((float)(x)))
+# endif
+# ifdef __cplusplus
+# if defined isnan || defined GNULIB_NAMESPACE
+_GL_MATH_CXX_REAL_FLOATING_DECL_1 (isnan)
+# undef isnan
+# if __GNUC__ >= 6 || (defined __clang__ && !((defined __APPLE__ && defined __MACH__ && __clang_major__ != 12) || (defined __FreeBSD__ && (__clang_major__ < 7 || __clang_major__ >= 11)) || defined __OpenBSD__ || (defined _WIN32 && !defined __CYGWIN__)))
+ /* This platform's <cmath> possibly defines isnan through a set of inline
+ functions. */
+_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isnan, rpl_isnan, bool)
+# define isnan rpl_isnan
+# define GNULIB_NAMESPACE_LACKS_ISNAN 1
+# else
+_GL_MATH_CXX_REAL_FLOATING_DECL_2 (isnan, isnan, bool)
+# endif
+# endif
+# else
+/* Ensure isnan is a macro. */
+# ifndef isnan
+# define isnan isnan
+# endif
+# endif
+#elif defined GNULIB_POSIXCHECK
+# if defined isnan
+_GL_WARN_REAL_FLOATING_DECL (isnan);
+# undef isnan
+# define isnan(x) _GL_WARN_REAL_FLOATING_IMPL (isnan, x)
+# endif
+#endif
+
+
+#if @GNULIB_SIGNBIT@
+# if (@REPLACE_SIGNBIT_USING_BUILTINS@ \
+ && (!defined __cplusplus || __cplusplus < 201103))
+# undef signbit
+ /* GCC >= 4.0 and clang provide three built-ins for signbit. */
+# define signbit(x) \
+ (sizeof (x) == sizeof (long double) ? __builtin_signbitl (x) : \
+ sizeof (x) == sizeof (double) ? __builtin_signbit (x) : \
+ __builtin_signbitf (x))
+# endif
+# if @REPLACE_SIGNBIT@ && !GNULIB_defined_signbit
+# undef signbit
+_GL_EXTERN_C int gl_signbitf (float arg);
+_GL_EXTERN_C int gl_signbitd (double arg);
+_GL_EXTERN_C int gl_signbitl (long double arg);
+# if (__GNUC__ >= 2 || defined __clang__) && !defined __STRICT_ANSI__
+# define _GL_NUM_UINT_WORDS(type) \
+ ((sizeof (type) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+# if defined FLT_SIGNBIT_WORD && defined FLT_SIGNBIT_BIT && !defined gl_signbitf
+# define gl_signbitf_OPTIMIZED_MACRO
+# define gl_signbitf(arg) \
+ ({ union { float _value; \
+ unsigned int _word[_GL_NUM_UINT_WORDS (float)]; \
+ } _m; \
+ _m._value = (arg); \
+ (_m._word[FLT_SIGNBIT_WORD] >> FLT_SIGNBIT_BIT) & 1; \
+ })
+# endif
+# if defined DBL_SIGNBIT_WORD && defined DBL_SIGNBIT_BIT && !defined gl_signbitd
+# define gl_signbitd_OPTIMIZED_MACRO
+# define gl_signbitd(arg) \
+ ({ union { double _value; \
+ unsigned int _word[_GL_NUM_UINT_WORDS (double)]; \
+ } _m; \
+ _m._value = (arg); \
+ (_m._word[DBL_SIGNBIT_WORD] >> DBL_SIGNBIT_BIT) & 1; \
+ })
+# endif
+# if defined LDBL_SIGNBIT_WORD && defined LDBL_SIGNBIT_BIT && !defined gl_signbitl
+# define gl_signbitl_OPTIMIZED_MACRO
+# define gl_signbitl(arg) \
+ ({ union { long double _value; \
+ unsigned int _word[_GL_NUM_UINT_WORDS (long double)]; \
+ } _m; \
+ _m._value = (arg); \
+ (_m._word[LDBL_SIGNBIT_WORD] >> LDBL_SIGNBIT_BIT) & 1; \
+ })
+# endif
+# endif
+# define signbit(x) \
+ (sizeof (x) == sizeof (long double) ? gl_signbitl (x) : \
+ sizeof (x) == sizeof (double) ? gl_signbitd (x) : \
+ gl_signbitf (x))
+# define GNULIB_defined_signbit 1
+# endif
+# ifdef __cplusplus
+# if defined signbit || defined GNULIB_NAMESPACE
+_GL_MATH_CXX_REAL_FLOATING_DECL_1 (signbit)
+# undef signbit
+# if __GNUC__ >= 6 || (defined __clang__ && !((defined __APPLE__ && defined __MACH__) || defined __FreeBSD__ || defined __OpenBSD__ || defined _AIX || (defined _WIN32 && !defined __CYGWIN__)))
+ /* This platform's <cmath> possibly defines signbit through a set of inline
+ functions. */
+_GL_MATH_CXX_REAL_FLOATING_DECL_2 (signbit, rpl_signbit, bool)
+# define signbit rpl_signbit
+# define GNULIB_NAMESPACE_LACKS_SIGNBIT 1
+# else
+_GL_MATH_CXX_REAL_FLOATING_DECL_2 (signbit, signbit, bool)
+# endif
+# endif
+# endif
+#elif defined GNULIB_POSIXCHECK
+# if defined signbit
+_GL_WARN_REAL_FLOATING_DECL (signbit);
+# undef signbit
+# define signbit(x) _GL_WARN_REAL_FLOATING_IMPL (signbit, x)
+# endif
+#endif
+
+_GL_INLINE_HEADER_END
+
+#endif /* _@GUARD_PREFIX@_MATH_H */
+#endif /* _GL_INCLUDING_MATH_H */
+#endif /* _@GUARD_PREFIX@_MATH_H */
+#endif
diff --git a/lib/printf-args.c b/lib/printf-args.c
new file mode 100644
index 00000000000..5e14f654794
--- /dev/null
+++ b/lib/printf-args.c
@@ -0,0 +1,183 @@
+/* Decomposed printf argument list.
+ Copyright (C) 1999, 2002-2003, 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/>. */
+
+/* This file can be parametrized with the following macros:
+ ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions.
+ PRINTF_FETCHARGS Name of the function to be defined.
+ STATIC Set to 'static' to declare the function static. */
+
+#ifndef PRINTF_FETCHARGS
+# include <config.h>
+#endif
+
+/* Specification. */
+#ifndef PRINTF_FETCHARGS
+# include "printf-args.h"
+#endif
+
+#ifdef STATIC
+STATIC
+#endif
+int
+PRINTF_FETCHARGS (va_list args, arguments *a)
+{
+ size_t i;
+ argument *ap;
+
+ for (i = 0, ap = &a->arg[0]; i < a->count; i++, ap++)
+ switch (ap->type)
+ {
+ case TYPE_SCHAR:
+ ap->a.a_schar = va_arg (args, /*signed char*/ int);
+ break;
+ case TYPE_UCHAR:
+ ap->a.a_uchar = va_arg (args, /*unsigned char*/ int);
+ break;
+ case TYPE_SHORT:
+ ap->a.a_short = va_arg (args, /*short*/ int);
+ break;
+ case TYPE_USHORT:
+ ap->a.a_ushort = va_arg (args, /*unsigned short*/ int);
+ break;
+ case TYPE_INT:
+ ap->a.a_int = va_arg (args, int);
+ break;
+ case TYPE_UINT:
+ ap->a.a_uint = va_arg (args, unsigned int);
+ break;
+ case TYPE_LONGINT:
+ ap->a.a_longint = va_arg (args, long int);
+ break;
+ case TYPE_ULONGINT:
+ ap->a.a_ulongint = va_arg (args, unsigned long int);
+ break;
+ case TYPE_LONGLONGINT:
+ ap->a.a_longlongint = va_arg (args, long long int);
+ break;
+ case TYPE_ULONGLONGINT:
+ ap->a.a_ulonglongint = va_arg (args, unsigned long long int);
+ break;
+ case TYPE_DOUBLE:
+ ap->a.a_double = va_arg (args, double);
+ break;
+ case TYPE_LONGDOUBLE:
+ ap->a.a_longdouble = va_arg (args, long double);
+ break;
+ case TYPE_CHAR:
+ ap->a.a_char = va_arg (args, int);
+ break;
+#if HAVE_WINT_T
+ case TYPE_WIDE_CHAR:
+ /* Although ISO C 99 7.24.1.(2) says that wint_t is "unchanged by
+ default argument promotions", this is not the case in mingw32,
+ where wint_t is 'unsigned short'. */
+ ap->a.a_wide_char =
+ (sizeof (wint_t) < sizeof (int)
+ ? (wint_t) va_arg (args, int)
+ : va_arg (args, wint_t));
+ break;
+#endif
+ case TYPE_STRING:
+ ap->a.a_string = va_arg (args, const char *);
+ /* A null pointer is an invalid argument for "%s", but in practice
+ it occurs quite frequently in printf statements that produce
+ debug output. Use a fallback in this case. */
+ if (ap->a.a_string == NULL)
+ ap->a.a_string = "(NULL)";
+ break;
+#if HAVE_WCHAR_T
+ case TYPE_WIDE_STRING:
+ ap->a.a_wide_string = va_arg (args, const wchar_t *);
+ /* A null pointer is an invalid argument for "%ls", but in practice
+ it occurs quite frequently in printf statements that produce
+ debug output. Use a fallback in this case. */
+ if (ap->a.a_wide_string == NULL)
+ {
+ static const wchar_t wide_null_string[] =
+ {
+ (wchar_t)'(',
+ (wchar_t)'N', (wchar_t)'U', (wchar_t)'L', (wchar_t)'L',
+ (wchar_t)')',
+ (wchar_t)0
+ };
+ ap->a.a_wide_string = wide_null_string;
+ }
+ break;
+#endif
+ case TYPE_POINTER:
+ ap->a.a_pointer = va_arg (args, void *);
+ break;
+ case TYPE_COUNT_SCHAR_POINTER:
+ ap->a.a_count_schar_pointer = va_arg (args, signed char *);
+ break;
+ case TYPE_COUNT_SHORT_POINTER:
+ ap->a.a_count_short_pointer = va_arg (args, short *);
+ break;
+ case TYPE_COUNT_INT_POINTER:
+ ap->a.a_count_int_pointer = va_arg (args, int *);
+ break;
+ case TYPE_COUNT_LONGINT_POINTER:
+ ap->a.a_count_longint_pointer = va_arg (args, long int *);
+ break;
+ case TYPE_COUNT_LONGLONGINT_POINTER:
+ ap->a.a_count_longlongint_pointer = va_arg (args, long long int *);
+ break;
+#if ENABLE_UNISTDIO
+ /* The unistdio extensions. */
+ case TYPE_U8_STRING:
+ ap->a.a_u8_string = va_arg (args, const uint8_t *);
+ /* A null pointer is an invalid argument for "%U", but in practice
+ it occurs quite frequently in printf statements that produce
+ debug output. Use a fallback in this case. */
+ if (ap->a.a_u8_string == NULL)
+ {
+ static const uint8_t u8_null_string[] =
+ { '(', 'N', 'U', 'L', 'L', ')', 0 };
+ ap->a.a_u8_string = u8_null_string;
+ }
+ break;
+ case TYPE_U16_STRING:
+ ap->a.a_u16_string = va_arg (args, const uint16_t *);
+ /* A null pointer is an invalid argument for "%lU", but in practice
+ it occurs quite frequently in printf statements that produce
+ debug output. Use a fallback in this case. */
+ if (ap->a.a_u16_string == NULL)
+ {
+ static const uint16_t u16_null_string[] =
+ { '(', 'N', 'U', 'L', 'L', ')', 0 };
+ ap->a.a_u16_string = u16_null_string;
+ }
+ break;
+ case TYPE_U32_STRING:
+ ap->a.a_u32_string = va_arg (args, const uint32_t *);
+ /* A null pointer is an invalid argument for "%llU", but in practice
+ it occurs quite frequently in printf statements that produce
+ debug output. Use a fallback in this case. */
+ if (ap->a.a_u32_string == NULL)
+ {
+ static const uint32_t u32_null_string[] =
+ { '(', 'N', 'U', 'L', 'L', ')', 0 };
+ ap->a.a_u32_string = u32_null_string;
+ }
+ break;
+#endif
+ default:
+ /* Unknown type. */
+ return -1;
+ }
+ return 0;
+}
diff --git a/lib/printf-args.h b/lib/printf-args.h
new file mode 100644
index 00000000000..f303cb19e9b
--- /dev/null
+++ b/lib/printf-args.h
@@ -0,0 +1,150 @@
+/* Decomposed printf argument list.
+ Copyright (C) 1999, 2002-2003, 2006-2007, 2011-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/>. */
+
+#ifndef _PRINTF_ARGS_H
+#define _PRINTF_ARGS_H
+
+/* This file can be parametrized with the following macros:
+ ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions.
+ PRINTF_FETCHARGS Name of the function to be declared.
+ STATIC Set to 'static' to declare the function static. */
+
+/* Default parameters. */
+#ifndef PRINTF_FETCHARGS
+# define PRINTF_FETCHARGS printf_fetchargs
+#endif
+
+/* Get size_t. */
+#include <stddef.h>
+
+/* Get wchar_t. */
+#if HAVE_WCHAR_T
+# include <stddef.h>
+#endif
+
+/* Get wint_t. */
+#if HAVE_WINT_T
+# include <wchar.h>
+#endif
+
+/* Get va_list. */
+#include <stdarg.h>
+
+
+/* Argument types */
+typedef enum
+{
+ TYPE_NONE,
+ TYPE_SCHAR,
+ TYPE_UCHAR,
+ TYPE_SHORT,
+ TYPE_USHORT,
+ TYPE_INT,
+ TYPE_UINT,
+ TYPE_LONGINT,
+ TYPE_ULONGINT,
+ TYPE_LONGLONGINT,
+ TYPE_ULONGLONGINT,
+ TYPE_DOUBLE,
+ TYPE_LONGDOUBLE,
+ TYPE_CHAR,
+#if HAVE_WINT_T
+ TYPE_WIDE_CHAR,
+#endif
+ TYPE_STRING,
+#if HAVE_WCHAR_T
+ TYPE_WIDE_STRING,
+#endif
+ TYPE_POINTER,
+ TYPE_COUNT_SCHAR_POINTER,
+ TYPE_COUNT_SHORT_POINTER,
+ TYPE_COUNT_INT_POINTER,
+ TYPE_COUNT_LONGINT_POINTER,
+ TYPE_COUNT_LONGLONGINT_POINTER
+#if ENABLE_UNISTDIO
+ /* The unistdio extensions. */
+, TYPE_U8_STRING
+, TYPE_U16_STRING
+, TYPE_U32_STRING
+#endif
+} arg_type;
+
+/* Polymorphic argument */
+typedef struct
+{
+ arg_type type;
+ union
+ {
+ signed char a_schar;
+ unsigned char a_uchar;
+ short a_short;
+ unsigned short a_ushort;
+ int a_int;
+ unsigned int a_uint;
+ long int a_longint;
+ unsigned long int a_ulongint;
+ long long int a_longlongint;
+ unsigned long long int a_ulonglongint;
+ float a_float;
+ double a_double;
+ long double a_longdouble;
+ int a_char;
+#if HAVE_WINT_T
+ wint_t a_wide_char;
+#endif
+ const char* a_string;
+#if HAVE_WCHAR_T
+ const wchar_t* a_wide_string;
+#endif
+ void* a_pointer;
+ signed char * a_count_schar_pointer;
+ short * a_count_short_pointer;
+ int * a_count_int_pointer;
+ long int * a_count_longint_pointer;
+ long long int * a_count_longlongint_pointer;
+#if ENABLE_UNISTDIO
+ /* The unistdio extensions. */
+ const uint8_t * a_u8_string;
+ const uint16_t * a_u16_string;
+ const uint32_t * a_u32_string;
+#endif
+ }
+ a;
+}
+argument;
+
+/* Number of directly allocated arguments (no malloc() needed). */
+#define N_DIRECT_ALLOC_ARGUMENTS 7
+
+typedef struct
+{
+ size_t count;
+ argument *arg;
+ argument direct_alloc_arg[N_DIRECT_ALLOC_ARGUMENTS];
+}
+arguments;
+
+
+/* Fetch the arguments, putting them into a. */
+#ifdef STATIC
+STATIC
+#else
+extern
+#endif
+int PRINTF_FETCHARGS (va_list args, arguments *a);
+
+#endif /* _PRINTF_ARGS_H */
diff --git a/lib/printf-frexp.c b/lib/printf-frexp.c
new file mode 100644
index 00000000000..8252b0656cb
--- /dev/null
+++ b/lib/printf-frexp.c
@@ -0,0 +1,190 @@
+/* Split a double into fraction and mantissa, for hexadecimal printf.
+ Copyright (C) 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/>. */
+
+#if ! defined USE_LONG_DOUBLE
+# include <config.h>
+#endif
+
+/* Specification. */
+#ifdef USE_LONG_DOUBLE
+# include "printf-frexpl.h"
+#else
+# include "printf-frexp.h"
+#endif
+
+#include <float.h>
+#include <math.h>
+#ifdef USE_LONG_DOUBLE
+# include "fpucw.h"
+#endif
+
+/* This file assumes FLT_RADIX = 2. If FLT_RADIX is a power of 2 greater
+ than 2, or not even a power of 2, some rounding errors can occur, so that
+ then the returned mantissa is only guaranteed to be <= 2.0, not < 2.0. */
+
+#ifdef USE_LONG_DOUBLE
+# define FUNC printf_frexpl
+# define DOUBLE long double
+# define MIN_EXP LDBL_MIN_EXP
+# if HAVE_FREXPL_IN_LIBC && HAVE_LDEXPL_IN_LIBC
+# define USE_FREXP_LDEXP
+# define FREXP frexpl
+# define LDEXP ldexpl
+# endif
+# define DECL_ROUNDING DECL_LONG_DOUBLE_ROUNDING
+# define BEGIN_ROUNDING() BEGIN_LONG_DOUBLE_ROUNDING ()
+# define END_ROUNDING() END_LONG_DOUBLE_ROUNDING ()
+# define L_(literal) literal##L
+#else
+# define FUNC printf_frexp
+# define DOUBLE double
+# define MIN_EXP DBL_MIN_EXP
+# if HAVE_FREXP_IN_LIBC && HAVE_LDEXP_IN_LIBC
+# define USE_FREXP_LDEXP
+# define FREXP frexp
+# define LDEXP ldexp
+# endif
+# define DECL_ROUNDING
+# define BEGIN_ROUNDING()
+# define END_ROUNDING()
+# define L_(literal) literal
+#endif
+
+DOUBLE
+FUNC (DOUBLE x, int *expptr)
+{
+ int exponent;
+ DECL_ROUNDING
+
+ BEGIN_ROUNDING ();
+
+#ifdef USE_FREXP_LDEXP
+ /* frexp and ldexp are usually faster than the loop below. */
+ x = FREXP (x, &exponent);
+
+ x = x + x;
+ exponent -= 1;
+
+ if (exponent < MIN_EXP - 1)
+ {
+ x = LDEXP (x, exponent - (MIN_EXP - 1));
+ exponent = MIN_EXP - 1;
+ }
+#else
+ {
+ /* Since the exponent is an 'int', it fits in 64 bits. Therefore the
+ loops are executed no more than 64 times. */
+ DOUBLE pow2[64]; /* pow2[i] = 2^2^i */
+ DOUBLE powh[64]; /* powh[i] = 2^-2^i */
+ int i;
+
+ exponent = 0;
+ if (x >= L_(1.0))
+ {
+ /* A nonnegative exponent. */
+ {
+ DOUBLE pow2_i; /* = pow2[i] */
+ DOUBLE powh_i; /* = powh[i] */
+
+ /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i,
+ x * 2^exponent = argument, x >= 1.0. */
+ for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5);
+ ;
+ i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i)
+ {
+ if (x >= pow2_i)
+ {
+ exponent += (1 << i);
+ x *= powh_i;
+ }
+ else
+ break;
+
+ pow2[i] = pow2_i;
+ powh[i] = powh_i;
+ }
+ }
+ /* Here 1.0 <= x < 2^2^i. */
+ }
+ else
+ {
+ /* A negative exponent. */
+ {
+ DOUBLE pow2_i; /* = pow2[i] */
+ DOUBLE powh_i; /* = powh[i] */
+
+ /* Invariants: pow2_i = 2^2^i, powh_i = 2^-2^i,
+ x * 2^exponent = argument, x < 1.0, exponent >= MIN_EXP - 1. */
+ for (i = 0, pow2_i = L_(2.0), powh_i = L_(0.5);
+ ;
+ i++, pow2_i = pow2_i * pow2_i, powh_i = powh_i * powh_i)
+ {
+ if (exponent - (1 << i) < MIN_EXP - 1)
+ break;
+
+ exponent -= (1 << i);
+ x *= pow2_i;
+ if (x >= L_(1.0))
+ break;
+
+ pow2[i] = pow2_i;
+ powh[i] = powh_i;
+ }
+ }
+ /* Here either x < 1.0 and exponent - 2^i < MIN_EXP - 1 <= exponent,
+ or 1.0 <= x < 2^2^i and exponent >= MIN_EXP - 1. */
+
+ if (x < L_(1.0))
+ /* Invariants: x * 2^exponent = argument, x < 1.0 and
+ exponent - 2^i < MIN_EXP - 1 <= exponent. */
+ while (i > 0)
+ {
+ i--;
+ if (exponent - (1 << i) >= MIN_EXP - 1)
+ {
+ exponent -= (1 << i);
+ x *= pow2[i];
+ if (x >= L_(1.0))
+ break;
+ }
+ }
+
+ /* Here either x < 1.0 and exponent = MIN_EXP - 1,
+ or 1.0 <= x < 2^2^i and exponent >= MIN_EXP - 1. */
+ }
+
+ /* Invariants: x * 2^exponent = argument, and
+ either x < 1.0 and exponent = MIN_EXP - 1,
+ or 1.0 <= x < 2^2^i and exponent >= MIN_EXP - 1. */
+ while (i > 0)
+ {
+ i--;
+ if (x >= pow2[i])
+ {
+ exponent += (1 << i);
+ x *= powh[i];
+ }
+ }
+ /* Here either x < 1.0 and exponent = MIN_EXP - 1,
+ or 1.0 <= x < 2.0 and exponent >= MIN_EXP - 1. */
+ }
+#endif
+
+ END_ROUNDING ();
+
+ *expptr = exponent;
+ return x;
+}
diff --git a/lib/printf-frexp.h b/lib/printf-frexp.h
new file mode 100644
index 00000000000..7c3f9e585db
--- /dev/null
+++ b/lib/printf-frexp.h
@@ -0,0 +1,23 @@
+/* Split a double into fraction and mantissa, for hexadecimal printf.
+ Copyright (C) 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/>. */
+
+/* Write a finite, positive number x as
+ x = mantissa * 2^exp
+ where exp >= DBL_MIN_EXP - 1,
+ mantissa < 2.0,
+ if x is not a denormalized number then mantissa >= 1.0.
+ Store exp in *EXPPTR and return mantissa. */
+extern double printf_frexp (double x, int *expptr);
diff --git a/lib/printf-frexpl.c b/lib/printf-frexpl.c
new file mode 100644
index 00000000000..ba7b579b987
--- /dev/null
+++ b/lib/printf-frexpl.c
@@ -0,0 +1,37 @@
+/* Split a 'long double' into fraction and mantissa, for hexadecimal printf.
+ Copyright (C) 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/>. */
+
+#include <config.h>
+
+#if HAVE_SAME_LONG_DOUBLE_AS_DOUBLE
+
+/* Specification. */
+# include "printf-frexpl.h"
+
+# include "printf-frexp.h"
+
+long double
+printf_frexpl (long double x, int *expptr)
+{
+ return printf_frexp (x, expptr);
+}
+
+#else
+
+# define USE_LONG_DOUBLE
+# include "printf-frexp.c"
+
+#endif
diff --git a/lib/printf-frexpl.h b/lib/printf-frexpl.h
new file mode 100644
index 00000000000..827443ec579
--- /dev/null
+++ b/lib/printf-frexpl.h
@@ -0,0 +1,23 @@
+/* Split a 'long double' into fraction and mantissa, for hexadecimal printf.
+ Copyright (C) 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/>. */
+
+/* Write a finite, positive number x as
+ x = mantissa * 2^exp
+ where exp >= LDBL_MIN_EXP - 1,
+ mantissa < 2.0,
+ if x is not a denormalized number then mantissa >= 1.0.
+ Store exp in *EXPPTR and return mantissa. */
+extern long double printf_frexpl (long double x, int *expptr);
diff --git a/lib/printf-parse.c b/lib/printf-parse.c
new file mode 100644
index 00000000000..3040749abdf
--- /dev/null
+++ b/lib/printf-parse.c
@@ -0,0 +1,623 @@
+/* Formatted output to strings.
+ Copyright (C) 1999-2000, 2002-2003, 2006-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/>. */
+
+/* This file can be parametrized with the following macros:
+ CHAR_T The element type of the format string.
+ CHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters
+ in the format string are ASCII.
+ DIRECTIVE Structure denoting a format directive.
+ Depends on CHAR_T.
+ DIRECTIVES Structure denoting the set of format directives of a
+ format string. Depends on CHAR_T.
+ PRINTF_PARSE Function that parses a format string.
+ Depends on CHAR_T.
+ STATIC Set to 'static' to declare the function static.
+ ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions. */
+
+#ifndef PRINTF_PARSE
+# include <config.h>
+#endif
+
+/* Specification. */
+#ifndef PRINTF_PARSE
+# include "printf-parse.h"
+#endif
+
+/* Default parameters. */
+#ifndef PRINTF_PARSE
+# define PRINTF_PARSE printf_parse
+# define CHAR_T char
+# define DIRECTIVE char_directive
+# define DIRECTIVES char_directives
+#endif
+
+/* Get size_t, NULL. */
+#include <stddef.h>
+
+/* Get intmax_t. */
+#include <stdint.h>
+
+/* malloc(), realloc(), free(). */
+#include <stdlib.h>
+
+/* memcpy(). */
+#include <string.h>
+
+/* errno. */
+#include <errno.h>
+
+/* Checked size_t computations. */
+#include "xsize.h"
+
+#if CHAR_T_ONLY_ASCII
+/* c_isascii(). */
+# include "c-ctype.h"
+#endif
+
+#ifdef STATIC
+STATIC
+#endif
+int
+PRINTF_PARSE (const CHAR_T *format, DIRECTIVES *d, arguments *a)
+{
+ const CHAR_T *cp = format; /* pointer into format */
+ size_t arg_posn = 0; /* number of regular arguments consumed */
+ size_t d_allocated; /* allocated elements of d->dir */
+ size_t a_allocated; /* allocated elements of a->arg */
+ size_t max_width_length = 0;
+ size_t max_precision_length = 0;
+
+ d->count = 0;
+ d_allocated = N_DIRECT_ALLOC_DIRECTIVES;
+ d->dir = d->direct_alloc_dir;
+
+ a->count = 0;
+ a_allocated = N_DIRECT_ALLOC_ARGUMENTS;
+ a->arg = a->direct_alloc_arg;
+
+#define REGISTER_ARG(_index_,_type_) \
+ { \
+ size_t n = (_index_); \
+ if (n >= a_allocated) \
+ { \
+ size_t memory_size; \
+ argument *memory; \
+ \
+ a_allocated = xtimes (a_allocated, 2); \
+ if (a_allocated <= n) \
+ a_allocated = xsum (n, 1); \
+ memory_size = xtimes (a_allocated, sizeof (argument)); \
+ if (size_overflow_p (memory_size)) \
+ /* Overflow, would lead to out of memory. */ \
+ goto out_of_memory; \
+ memory = (argument *) (a->arg != a->direct_alloc_arg \
+ ? realloc (a->arg, memory_size) \
+ : malloc (memory_size)); \
+ if (memory == NULL) \
+ /* Out of memory. */ \
+ goto out_of_memory; \
+ if (a->arg == a->direct_alloc_arg) \
+ memcpy (memory, a->arg, a->count * sizeof (argument)); \
+ a->arg = memory; \
+ } \
+ while (a->count <= n) \
+ a->arg[a->count++].type = TYPE_NONE; \
+ if (a->arg[n].type == TYPE_NONE) \
+ a->arg[n].type = (_type_); \
+ else if (a->arg[n].type != (_type_)) \
+ /* Ambiguous type for positional argument. */ \
+ goto error; \
+ }
+
+ while (*cp != '\0')
+ {
+ CHAR_T c = *cp++;
+ if (c == '%')
+ {
+ size_t arg_index = ARG_NONE;
+ DIRECTIVE *dp = &d->dir[d->count]; /* pointer to next directive */
+
+ /* Initialize the next directive. */
+ dp->dir_start = cp - 1;
+ dp->flags = 0;
+ dp->width_start = NULL;
+ dp->width_end = NULL;
+ dp->width_arg_index = ARG_NONE;
+ dp->precision_start = NULL;
+ dp->precision_end = NULL;
+ dp->precision_arg_index = ARG_NONE;
+ dp->arg_index = ARG_NONE;
+
+ /* Test for positional argument. */
+ if (*cp >= '0' && *cp <= '9')
+ {
+ const CHAR_T *np;
+
+ for (np = cp; *np >= '0' && *np <= '9'; np++)
+ ;
+ if (*np == '$')
+ {
+ size_t n = 0;
+
+ for (np = cp; *np >= '0' && *np <= '9'; np++)
+ n = xsum (xtimes (n, 10), *np - '0');
+ if (n == 0)
+ /* Positional argument 0. */
+ goto error;
+ if (size_overflow_p (n))
+ /* n too large, would lead to out of memory later. */
+ goto error;
+ arg_index = n - 1;
+ cp = np + 1;
+ }
+ }
+
+ /* Read the flags. */
+ for (;;)
+ {
+ if (*cp == '\'')
+ {
+ dp->flags |= FLAG_GROUP;
+ cp++;
+ }
+ else if (*cp == '-')
+ {
+ dp->flags |= FLAG_LEFT;
+ cp++;
+ }
+ else if (*cp == '+')
+ {
+ dp->flags |= FLAG_SHOWSIGN;
+ cp++;
+ }
+ else if (*cp == ' ')
+ {
+ dp->flags |= FLAG_SPACE;
+ cp++;
+ }
+ else if (*cp == '#')
+ {
+ dp->flags |= FLAG_ALT;
+ cp++;
+ }
+ else if (*cp == '0')
+ {
+ dp->flags |= FLAG_ZERO;
+ cp++;
+ }
+#if __GLIBC__ >= 2 && !defined __UCLIBC__
+ else if (*cp == 'I')
+ {
+ dp->flags |= FLAG_LOCALIZED;
+ cp++;
+ }
+#endif
+ else
+ break;
+ }
+
+ /* Parse the field width. */
+ if (*cp == '*')
+ {
+ dp->width_start = cp;
+ cp++;
+ dp->width_end = cp;
+ if (max_width_length < 1)
+ max_width_length = 1;
+
+ /* Test for positional argument. */
+ if (*cp >= '0' && *cp <= '9')
+ {
+ const CHAR_T *np;
+
+ for (np = cp; *np >= '0' && *np <= '9'; np++)
+ ;
+ if (*np == '$')
+ {
+ size_t n = 0;
+
+ for (np = cp; *np >= '0' && *np <= '9'; np++)
+ n = xsum (xtimes (n, 10), *np - '0');
+ if (n == 0)
+ /* Positional argument 0. */
+ goto error;
+ if (size_overflow_p (n))
+ /* n too large, would lead to out of memory later. */
+ goto error;
+ dp->width_arg_index = n - 1;
+ cp = np + 1;
+ }
+ }
+ if (dp->width_arg_index == ARG_NONE)
+ {
+ dp->width_arg_index = arg_posn++;
+ if (dp->width_arg_index == ARG_NONE)
+ /* arg_posn wrapped around. */
+ goto error;
+ }
+ REGISTER_ARG (dp->width_arg_index, TYPE_INT);
+ }
+ else if (*cp >= '0' && *cp <= '9')
+ {
+ size_t width_length;
+
+ dp->width_start = cp;
+ for (; *cp >= '0' && *cp <= '9'; cp++)
+ ;
+ dp->width_end = cp;
+ width_length = dp->width_end - dp->width_start;
+ if (max_width_length < width_length)
+ max_width_length = width_length;
+ }
+
+ /* Parse the precision. */
+ if (*cp == '.')
+ {
+ cp++;
+ if (*cp == '*')
+ {
+ dp->precision_start = cp - 1;
+ cp++;
+ dp->precision_end = cp;
+ if (max_precision_length < 2)
+ max_precision_length = 2;
+
+ /* Test for positional argument. */
+ if (*cp >= '0' && *cp <= '9')
+ {
+ const CHAR_T *np;
+
+ for (np = cp; *np >= '0' && *np <= '9'; np++)
+ ;
+ if (*np == '$')
+ {
+ size_t n = 0;
+
+ for (np = cp; *np >= '0' && *np <= '9'; np++)
+ n = xsum (xtimes (n, 10), *np - '0');
+ if (n == 0)
+ /* Positional argument 0. */
+ goto error;
+ if (size_overflow_p (n))
+ /* n too large, would lead to out of memory
+ later. */
+ goto error;
+ dp->precision_arg_index = n - 1;
+ cp = np + 1;
+ }
+ }
+ if (dp->precision_arg_index == ARG_NONE)
+ {
+ dp->precision_arg_index = arg_posn++;
+ if (dp->precision_arg_index == ARG_NONE)
+ /* arg_posn wrapped around. */
+ goto error;
+ }
+ REGISTER_ARG (dp->precision_arg_index, TYPE_INT);
+ }
+ else
+ {
+ size_t precision_length;
+
+ dp->precision_start = cp - 1;
+ for (; *cp >= '0' && *cp <= '9'; cp++)
+ ;
+ dp->precision_end = cp;
+ precision_length = dp->precision_end - dp->precision_start;
+ if (max_precision_length < precision_length)
+ max_precision_length = precision_length;
+ }
+ }
+
+ {
+ arg_type type;
+
+ /* Parse argument type/size specifiers. */
+ {
+ int flags = 0;
+
+ for (;;)
+ {
+ if (*cp == 'h')
+ {
+ flags |= (1 << (flags & 1));
+ cp++;
+ }
+ else if (*cp == 'L')
+ {
+ flags |= 4;
+ cp++;
+ }
+ else if (*cp == 'l')
+ {
+ flags += 8;
+ cp++;
+ }
+ else if (*cp == 'j')
+ {
+ if (sizeof (intmax_t) > sizeof (long))
+ {
+ /* intmax_t = long long */
+ flags += 16;
+ }
+ else if (sizeof (intmax_t) > sizeof (int))
+ {
+ /* intmax_t = long */
+ flags += 8;
+ }
+ cp++;
+ }
+ else if (*cp == 'z' || *cp == 'Z')
+ {
+ /* 'z' is standardized in ISO C 99, but glibc uses 'Z'
+ because the warning facility in gcc-2.95.2 understands
+ only 'Z' (see gcc-2.95.2/gcc/c-common.c:1784). */
+ if (sizeof (size_t) > sizeof (long))
+ {
+ /* size_t = long long */
+ flags += 16;
+ }
+ else if (sizeof (size_t) > sizeof (int))
+ {
+ /* size_t = long */
+ flags += 8;
+ }
+ cp++;
+ }
+ else if (*cp == 't')
+ {
+ if (sizeof (ptrdiff_t) > sizeof (long))
+ {
+ /* ptrdiff_t = long long */
+ flags += 16;
+ }
+ else if (sizeof (ptrdiff_t) > sizeof (int))
+ {
+ /* ptrdiff_t = long */
+ flags += 8;
+ }
+ cp++;
+ }
+#if defined __APPLE__ && defined __MACH__
+ /* On Mac OS X 10.3, PRIdMAX is defined as "qd".
+ We cannot change it to "lld" because PRIdMAX must also
+ be understood by the system's printf routines. */
+ else if (*cp == 'q')
+ {
+ if (64 / 8 > sizeof (long))
+ {
+ /* int64_t = long long */
+ flags += 16;
+ }
+ else
+ {
+ /* int64_t = long */
+ flags += 8;
+ }
+ cp++;
+ }
+#endif
+#if defined _WIN32 && ! defined __CYGWIN__
+ /* On native Windows, PRIdMAX is defined as "I64d".
+ We cannot change it to "lld" because PRIdMAX must also
+ be understood by the system's printf routines. */
+ else if (*cp == 'I' && cp[1] == '6' && cp[2] == '4')
+ {
+ if (64 / 8 > sizeof (long))
+ {
+ /* __int64 = long long */
+ flags += 16;
+ }
+ else
+ {
+ /* __int64 = long */
+ flags += 8;
+ }
+ cp += 3;
+ }
+#endif
+ else
+ break;
+ }
+
+ /* Read the conversion character. */
+ c = *cp++;
+ switch (c)
+ {
+ case 'd': case 'i':
+ /* If 'long long' is larger than 'long': */
+ if (flags >= 16 || (flags & 4))
+ type = TYPE_LONGLONGINT;
+ else
+ /* If 'long long' is the same as 'long', we parse "lld" into
+ TYPE_LONGINT. */
+ if (flags >= 8)
+ type = TYPE_LONGINT;
+ else if (flags & 2)
+ type = TYPE_SCHAR;
+ else if (flags & 1)
+ type = TYPE_SHORT;
+ else
+ type = TYPE_INT;
+ break;
+ case 'o': case 'u': case 'x': case 'X':
+ /* If 'unsigned long long' is larger than 'unsigned long': */
+ if (flags >= 16 || (flags & 4))
+ type = TYPE_ULONGLONGINT;
+ else
+ /* If 'unsigned long long' is the same as 'unsigned long', we
+ parse "llu" into TYPE_ULONGINT. */
+ if (flags >= 8)
+ type = TYPE_ULONGINT;
+ else if (flags & 2)
+ type = TYPE_UCHAR;
+ else if (flags & 1)
+ type = TYPE_USHORT;
+ else
+ type = TYPE_UINT;
+ break;
+ case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
+ case 'a': case 'A':
+ if (flags >= 16 || (flags & 4))
+ type = TYPE_LONGDOUBLE;
+ else
+ type = TYPE_DOUBLE;
+ break;
+ case 'c':
+ if (flags >= 8)
+#if HAVE_WINT_T
+ type = TYPE_WIDE_CHAR;
+#else
+ goto error;
+#endif
+ else
+ type = TYPE_CHAR;
+ break;
+#if HAVE_WINT_T
+ case 'C':
+ type = TYPE_WIDE_CHAR;
+ c = 'c';
+ break;
+#endif
+ case 's':
+ if (flags >= 8)
+#if HAVE_WCHAR_T
+ type = TYPE_WIDE_STRING;
+#else
+ goto error;
+#endif
+ else
+ type = TYPE_STRING;
+ break;
+#if HAVE_WCHAR_T
+ case 'S':
+ type = TYPE_WIDE_STRING;
+ c = 's';
+ break;
+#endif
+ case 'p':
+ type = TYPE_POINTER;
+ break;
+ case 'n':
+ /* If 'long long' is larger than 'long': */
+ if (flags >= 16 || (flags & 4))
+ type = TYPE_COUNT_LONGLONGINT_POINTER;
+ else
+ /* If 'long long' is the same as 'long', we parse "lln" into
+ TYPE_COUNT_LONGINT_POINTER. */
+ if (flags >= 8)
+ type = TYPE_COUNT_LONGINT_POINTER;
+ else if (flags & 2)
+ type = TYPE_COUNT_SCHAR_POINTER;
+ else if (flags & 1)
+ type = TYPE_COUNT_SHORT_POINTER;
+ else
+ type = TYPE_COUNT_INT_POINTER;
+ break;
+#if ENABLE_UNISTDIO
+ /* The unistdio extensions. */
+ case 'U':
+ if (flags >= 16)
+ type = TYPE_U32_STRING;
+ else if (flags >= 8)
+ type = TYPE_U16_STRING;
+ else
+ type = TYPE_U8_STRING;
+ break;
+#endif
+ case '%':
+ type = TYPE_NONE;
+ break;
+ default:
+ /* Unknown conversion character. */
+ goto error;
+ }
+ }
+
+ if (type != TYPE_NONE)
+ {
+ dp->arg_index = arg_index;
+ if (dp->arg_index == ARG_NONE)
+ {
+ dp->arg_index = arg_posn++;
+ if (dp->arg_index == ARG_NONE)
+ /* arg_posn wrapped around. */
+ goto error;
+ }
+ REGISTER_ARG (dp->arg_index, type);
+ }
+ dp->conversion = c;
+ dp->dir_end = cp;
+ }
+
+ d->count++;
+ if (d->count >= d_allocated)
+ {
+ size_t memory_size;
+ DIRECTIVE *memory;
+
+ d_allocated = xtimes (d_allocated, 2);
+ memory_size = xtimes (d_allocated, sizeof (DIRECTIVE));
+ if (size_overflow_p (memory_size))
+ /* Overflow, would lead to out of memory. */
+ goto out_of_memory;
+ memory = (DIRECTIVE *) (d->dir != d->direct_alloc_dir
+ ? realloc (d->dir, memory_size)
+ : malloc (memory_size));
+ if (memory == NULL)
+ /* Out of memory. */
+ goto out_of_memory;
+ if (d->dir == d->direct_alloc_dir)
+ memcpy (memory, d->dir, d->count * sizeof (DIRECTIVE));
+ d->dir = memory;
+ }
+ }
+#if CHAR_T_ONLY_ASCII
+ else if (!c_isascii (c))
+ {
+ /* Non-ASCII character. Not supported. */
+ goto error;
+ }
+#endif
+ }
+ d->dir[d->count].dir_start = cp;
+
+ d->max_width_length = max_width_length;
+ d->max_precision_length = max_precision_length;
+ return 0;
+
+error:
+ if (a->arg != a->direct_alloc_arg)
+ free (a->arg);
+ if (d->dir != d->direct_alloc_dir)
+ free (d->dir);
+ errno = EINVAL;
+ return -1;
+
+out_of_memory:
+ if (a->arg != a->direct_alloc_arg)
+ free (a->arg);
+ if (d->dir != d->direct_alloc_dir)
+ free (d->dir);
+ errno = ENOMEM;
+ return -1;
+}
+
+#undef PRINTF_PARSE
+#undef DIRECTIVES
+#undef DIRECTIVE
+#undef CHAR_T_ONLY_ASCII
+#undef CHAR_T
diff --git a/lib/printf-parse.h b/lib/printf-parse.h
new file mode 100644
index 00000000000..1f86e32c99e
--- /dev/null
+++ b/lib/printf-parse.h
@@ -0,0 +1,193 @@
+/* Parse printf format string.
+ Copyright (C) 1999, 2002-2003, 2005, 2007, 2010-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/>. */
+
+#ifndef _PRINTF_PARSE_H
+#define _PRINTF_PARSE_H
+
+/* This file can be parametrized with the following macros:
+ ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions.
+ STATIC Set to 'static' to declare the function static. */
+
+#if HAVE_FEATURES_H
+# include <features.h> /* for __GLIBC__, __UCLIBC__ */
+#endif
+
+#include "printf-args.h"
+
+
+/* Flags */
+#define FLAG_GROUP 1 /* ' flag */
+#define FLAG_LEFT 2 /* - flag */
+#define FLAG_SHOWSIGN 4 /* + flag */
+#define FLAG_SPACE 8 /* space flag */
+#define FLAG_ALT 16 /* # flag */
+#define FLAG_ZERO 32
+#if __GLIBC__ >= 2 && !defined __UCLIBC__
+# define FLAG_LOCALIZED 64 /* I flag, uses localized digits */
+#endif
+
+/* arg_index value indicating that no argument is consumed. */
+#define ARG_NONE (~(size_t)0)
+
+/* xxx_directive: A parsed directive.
+ xxx_directives: A parsed format string. */
+
+/* Number of directly allocated directives (no malloc() needed). */
+#define N_DIRECT_ALLOC_DIRECTIVES 7
+
+/* A parsed directive. */
+typedef struct
+{
+ const char* dir_start;
+ const char* dir_end;
+ int flags;
+ const char* width_start;
+ const char* width_end;
+ size_t width_arg_index;
+ const char* precision_start;
+ const char* precision_end;
+ size_t precision_arg_index;
+ char conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */
+ size_t arg_index;
+}
+char_directive;
+
+/* A parsed format string. */
+typedef struct
+{
+ size_t count;
+ char_directive *dir;
+ size_t max_width_length;
+ size_t max_precision_length;
+ char_directive direct_alloc_dir[N_DIRECT_ALLOC_DIRECTIVES];
+}
+char_directives;
+
+#if ENABLE_UNISTDIO
+
+/* A parsed directive. */
+typedef struct
+{
+ const uint8_t* dir_start;
+ const uint8_t* dir_end;
+ int flags;
+ const uint8_t* width_start;
+ const uint8_t* width_end;
+ size_t width_arg_index;
+ const uint8_t* precision_start;
+ const uint8_t* precision_end;
+ size_t precision_arg_index;
+ uint8_t conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */
+ size_t arg_index;
+}
+u8_directive;
+
+/* A parsed format string. */
+typedef struct
+{
+ size_t count;
+ u8_directive *dir;
+ size_t max_width_length;
+ size_t max_precision_length;
+ u8_directive direct_alloc_dir[N_DIRECT_ALLOC_DIRECTIVES];
+}
+u8_directives;
+
+/* A parsed directive. */
+typedef struct
+{
+ const uint16_t* dir_start;
+ const uint16_t* dir_end;
+ int flags;
+ const uint16_t* width_start;
+ const uint16_t* width_end;
+ size_t width_arg_index;
+ const uint16_t* precision_start;
+ const uint16_t* precision_end;
+ size_t precision_arg_index;
+ uint16_t conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */
+ size_t arg_index;
+}
+u16_directive;
+
+/* A parsed format string. */
+typedef struct
+{
+ size_t count;
+ u16_directive *dir;
+ size_t max_width_length;
+ size_t max_precision_length;
+ u16_directive direct_alloc_dir[N_DIRECT_ALLOC_DIRECTIVES];
+}
+u16_directives;
+
+/* A parsed directive. */
+typedef struct
+{
+ const uint32_t* dir_start;
+ const uint32_t* dir_end;
+ int flags;
+ const uint32_t* width_start;
+ const uint32_t* width_end;
+ size_t width_arg_index;
+ const uint32_t* precision_start;
+ const uint32_t* precision_end;
+ size_t precision_arg_index;
+ uint32_t conversion; /* d i o u x X f F e E g G a A c s p n U % but not C S */
+ size_t arg_index;
+}
+u32_directive;
+
+/* A parsed format string. */
+typedef struct
+{
+ size_t count;
+ u32_directive *dir;
+ size_t max_width_length;
+ size_t max_precision_length;
+ u32_directive direct_alloc_dir[N_DIRECT_ALLOC_DIRECTIVES];
+}
+u32_directives;
+
+#endif
+
+
+/* Parses the format string. Fills in the number N of directives, and fills
+ in directives[0], ..., directives[N-1], and sets directives[N].dir_start
+ to the end of the format string. Also fills in the arg_type fields of the
+ arguments and the needed count of arguments. */
+#if ENABLE_UNISTDIO
+extern int
+ ulc_printf_parse (const char *format, char_directives *d, arguments *a);
+extern int
+ u8_printf_parse (const uint8_t *format, u8_directives *d, arguments *a);
+extern int
+ u16_printf_parse (const uint16_t *format, u16_directives *d,
+ arguments *a);
+extern int
+ u32_printf_parse (const uint32_t *format, u32_directives *d,
+ arguments *a);
+#else
+# ifdef STATIC
+STATIC
+# else
+extern
+# endif
+int printf_parse (const char *format, char_directives *d, arguments *a);
+#endif
+
+#endif /* _PRINTF_PARSE_H */
diff --git a/lib/printf.c b/lib/printf.c
new file mode 100644
index 00000000000..00483271292
--- /dev/null
+++ b/lib/printf.c
@@ -0,0 +1,40 @@
+/* Formatted output to a stream.
+ Copyright (C) 2007, 2010-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 3 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/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include <stdio.h>
+
+#include <stdarg.h>
+
+/* Print formatted output to standard output.
+ Return string length of formatted string. On error, return a negative
+ value. */
+int
+printf (const char *format, ...)
+{
+ int retval;
+ va_list args;
+
+ va_start (args, format);
+ retval = vfprintf (stdout, format, args);
+ va_end (args);
+
+ return retval;
+}
diff --git a/lib/signbitd.c b/lib/signbitd.c
new file mode 100644
index 00000000000..706ea394414
--- /dev/null
+++ b/lib/signbitd.c
@@ -0,0 +1,64 @@
+/* signbit() macro: Determine the sign bit of a floating-point number.
+ Copyright (C) 2007-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/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include <math.h>
+
+#include <string.h>
+#include "isnand-nolibm.h"
+#include "float+.h"
+
+#ifdef gl_signbitd_OPTIMIZED_MACRO
+# undef gl_signbitd
+#endif
+
+int
+gl_signbitd (double arg)
+{
+#if defined DBL_SIGNBIT_WORD && defined DBL_SIGNBIT_BIT
+ /* The use of a union to extract the bits of the representation of a
+ 'long double' is safe in practice, despite of the "aliasing rules" of
+ C99, because the GCC docs say
+ "Even with '-fstrict-aliasing', type-punning is allowed, provided the
+ memory is accessed through the union type."
+ and similarly for other compilers. */
+# define NWORDS \
+ ((sizeof (double) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+ union { double value; unsigned int word[NWORDS]; } m;
+ m.value = arg;
+ return (m.word[DBL_SIGNBIT_WORD] >> DBL_SIGNBIT_BIT) & 1;
+#elif HAVE_COPYSIGN_IN_LIBC
+ return copysign (1.0, arg) < 0;
+#else
+ /* This does not do the right thing for NaN, but this is irrelevant for
+ most use cases. */
+ if (isnand (arg))
+ return 0;
+ if (arg < 0.0)
+ return 1;
+ else if (arg == 0.0)
+ {
+ /* Distinguish 0.0 and -0.0. */
+ static double plus_zero = 0.0;
+ double arg_mem = arg;
+ return (memcmp (&plus_zero, &arg_mem, SIZEOF_DBL) != 0);
+ }
+ else
+ return 0;
+#endif
+}
diff --git a/lib/signbitf.c b/lib/signbitf.c
new file mode 100644
index 00000000000..c25bb64a0d2
--- /dev/null
+++ b/lib/signbitf.c
@@ -0,0 +1,64 @@
+/* signbit() macro: Determine the sign bit of a floating-point number.
+ Copyright (C) 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/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include <math.h>
+
+#include <string.h>
+#include "isnanf-nolibm.h"
+#include "float+.h"
+
+#ifdef gl_signbitf_OPTIMIZED_MACRO
+# undef gl_signbitf
+#endif
+
+int
+gl_signbitf (float arg)
+{
+#if defined FLT_SIGNBIT_WORD && defined FLT_SIGNBIT_BIT
+ /* The use of a union to extract the bits of the representation of a
+ 'long double' is safe in practice, despite of the "aliasing rules" of
+ C99, because the GCC docs say
+ "Even with '-fstrict-aliasing', type-punning is allowed, provided the
+ memory is accessed through the union type."
+ and similarly for other compilers. */
+# define NWORDS \
+ ((sizeof (float) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+ union { float value; unsigned int word[NWORDS]; } m;
+ m.value = arg;
+ return (m.word[FLT_SIGNBIT_WORD] >> FLT_SIGNBIT_BIT) & 1;
+#elif HAVE_COPYSIGNF_IN_LIBC
+ return copysignf (1.0f, arg) < 0;
+#else
+ /* This does not do the right thing for NaN, but this is irrelevant for
+ most use cases. */
+ if (isnanf (arg))
+ return 0;
+ if (arg < 0.0f)
+ return 1;
+ else if (arg == 0.0f)
+ {
+ /* Distinguish 0.0f and -0.0f. */
+ static float plus_zero = 0.0f;
+ float arg_mem = arg;
+ return (memcmp (&plus_zero, &arg_mem, SIZEOF_FLT) != 0);
+ }
+ else
+ return 0;
+#endif
+}
diff --git a/lib/signbitl.c b/lib/signbitl.c
new file mode 100644
index 00000000000..9d459caf879
--- /dev/null
+++ b/lib/signbitl.c
@@ -0,0 +1,64 @@
+/* signbit() macro: Determine the sign bit of a floating-point number.
+ Copyright (C) 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/>. */
+
+#include <config.h>
+
+/* Specification. */
+#include <math.h>
+
+#include <string.h>
+#include "isnanl-nolibm.h"
+#include "float+.h"
+
+#ifdef gl_signbitl_OPTIMIZED_MACRO
+# undef gl_signbitl
+#endif
+
+int
+gl_signbitl (long double arg)
+{
+#if defined LDBL_SIGNBIT_WORD && defined LDBL_SIGNBIT_BIT
+ /* The use of a union to extract the bits of the representation of a
+ 'long double' is safe in practice, despite of the "aliasing rules" of
+ C99, because the GCC docs say
+ "Even with '-fstrict-aliasing', type-punning is allowed, provided the
+ memory is accessed through the union type."
+ and similarly for other compilers. */
+# define NWORDS \
+ ((sizeof (long double) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+ union { long double value; unsigned int word[NWORDS]; } m;
+ m.value = arg;
+ return (m.word[LDBL_SIGNBIT_WORD] >> LDBL_SIGNBIT_BIT) & 1;
+#elif HAVE_COPYSIGNL_IN_LIBC
+ return copysignl (1.0L, arg) < 0;
+#else
+ /* This does not do the right thing for NaN, but this is irrelevant for
+ most use cases. */
+ if (isnanl (arg))
+ return 0;
+ if (arg < 0.0L)
+ return 1;
+ else if (arg == 0.0L)
+ {
+ /* Distinguish 0.0L and -0.0L. */
+ static long double plus_zero = 0.0L;
+ long double arg_mem = arg;
+ return (memcmp (&plus_zero, &arg_mem, SIZEOF_LDBL) != 0);
+ }
+ else
+ return 0;
+#endif
+}
diff --git a/lib/size_max.h b/lib/size_max.h
new file mode 100644
index 00000000000..48af0250556
--- /dev/null
+++ b/lib/size_max.h
@@ -0,0 +1,30 @@
+/* size_max.h -- declare SIZE_MAX through system headers
+ Copyright (C) 2005-2006, 2009-2023 Free Software Foundation, Inc.
+ Written by Simon Josefsson.
+
+ 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/>. */
+
+#ifndef GNULIB_SIZE_MAX_H
+#define GNULIB_SIZE_MAX_H
+
+/* Get SIZE_MAX declaration on systems like Solaris 7/8/9. */
+# include <limits.h>
+/* Get SIZE_MAX declaration on systems like glibc 2. */
+# if HAVE_STDINT_H
+# include <stdint.h>
+# endif
+/* On systems where these include files don't define it, SIZE_MAX is defined
+ in config.h. */
+
+#endif /* GNULIB_SIZE_MAX_H */
diff --git a/lib/stdalign.in.h b/lib/stdalign.in.h
new file mode 100644
index 00000000000..b616c100fdc
--- /dev/null
+++ b/lib/stdalign.in.h
@@ -0,0 +1,49 @@
+/* A substitute for ISO C11 <stdalign.h>.
+
+ Copyright 2011-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 Paul Eggert and Bruno Haible. */
+
+/* Define two obsolescent C11 macros, assuming alignas and alignof are
+ either keywords or alignasof-defined macros. */
+
+#ifndef _@GUARD_PREFIX@_STDALIGN_H
+
+#if __GNUC__ >= 3
+@PRAGMA_SYSTEM_HEADER@
+#endif
+@PRAGMA_COLUMNS@
+
+/* We need to include the system's <stdalign.h> when it exists, because it might
+ define 'alignof' as a macro when it's not a keyword or compiler built-in. */
+#if @HAVE_STDALIGN_H@
+/* The include_next requires a split double-inclusion guard. */
+# @INCLUDE_NEXT@ @NEXT_STDALIGN_H@
+#endif
+
+#ifndef _@GUARD_PREFIX@_STDALIGN_H
+#define _@GUARD_PREFIX@_STDALIGN_H
+
+#if (defined alignas \
+ || (defined __STDC_VERSION__ && 202311 <= __STDC_VERSION__) \
+ || (defined __cplusplus && (201103 <= __cplusplus || defined _MSC_VER)))
+# define __alignas_is_defined 1
+#endif
+
+#define __alignof_is_defined 1
+
+#endif /* _@GUARD_PREFIX@_STDALIGN_H */
+#endif /* _@GUARD_PREFIX@_STDALIGN_H */
diff --git a/lib/stpncpy.c b/lib/stpncpy.c
new file mode 100644
index 00000000000..d1422a927df
--- /dev/null
+++ b/lib/stpncpy.c
@@ -0,0 +1,92 @@
+/* Copyright (C) 1993, 1995-1997, 2002-2003, 2005-2007, 2009-2023 Free Software
+ * Foundation, Inc.
+
+ NOTE: The canonical source of this file is maintained with the GNU C Library.
+ Bugs can be reported to bug-glibc@gnu.org.
+
+ 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/>. */
+
+/* This is almost copied from strncpy.c, written by Torbjorn Granlund. */
+
+#include <config.h>
+
+/* Specification. */
+#include <string.h>
+
+#ifndef weak_alias
+# define __stpncpy stpncpy
+#endif
+
+/* Copy no more than N bytes of SRC to DST, returning a pointer past the
+ last non-NUL byte written into DST. */
+char *
+(__stpncpy) (char *dest, const char *src, size_t n)
+{
+ char c;
+ char *s = dest;
+
+ if (n >= 4)
+ {
+ size_t n4 = n >> 2;
+
+ for (;;)
+ {
+ c = *src++;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ c = *src++;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ c = *src++;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ c = *src++;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ if (--n4 == 0)
+ goto last_chars;
+ }
+ n -= dest - s;
+ goto zero_fill;
+ }
+
+ last_chars:
+ n &= 3;
+ if (n == 0)
+ return dest;
+
+ for (;;)
+ {
+ c = *src++;
+ --n;
+ *dest++ = c;
+ if (c == '\0')
+ break;
+ if (n == 0)
+ return dest;
+ }
+
+ zero_fill:
+ while (n-- > 0)
+ dest[n] = '\0';
+
+ return dest - 1;
+}
+#ifdef weak_alias
+weak_alias (__stpncpy, stpncpy)
+#endif
diff --git a/lib/vasnprintf.c b/lib/vasnprintf.c
new file mode 100644
index 00000000000..72b8cdbfa6b
--- /dev/null
+++ b/lib/vasnprintf.c
@@ -0,0 +1,5741 @@
+/* vsprintf with automatic memory allocation.
+ Copyright (C) 1999, 2002-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/>. */
+
+/* This file can be parametrized with the following macros:
+ VASNPRINTF The name of the function being defined.
+ FCHAR_T The element type of the format string.
+ DCHAR_T The element type of the destination (result) string.
+ FCHAR_T_ONLY_ASCII Set to 1 to enable verification that all characters
+ in the format string are ASCII. MUST be set if
+ FCHAR_T and DCHAR_T are not the same type.
+ DIRECTIVE Structure denoting a format directive.
+ Depends on FCHAR_T.
+ DIRECTIVES Structure denoting the set of format directives of a
+ format string. Depends on FCHAR_T.
+ PRINTF_PARSE Function that parses a format string.
+ Depends on FCHAR_T.
+ DCHAR_CPY memcpy like function for DCHAR_T[] arrays.
+ DCHAR_SET memset like function for DCHAR_T[] arrays.
+ DCHAR_MBSNLEN mbsnlen like function for DCHAR_T[] arrays.
+ SNPRINTF The system's snprintf (or similar) function.
+ This may be either snprintf or swprintf.
+ TCHAR_T The element type of the argument and result string
+ of the said SNPRINTF function. This may be either
+ char or wchar_t. The code exploits that
+ sizeof (TCHAR_T) | sizeof (DCHAR_T) and
+ alignof (TCHAR_T) <= alignof (DCHAR_T).
+ DCHAR_IS_TCHAR Set to 1 if DCHAR_T and TCHAR_T are the same type.
+ DCHAR_CONV_FROM_ENCODING A function to convert from char[] to DCHAR[].
+ DCHAR_IS_UINT8_T Set to 1 if DCHAR_T is uint8_t.
+ DCHAR_IS_UINT16_T Set to 1 if DCHAR_T is uint16_t.
+ DCHAR_IS_UINT32_T Set to 1 if DCHAR_T is uint32_t.
+ ENABLE_UNISTDIO Set to 1 to enable the unistdio extensions.
+ ENABLE_WCHAR_FALLBACK Set to 1 to avoid EILSEQ during conversion of wide
+ characters (wchar_t) and wide character strings
+ (wchar_t[]) to multibyte sequences. The fallback is the
+ hexadecimal escape syntax (\unnnn or \Unnnnnnnn) or,
+ if wchar_t is not Unicode encoded, \wnnnn or \Wnnnnnnnn.
+ */
+
+/* Tell glibc's <stdio.h> to provide a prototype for snprintf().
+ This must come before <config.h> because <config.h> may include
+ <features.h>, and once <features.h> has been included, it's too late. */
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE 1
+#endif
+
+#ifndef VASNPRINTF
+# include <config.h>
+#endif
+
+/* As of GCC 11.2.1, gcc -Wanalyzer-too-complex reports that main's
+ use of CHECK macros expands to code that is too complicated for gcc
+ -fanalyzer. Suppress the resulting bogus warnings. */
+#if 10 <= __GNUC__
+# pragma GCC diagnostic ignored "-Wanalyzer-null-argument"
+#endif
+
+#include <alloca.h>
+
+/* Specification. */
+#ifndef VASNPRINTF
+# if WIDE_CHAR_VERSION
+# include "vasnwprintf.h"
+# else
+# include "vasnprintf.h"
+# endif
+#endif
+
+#include <locale.h> /* localeconv() */
+#include <stdio.h> /* snprintf(), sprintf() */
+#include <stdlib.h> /* abort(), malloc(), realloc(), free() */
+#include <string.h> /* memcpy(), strlen() */
+#include <wchar.h> /* mbstate_t, mbrtowc(), mbrlen(), wcrtomb() */
+#include <errno.h> /* errno */
+#include <limits.h> /* CHAR_BIT */
+#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */
+#if HAVE_NL_LANGINFO
+# include <langinfo.h>
+#endif
+#ifndef VASNPRINTF
+# if WIDE_CHAR_VERSION
+# include "wprintf-parse.h"
+# else
+# include "printf-parse.h"
+# endif
+#endif
+
+/* Checked size_t computations. */
+#include "xsize.h"
+
+#include "attribute.h"
+
+#if (NEED_PRINTF_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL
+# include <math.h>
+# include "float+.h"
+#endif
+
+#if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL
+# include <math.h>
+# include "isnand-nolibm.h"
+#endif
+
+#if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE) && !defined IN_LIBINTL
+# include <math.h>
+# include "isnanl-nolibm.h"
+# include "fpucw.h"
+#endif
+
+#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL
+# include <math.h>
+# include "isnand-nolibm.h"
+# include "printf-frexp.h"
+#endif
+
+#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL
+# include <math.h>
+# include "isnanl-nolibm.h"
+# include "printf-frexpl.h"
+# include "fpucw.h"
+#endif
+
+/* Default parameters. */
+#ifndef VASNPRINTF
+# if WIDE_CHAR_VERSION
+# define VASNPRINTF vasnwprintf
+# define FCHAR_T wchar_t
+# define DCHAR_T wchar_t
+# define TCHAR_T wchar_t
+# define DCHAR_IS_TCHAR 1
+# define DIRECTIVE wchar_t_directive
+# define DIRECTIVES wchar_t_directives
+# define PRINTF_PARSE wprintf_parse
+# define DCHAR_CPY wmemcpy
+# define DCHAR_SET wmemset
+# else
+# define VASNPRINTF vasnprintf
+# define FCHAR_T char
+# define DCHAR_T char
+# define TCHAR_T char
+# define DCHAR_IS_TCHAR 1
+# define DIRECTIVE char_directive
+# define DIRECTIVES char_directives
+# define PRINTF_PARSE printf_parse
+# define DCHAR_CPY memcpy
+# define DCHAR_SET memset
+# endif
+#endif
+#if WIDE_CHAR_VERSION
+ /* TCHAR_T is wchar_t. */
+# define USE_SNPRINTF 1
+# if HAVE_DECL__SNWPRINTF
+ /* On Windows, the function swprintf() has a different signature than
+ on Unix; we use the function _snwprintf() or - on mingw - snwprintf()
+ instead. The mingw function snwprintf() has fewer bugs than the
+ MSVCRT function _snwprintf(), so prefer that. */
+# if defined __MINGW32__
+# define SNPRINTF snwprintf
+# else
+# define SNPRINTF _snwprintf
+# define USE_MSVC__SNPRINTF 1
+# endif
+# else
+ /* Unix. */
+# define SNPRINTF swprintf
+# endif
+#else
+ /* TCHAR_T is char. */
+ /* Use snprintf if it exists under the name 'snprintf' or '_snprintf'.
+ But don't use it on BeOS, since BeOS snprintf produces no output if the
+ size argument is >= 0x3000000.
+ Also don't use it on Linux libc5, since there snprintf with size = 1
+ writes any output without bounds, like sprintf. */
+# if (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) && !defined __BEOS__ && !(__GNU_LIBRARY__ == 1)
+# define USE_SNPRINTF 1
+# else
+# define USE_SNPRINTF 0
+# endif
+# if HAVE_DECL__SNPRINTF
+ /* Windows. The mingw function snprintf() has fewer bugs than the MSVCRT
+ function _snprintf(), so prefer that. */
+# if defined __MINGW32__
+# define SNPRINTF snprintf
+ /* Here we need to call the native snprintf, not rpl_snprintf. */
+# undef snprintf
+# else
+ /* MSVC versions < 14 did not have snprintf, only _snprintf. */
+# define SNPRINTF _snprintf
+# define USE_MSVC__SNPRINTF 1
+# endif
+# else
+ /* Unix. */
+# define SNPRINTF snprintf
+ /* Here we need to call the native snprintf, not rpl_snprintf. */
+# undef snprintf
+# endif
+#endif
+/* Here we need to call the native sprintf, not rpl_sprintf. */
+#undef sprintf
+
+/* GCC >= 4.0 with -Wall emits unjustified "... may be used uninitialized"
+ warnings in this file. Use -Dlint to suppress them. */
+#if defined GCC_LINT || defined lint
+# define IF_LINT(Code) Code
+#else
+# define IF_LINT(Code) /* empty */
+#endif
+
+/* Avoid some warnings from "gcc -Wshadow".
+ This file doesn't use the exp() and remainder() functions. */
+#undef exp
+#define exp expo
+#undef remainder
+#define remainder rem
+
+#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && !WIDE_CHAR_VERSION
+# if (HAVE_STRNLEN && !defined _AIX)
+# define local_strnlen strnlen
+# else
+# ifndef local_strnlen_defined
+# define local_strnlen_defined 1
+static size_t
+local_strnlen (const char *string, size_t maxlen)
+{
+ const char *end = memchr (string, '\0', maxlen);
+ return end ? (size_t) (end - string) : maxlen;
+}
+# endif
+# endif
+#endif
+
+#if (((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && WIDE_CHAR_VERSION) || ((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL)) && !WIDE_CHAR_VERSION && DCHAR_IS_TCHAR)) && HAVE_WCHAR_T
+# if HAVE_WCSLEN
+# define local_wcslen wcslen
+# else
+ /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid
+ a dependency towards this library, here is a local substitute.
+ Define this substitute only once, even if this file is included
+ twice in the same compilation unit. */
+# ifndef local_wcslen_defined
+# define local_wcslen_defined 1
+static size_t
+local_wcslen (const wchar_t *s)
+{
+ const wchar_t *ptr;
+
+ for (ptr = s; *ptr != (wchar_t) 0; ptr++)
+ ;
+ return ptr - s;
+}
+# endif
+# endif
+#endif
+
+#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF) && HAVE_WCHAR_T && WIDE_CHAR_VERSION
+# if HAVE_WCSNLEN
+# define local_wcsnlen wcsnlen
+# else
+# ifndef local_wcsnlen_defined
+# define local_wcsnlen_defined 1
+static size_t
+local_wcsnlen (const wchar_t *s, size_t maxlen)
+{
+ const wchar_t *ptr;
+
+ for (ptr = s; maxlen > 0 && *ptr != (wchar_t) 0; ptr++, maxlen--)
+ ;
+ return ptr - s;
+}
+# endif
+# endif
+#endif
+
+#if (((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL) || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T) || (ENABLE_WCHAR_FALLBACK && HAVE_WINT_T)) && !WIDE_CHAR_VERSION
+# if ENABLE_WCHAR_FALLBACK
+static size_t
+wctomb_fallback (char *s, wchar_t wc)
+{
+ static char hex[16] = "0123456789ABCDEF";
+
+ s[0] = '\\';
+ if (sizeof (wchar_t) > 2 && wc > 0xffff)
+ {
+# if __STDC_ISO_10646__ || (__GLIBC__ >= 2) || (defined _WIN32 || defined __CYGWIN__)
+ s[1] = 'U';
+# else
+ s[1] = 'W';
+# endif
+ s[2] = hex[(wc & 0xf0000000U) >> 28];
+ s[3] = hex[(wc & 0xf000000U) >> 24];
+ s[4] = hex[(wc & 0xf00000U) >> 20];
+ s[5] = hex[(wc & 0xf0000U) >> 16];
+ s[6] = hex[(wc & 0xf000U) >> 12];
+ s[7] = hex[(wc & 0xf00U) >> 8];
+ s[8] = hex[(wc & 0xf0U) >> 4];
+ s[9] = hex[wc & 0xfU];
+ return 10;
+ }
+ else
+ {
+# if __STDC_ISO_10646__ || (__GLIBC__ >= 2) || (defined _WIN32 || defined __CYGWIN__)
+ s[1] = 'u';
+# else
+ s[1] = 'w';
+# endif
+ s[2] = hex[(wc & 0xf000U) >> 12];
+ s[3] = hex[(wc & 0xf00U) >> 8];
+ s[4] = hex[(wc & 0xf0U) >> 4];
+ s[5] = hex[wc & 0xfU];
+ return 6;
+ }
+}
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+static size_t
+local_wcrtomb (char *s, wchar_t wc, mbstate_t *ps)
+{
+ size_t count = wcrtomb (s, wc, ps);
+ if (count == (size_t)(-1))
+ count = wctomb_fallback (s, wc);
+ return count;
+}
+# else
+static int
+local_wctomb (char *s, wchar_t wc)
+{
+ int count = wctomb (s, wc);
+ if (count < 0)
+ count = wctomb_fallback (s, wc);
+ return count;
+}
+# define local_wcrtomb(S, WC, PS) local_wctomb ((S), (WC))
+# endif
+# else
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+# define local_wcrtomb(S, WC, PS) wcrtomb ((S), (WC), (PS))
+# else
+# define local_wcrtomb(S, WC, PS) wctomb ((S), (WC))
+# endif
+# endif
+#endif
+
+#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && !defined IN_LIBINTL
+/* Determine the decimal-point character according to the current locale. */
+# ifndef decimal_point_char_defined
+# define decimal_point_char_defined 1
+static char
+decimal_point_char (void)
+{
+ const char *point;
+ /* Determine it in a multithread-safe way. We know nl_langinfo is
+ multithread-safe on glibc systems and Mac OS X systems, but is not required
+ to be multithread-safe by POSIX. sprintf(), however, is multithread-safe.
+ localeconv() is rarely multithread-safe. */
+# if HAVE_NL_LANGINFO && (__GLIBC__ || defined __UCLIBC__ || (defined __APPLE__ && defined __MACH__))
+ point = nl_langinfo (RADIXCHAR);
+# elif 1
+ char pointbuf[5];
+ sprintf (pointbuf, "%#.0f", 1.0);
+ point = &pointbuf[1];
+# else
+ point = localeconv () -> decimal_point;
+# endif
+ /* The decimal point is always a single byte: either '.' or ','. */
+ return (point[0] != '\0' ? point[0] : '.');
+}
+# endif
+#endif
+
+#if NEED_PRINTF_INFINITE_DOUBLE && !NEED_PRINTF_DOUBLE && !defined IN_LIBINTL
+
+/* Equivalent to !isfinite(x) || x == 0, but does not require libm. */
+static int
+is_infinite_or_zero (double x)
+{
+ return isnand (x) || x + x == x;
+}
+
+#endif
+
+#if NEED_PRINTF_INFINITE_LONG_DOUBLE && !NEED_PRINTF_LONG_DOUBLE && !defined IN_LIBINTL
+
+/* Equivalent to !isfinite(x) || x == 0, but does not require libm. */
+static int
+is_infinite_or_zerol (long double x)
+{
+ return isnanl (x) || x + x == x;
+}
+
+#endif
+
+#if (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL
+
+/* Converting 'long double' to decimal without rare rounding bugs requires
+ real bignums. We use the naming conventions of GNU gmp, but vastly simpler
+ (and slower) algorithms. */
+
+typedef unsigned int mp_limb_t;
+# define GMP_LIMB_BITS 32
+static_assert (sizeof (mp_limb_t) * CHAR_BIT == GMP_LIMB_BITS);
+
+typedef unsigned long long mp_twolimb_t;
+# define GMP_TWOLIMB_BITS 64
+static_assert (sizeof (mp_twolimb_t) * CHAR_BIT == GMP_TWOLIMB_BITS);
+
+/* Representation of a bignum >= 0. */
+typedef struct
+{
+ size_t nlimbs;
+ mp_limb_t *limbs; /* Bits in little-endian order, allocated with malloc(). */
+} mpn_t;
+
+/* Compute the product of two bignums >= 0.
+ Return the allocated memory in case of success, NULL in case of memory
+ allocation failure. */
+static void *
+multiply (mpn_t src1, mpn_t src2, mpn_t *dest)
+{
+ const mp_limb_t *p1;
+ const mp_limb_t *p2;
+ size_t len1;
+ size_t len2;
+
+ if (src1.nlimbs <= src2.nlimbs)
+ {
+ len1 = src1.nlimbs;
+ p1 = src1.limbs;
+ len2 = src2.nlimbs;
+ p2 = src2.limbs;
+ }
+ else
+ {
+ len1 = src2.nlimbs;
+ p1 = src2.limbs;
+ len2 = src1.nlimbs;
+ p2 = src1.limbs;
+ }
+ /* Now 0 <= len1 <= len2. */
+ if (len1 == 0)
+ {
+ /* src1 or src2 is zero. */
+ dest->nlimbs = 0;
+ dest->limbs = (mp_limb_t *) malloc (1);
+ }
+ else
+ {
+ /* Here 1 <= len1 <= len2. */
+ size_t dlen;
+ mp_limb_t *dp;
+ size_t k, i, j;
+
+ dlen = len1 + len2;
+ dp = (mp_limb_t *) malloc (dlen * sizeof (mp_limb_t));
+ if (dp == NULL)
+ return NULL;
+ for (k = len2; k > 0; )
+ dp[--k] = 0;
+ for (i = 0; i < len1; i++)
+ {
+ mp_limb_t digit1 = p1[i];
+ mp_twolimb_t carry = 0;
+ for (j = 0; j < len2; j++)
+ {
+ mp_limb_t digit2 = p2[j];
+ carry += (mp_twolimb_t) digit1 * (mp_twolimb_t) digit2;
+ carry += dp[i + j];
+ dp[i + j] = (mp_limb_t) carry;
+ carry = carry >> GMP_LIMB_BITS;
+ }
+ dp[i + len2] = (mp_limb_t) carry;
+ }
+ /* Normalise. */
+ while (dlen > 0 && dp[dlen - 1] == 0)
+ dlen--;
+ dest->nlimbs = dlen;
+ dest->limbs = dp;
+ }
+ return dest->limbs;
+}
+
+/* Compute the quotient of a bignum a >= 0 and a bignum b > 0.
+ a is written as a = q * b + r with 0 <= r < b. q is the quotient, r
+ the remainder.
+ Finally, round-to-even is performed: If r > b/2 or if r = b/2 and q is odd,
+ q is incremented.
+ Return the allocated memory in case of success, NULL in case of memory
+ allocation failure. */
+static void *
+divide (mpn_t a, mpn_t b, mpn_t *q)
+{
+ /* Algorithm:
+ First normalise a and b: a=[a[m-1],...,a[0]], b=[b[n-1],...,b[0]]
+ with m>=0 and n>0 (in base beta = 2^GMP_LIMB_BITS).
+ If m<n, then q:=0 and r:=a.
+ If m>=n=1, perform a single-precision division:
+ r:=0, j:=m,
+ while j>0 do
+ {Here (q[m-1]*beta^(m-1)+...+q[j]*beta^j) * b[0] + r*beta^j =
+ = a[m-1]*beta^(m-1)+...+a[j]*beta^j und 0<=r<b[0]<beta}
+ j:=j-1, r:=r*beta+a[j], q[j]:=floor(r/b[0]), r:=r-b[0]*q[j].
+ Normalise [q[m-1],...,q[0]], yields q.
+ If m>=n>1, perform a multiple-precision division:
+ We have a/b < beta^(m-n+1).
+ s:=intDsize-1-(highest bit in b[n-1]), 0<=s<intDsize.
+ Shift a and b left by s bits, copying them. r:=a.
+ r=[r[m],...,r[0]], b=[b[n-1],...,b[0]] with b[n-1]>=beta/2.
+ For j=m-n,...,0: {Here 0 <= r < b*beta^(j+1).}
+ Compute q* :
+ q* := floor((r[j+n]*beta+r[j+n-1])/b[n-1]).
+ In case of overflow (q* >= beta) set q* := beta-1.
+ Compute c2 := ((r[j+n]*beta+r[j+n-1]) - q* * b[n-1])*beta + r[j+n-2]
+ and c3 := b[n-2] * q*.
+ {We have 0 <= c2 < 2*beta^2, even 0 <= c2 < beta^2 if no overflow
+ occurred. Furthermore 0 <= c3 < beta^2.
+ If there was overflow and
+ r[j+n]*beta+r[j+n-1] - q* * b[n-1] >= beta, i.e. c2 >= beta^2,
+ the next test can be skipped.}
+ While c3 > c2, {Here 0 <= c2 < c3 < beta^2}
+ Put q* := q* - 1, c2 := c2 + b[n-1]*beta, c3 := c3 - b[n-2].
+ If q* > 0:
+ Put r := r - b * q* * beta^j. In detail:
+ [r[n+j],...,r[j]] := [r[n+j],...,r[j]] - q* * [b[n-1],...,b[0]].
+ hence: u:=0, for i:=0 to n-1 do
+ u := u + q* * b[i],
+ r[j+i]:=r[j+i]-(u mod beta) (+ beta, if carry),
+ u:=u div beta (+ 1, if carry in subtraction)
+ r[n+j]:=r[n+j]-u.
+ {Since always u = (q* * [b[i-1],...,b[0]] div beta^i) + 1
+ < q* + 1 <= beta,
+ the carry u does not overflow.}
+ If a negative carry occurs, put q* := q* - 1
+ and [r[n+j],...,r[j]] := [r[n+j],...,r[j]] + [0,b[n-1],...,b[0]].
+ Set q[j] := q*.
+ Normalise [q[m-n],..,q[0]]; this yields the quotient q.
+ Shift [r[n-1],...,r[0]] right by s bits and normalise; this yields the
+ rest r.
+ The room for q[j] can be allocated at the memory location of r[n+j].
+ Finally, round-to-even:
+ Shift r left by 1 bit.
+ If r > b or if r = b and q[0] is odd, q := q+1.
+ */
+ const mp_limb_t *a_ptr = a.limbs;
+ size_t a_len = a.nlimbs;
+ const mp_limb_t *b_ptr = b.limbs;
+ size_t b_len = b.nlimbs;
+ mp_limb_t *roomptr;
+ mp_limb_t *tmp_roomptr = NULL;
+ mp_limb_t *q_ptr;
+ size_t q_len;
+ mp_limb_t *r_ptr;
+ size_t r_len;
+
+ /* Allocate room for a_len+2 digits.
+ (Need a_len+1 digits for the real division and 1 more digit for the
+ final rounding of q.) */
+ roomptr = (mp_limb_t *) malloc ((a_len + 2) * sizeof (mp_limb_t));
+ if (roomptr == NULL)
+ return NULL;
+
+ /* Normalise a. */
+ while (a_len > 0 && a_ptr[a_len - 1] == 0)
+ a_len--;
+
+ /* Normalise b. */
+ for (;;)
+ {
+ if (b_len == 0)
+ /* Division by zero. */
+ abort ();
+ if (b_ptr[b_len - 1] == 0)
+ b_len--;
+ else
+ break;
+ }
+
+ /* Here m = a_len >= 0 and n = b_len > 0. */
+
+ if (a_len < b_len)
+ {
+ /* m<n: trivial case. q=0, r := copy of a. */
+ r_ptr = roomptr;
+ r_len = a_len;
+ memcpy (r_ptr, a_ptr, a_len * sizeof (mp_limb_t));
+ q_ptr = roomptr + a_len;
+ q_len = 0;
+ }
+ else if (b_len == 1)
+ {
+ /* n=1: single precision division.
+ beta^(m-1) <= a < beta^m ==> beta^(m-2) <= a/b < beta^m */
+ r_ptr = roomptr;
+ q_ptr = roomptr + 1;
+ {
+ mp_limb_t den = b_ptr[0];
+ mp_limb_t remainder = 0;
+ const mp_limb_t *sourceptr = a_ptr + a_len;
+ mp_limb_t *destptr = q_ptr + a_len;
+ size_t count;
+ for (count = a_len; count > 0; count--)
+ {
+ mp_twolimb_t num =
+ ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--sourceptr;
+ *--destptr = num / den;
+ remainder = num % den;
+ }
+ /* Normalise and store r. */
+ if (remainder > 0)
+ {
+ r_ptr[0] = remainder;
+ r_len = 1;
+ }
+ else
+ r_len = 0;
+ /* Normalise q. */
+ q_len = a_len;
+ if (q_ptr[q_len - 1] == 0)
+ q_len--;
+ }
+ }
+ else
+ {
+ /* n>1: multiple precision division.
+ beta^(m-1) <= a < beta^m, beta^(n-1) <= b < beta^n ==>
+ beta^(m-n-1) <= a/b < beta^(m-n+1). */
+ /* Determine s. */
+ size_t s;
+ {
+ mp_limb_t msd = b_ptr[b_len - 1]; /* = b[n-1], > 0 */
+ /* Determine s = GMP_LIMB_BITS - integer_length (msd).
+ Code copied from gnulib's integer_length.c. */
+# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4) \
+ || (__clang_major__ >= 4)
+ s = __builtin_clz (msd);
+# else
+# if defined DBL_EXPBIT0_WORD && defined DBL_EXPBIT0_BIT
+ if (GMP_LIMB_BITS <= DBL_MANT_BIT)
+ {
+ /* Use 'double' operations.
+ Assumes an IEEE 754 'double' implementation. */
+# define DBL_EXP_MASK ((DBL_MAX_EXP - DBL_MIN_EXP) | 7)
+# define DBL_EXP_BIAS (DBL_EXP_MASK / 2 - 1)
+# define NWORDS \
+ ((sizeof (double) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+ union { double value; unsigned int word[NWORDS]; } m;
+
+ /* Use a single integer to floating-point conversion. */
+ m.value = msd;
+
+ s = GMP_LIMB_BITS
+ - (((m.word[DBL_EXPBIT0_WORD] >> DBL_EXPBIT0_BIT) & DBL_EXP_MASK)
+ - DBL_EXP_BIAS);
+ }
+ else
+# undef NWORDS
+# endif
+ {
+ s = 31;
+ if (msd >= 0x10000)
+ {
+ msd = msd >> 16;
+ s -= 16;
+ }
+ if (msd >= 0x100)
+ {
+ msd = msd >> 8;
+ s -= 8;
+ }
+ if (msd >= 0x10)
+ {
+ msd = msd >> 4;
+ s -= 4;
+ }
+ if (msd >= 0x4)
+ {
+ msd = msd >> 2;
+ s -= 2;
+ }
+ if (msd >= 0x2)
+ {
+ msd = msd >> 1;
+ s -= 1;
+ }
+ }
+# endif
+ }
+ /* 0 <= s < GMP_LIMB_BITS.
+ Copy b, shifting it left by s bits. */
+ if (s > 0)
+ {
+ tmp_roomptr = (mp_limb_t *) malloc (b_len * sizeof (mp_limb_t));
+ if (tmp_roomptr == NULL)
+ {
+ free (roomptr);
+ return NULL;
+ }
+ {
+ const mp_limb_t *sourceptr = b_ptr;
+ mp_limb_t *destptr = tmp_roomptr;
+ mp_twolimb_t accu = 0;
+ size_t count;
+ for (count = b_len; count > 0; count--)
+ {
+ accu += (mp_twolimb_t) *sourceptr++ << s;
+ *destptr++ = (mp_limb_t) accu;
+ accu = accu >> GMP_LIMB_BITS;
+ }
+ /* accu must be zero, since that was how s was determined. */
+ if (accu != 0)
+ abort ();
+ }
+ b_ptr = tmp_roomptr;
+ }
+ /* Copy a, shifting it left by s bits, yields r.
+ Memory layout:
+ At the beginning: r = roomptr[0..a_len],
+ at the end: r = roomptr[0..b_len-1], q = roomptr[b_len..a_len] */
+ r_ptr = roomptr;
+ if (s == 0)
+ {
+ memcpy (r_ptr, a_ptr, a_len * sizeof (mp_limb_t));
+ r_ptr[a_len] = 0;
+ }
+ else
+ {
+ const mp_limb_t *sourceptr = a_ptr;
+ mp_limb_t *destptr = r_ptr;
+ mp_twolimb_t accu = 0;
+ size_t count;
+ for (count = a_len; count > 0; count--)
+ {
+ accu += (mp_twolimb_t) *sourceptr++ << s;
+ *destptr++ = (mp_limb_t) accu;
+ accu = accu >> GMP_LIMB_BITS;
+ }
+ *destptr++ = (mp_limb_t) accu;
+ }
+ q_ptr = roomptr + b_len;
+ q_len = a_len - b_len + 1; /* q will have m-n+1 limbs */
+ {
+ size_t j = a_len - b_len; /* m-n */
+ mp_limb_t b_msd = b_ptr[b_len - 1]; /* b[n-1] */
+ mp_limb_t b_2msd = b_ptr[b_len - 2]; /* b[n-2] */
+ mp_twolimb_t b_msdd = /* b[n-1]*beta+b[n-2] */
+ ((mp_twolimb_t) b_msd << GMP_LIMB_BITS) | b_2msd;
+ /* Division loop, traversed m-n+1 times.
+ j counts down, b is unchanged, beta/2 <= b[n-1] < beta. */
+ for (;;)
+ {
+ mp_limb_t q_star;
+ mp_limb_t c1;
+ if (r_ptr[j + b_len] < b_msd) /* r[j+n] < b[n-1] ? */
+ {
+ /* Divide r[j+n]*beta+r[j+n-1] by b[n-1], no overflow. */
+ mp_twolimb_t num =
+ ((mp_twolimb_t) r_ptr[j + b_len] << GMP_LIMB_BITS)
+ | r_ptr[j + b_len - 1];
+ q_star = num / b_msd;
+ c1 = num % b_msd;
+ }
+ else
+ {
+ /* Overflow, hence r[j+n]*beta+r[j+n-1] >= beta*b[n-1]. */
+ q_star = (mp_limb_t)~(mp_limb_t)0; /* q* = beta-1 */
+ /* Test whether r[j+n]*beta+r[j+n-1] - (beta-1)*b[n-1] >= beta
+ <==> r[j+n]*beta+r[j+n-1] + b[n-1] >= beta*b[n-1]+beta
+ <==> b[n-1] < floor((r[j+n]*beta+r[j+n-1]+b[n-1])/beta)
+ {<= beta !}.
+ If yes, jump directly to the subtraction loop.
+ (Otherwise, r[j+n]*beta+r[j+n-1] - (beta-1)*b[n-1] < beta
+ <==> floor((r[j+n]*beta+r[j+n-1]+b[n-1])/beta) = b[n-1] ) */
+ if (r_ptr[j + b_len] > b_msd
+ || (c1 = r_ptr[j + b_len - 1] + b_msd) < b_msd)
+ /* r[j+n] >= b[n-1]+1 or
+ r[j+n] = b[n-1] and the addition r[j+n-1]+b[n-1] gives a
+ carry. */
+ goto subtract;
+ }
+ /* q_star = q*,
+ c1 = (r[j+n]*beta+r[j+n-1]) - q* * b[n-1] (>=0, <beta). */
+ {
+ mp_twolimb_t c2 = /* c1*beta+r[j+n-2] */
+ ((mp_twolimb_t) c1 << GMP_LIMB_BITS) | r_ptr[j + b_len - 2];
+ mp_twolimb_t c3 = /* b[n-2] * q* */
+ (mp_twolimb_t) b_2msd * (mp_twolimb_t) q_star;
+ /* While c2 < c3, increase c2 and decrease c3.
+ Consider c3-c2. While it is > 0, decrease it by
+ b[n-1]*beta+b[n-2]. Because of b[n-1]*beta+b[n-2] >= beta^2/2
+ this can happen only twice. */
+ if (c3 > c2)
+ {
+ q_star = q_star - 1; /* q* := q* - 1 */
+ if (c3 - c2 > b_msdd)
+ q_star = q_star - 1; /* q* := q* - 1 */
+ }
+ }
+ if (q_star > 0)
+ subtract:
+ {
+ /* Subtract r := r - b * q* * beta^j. */
+ mp_limb_t cr;
+ {
+ const mp_limb_t *sourceptr = b_ptr;
+ mp_limb_t *destptr = r_ptr + j;
+ mp_twolimb_t carry = 0;
+ size_t count;
+ for (count = b_len; count > 0; count--)
+ {
+ /* Here 0 <= carry <= q*. */
+ carry =
+ carry
+ + (mp_twolimb_t) q_star * (mp_twolimb_t) *sourceptr++
+ + (mp_limb_t) ~(*destptr);
+ /* Here 0 <= carry <= beta*q* + beta-1. */
+ *destptr++ = ~(mp_limb_t) carry;
+ carry = carry >> GMP_LIMB_BITS; /* <= q* */
+ }
+ cr = (mp_limb_t) carry;
+ }
+ /* Subtract cr from r_ptr[j + b_len], then forget about
+ r_ptr[j + b_len]. */
+ if (cr > r_ptr[j + b_len])
+ {
+ /* Subtraction gave a carry. */
+ q_star = q_star - 1; /* q* := q* - 1 */
+ /* Add b back. */
+ {
+ const mp_limb_t *sourceptr = b_ptr;
+ mp_limb_t *destptr = r_ptr + j;
+ mp_limb_t carry = 0;
+ size_t count;
+ for (count = b_len; count > 0; count--)
+ {
+ mp_limb_t source1 = *sourceptr++;
+ mp_limb_t source2 = *destptr;
+ *destptr++ = source1 + source2 + carry;
+ carry =
+ (carry
+ ? source1 >= (mp_limb_t) ~source2
+ : source1 > (mp_limb_t) ~source2);
+ }
+ }
+ /* Forget about the carry and about r[j+n]. */
+ }
+ }
+ /* q* is determined. Store it as q[j]. */
+ q_ptr[j] = q_star;
+ if (j == 0)
+ break;
+ j--;
+ }
+ }
+ r_len = b_len;
+ /* Normalise q. */
+ if (q_ptr[q_len - 1] == 0)
+ q_len--;
+# if 0 /* Not needed here, since we need r only to compare it with b/2, and
+ b is shifted left by s bits. */
+ /* Shift r right by s bits. */
+ if (s > 0)
+ {
+ mp_limb_t ptr = r_ptr + r_len;
+ mp_twolimb_t accu = 0;
+ size_t count;
+ for (count = r_len; count > 0; count--)
+ {
+ accu = (mp_twolimb_t) (mp_limb_t) accu << GMP_LIMB_BITS;
+ accu += (mp_twolimb_t) *--ptr << (GMP_LIMB_BITS - s);
+ *ptr = (mp_limb_t) (accu >> GMP_LIMB_BITS);
+ }
+ }
+# endif
+ /* Normalise r. */
+ while (r_len > 0 && r_ptr[r_len - 1] == 0)
+ r_len--;
+ }
+ /* Compare r << 1 with b. */
+ if (r_len > b_len)
+ goto increment_q;
+ {
+ size_t i;
+ for (i = b_len;;)
+ {
+ mp_limb_t r_i =
+ (i <= r_len && i > 0 ? r_ptr[i - 1] >> (GMP_LIMB_BITS - 1) : 0)
+ | (i < r_len ? r_ptr[i] << 1 : 0);
+ mp_limb_t b_i = (i < b_len ? b_ptr[i] : 0);
+ if (r_i > b_i)
+ goto increment_q;
+ if (r_i < b_i)
+ goto keep_q;
+ if (i == 0)
+ break;
+ i--;
+ }
+ }
+ if (q_len > 0 && ((q_ptr[0] & 1) != 0))
+ /* q is odd. */
+ increment_q:
+ {
+ size_t i;
+ for (i = 0; i < q_len; i++)
+ if (++(q_ptr[i]) != 0)
+ goto keep_q;
+ q_ptr[q_len++] = 1;
+ }
+ keep_q:
+ free (tmp_roomptr);
+ q->limbs = q_ptr;
+ q->nlimbs = q_len;
+ return roomptr;
+}
+
+/* Convert a bignum a >= 0, multiplied with 10^extra_zeroes, to decimal
+ representation.
+ Destroys the contents of a.
+ Return the allocated memory - containing the decimal digits in low-to-high
+ order, terminated with a NUL character - in case of success, NULL in case
+ of memory allocation failure. */
+static char *
+convert_to_decimal (mpn_t a, size_t extra_zeroes)
+{
+ mp_limb_t *a_ptr = a.limbs;
+ size_t a_len = a.nlimbs;
+ /* 0.03345 is slightly larger than log(2)/(9*log(10)). */
+ size_t c_len = 9 * ((size_t)(a_len * (GMP_LIMB_BITS * 0.03345f)) + 1);
+ /* We need extra_zeroes bytes for zeroes, followed by c_len bytes for the
+ digits of a, followed by 1 byte for the terminating NUL. */
+ char *c_ptr = (char *) malloc (xsum (xsum (extra_zeroes, c_len), 1));
+ if (c_ptr != NULL)
+ {
+ char *d_ptr = c_ptr;
+ for (; extra_zeroes > 0; extra_zeroes--)
+ *d_ptr++ = '0';
+ while (a_len > 0)
+ {
+ /* Divide a by 10^9, in-place. */
+ mp_limb_t remainder = 0;
+ mp_limb_t *ptr = a_ptr + a_len;
+ size_t count;
+ for (count = a_len; count > 0; count--)
+ {
+ mp_twolimb_t num =
+ ((mp_twolimb_t) remainder << GMP_LIMB_BITS) | *--ptr;
+ *ptr = num / 1000000000;
+ remainder = num % 1000000000;
+ }
+ /* Store the remainder as 9 decimal digits. */
+ for (count = 9; count > 0; count--)
+ {
+ *d_ptr++ = '0' + (remainder % 10);
+ remainder = remainder / 10;
+ }
+ /* Normalize a. */
+ if (a_ptr[a_len - 1] == 0)
+ a_len--;
+ }
+ /* Remove leading zeroes. */
+ while (d_ptr > c_ptr && d_ptr[-1] == '0')
+ d_ptr--;
+ /* But keep at least one zero. */
+ if (d_ptr == c_ptr)
+ *d_ptr++ = '0';
+ /* Terminate the string. */
+ *d_ptr = '\0';
+ }
+ return c_ptr;
+}
+
+# if NEED_PRINTF_LONG_DOUBLE
+
+/* Assuming x is finite and >= 0:
+ write x as x = 2^e * m, where m is a bignum.
+ Return the allocated memory in case of success, NULL in case of memory
+ allocation failure. */
+static void *
+decode_long_double (long double x, int *ep, mpn_t *mp)
+{
+ mpn_t m;
+ int exp;
+ long double y;
+ size_t i;
+
+ /* Allocate memory for result. */
+ m.nlimbs = (LDBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS;
+ m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t));
+ if (m.limbs == NULL)
+ return NULL;
+ /* Split into exponential part and mantissa. */
+ y = frexpl (x, &exp);
+ if (!(y >= 0.0L && y < 1.0L))
+ abort ();
+ /* x = 2^exp * y = 2^(exp - LDBL_MANT_BIT) * (y * 2^LDBL_MANT_BIT), and the
+ latter is an integer. */
+ /* Convert the mantissa (y * 2^LDBL_MANT_BIT) to a sequence of limbs.
+ I'm not sure whether it's safe to cast a 'long double' value between
+ 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only
+ 'long double' values between 0 and 2^16 (to 'unsigned int' or 'int',
+ doesn't matter). */
+# if (LDBL_MANT_BIT % GMP_LIMB_BITS) != 0
+# if (LDBL_MANT_BIT % GMP_LIMB_BITS) > GMP_LIMB_BITS / 2
+ {
+ mp_limb_t hi, lo;
+ y *= (mp_limb_t) 1 << (LDBL_MANT_BIT % (GMP_LIMB_BITS / 2));
+ hi = (int) y;
+ y -= hi;
+ if (!(y >= 0.0L && y < 1.0L))
+ abort ();
+ y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2);
+ lo = (int) y;
+ y -= lo;
+ if (!(y >= 0.0L && y < 1.0L))
+ abort ();
+ m.limbs[LDBL_MANT_BIT / GMP_LIMB_BITS] = (hi << (GMP_LIMB_BITS / 2)) | lo;
+ }
+# else
+ {
+ mp_limb_t d;
+ y *= (mp_limb_t) 1 << (LDBL_MANT_BIT % GMP_LIMB_BITS);
+ d = (int) y;
+ y -= d;
+ if (!(y >= 0.0L && y < 1.0L))
+ abort ();
+ m.limbs[LDBL_MANT_BIT / GMP_LIMB_BITS] = d;
+ }
+# endif
+# endif
+ for (i = LDBL_MANT_BIT / GMP_LIMB_BITS; i > 0; )
+ {
+ mp_limb_t hi, lo;
+ y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2);
+ hi = (int) y;
+ y -= hi;
+ if (!(y >= 0.0L && y < 1.0L))
+ abort ();
+ y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2);
+ lo = (int) y;
+ y -= lo;
+ if (!(y >= 0.0L && y < 1.0L))
+ abort ();
+ m.limbs[--i] = (hi << (GMP_LIMB_BITS / 2)) | lo;
+ }
+# if 0 /* On FreeBSD 6.1/x86, 'long double' numbers sometimes have excess
+ precision. */
+ if (!(y == 0.0L))
+ abort ();
+# endif
+ /* Normalise. */
+ while (m.nlimbs > 0 && m.limbs[m.nlimbs - 1] == 0)
+ m.nlimbs--;
+ *mp = m;
+ *ep = exp - LDBL_MANT_BIT;
+ return m.limbs;
+}
+
+# endif
+
+# if NEED_PRINTF_DOUBLE
+
+/* Assuming x is finite and >= 0:
+ write x as x = 2^e * m, where m is a bignum.
+ Return the allocated memory in case of success, NULL in case of memory
+ allocation failure. */
+static void *
+decode_double (double x, int *ep, mpn_t *mp)
+{
+ mpn_t m;
+ int exp;
+ double y;
+ size_t i;
+
+ /* Allocate memory for result. */
+ m.nlimbs = (DBL_MANT_BIT + GMP_LIMB_BITS - 1) / GMP_LIMB_BITS;
+ m.limbs = (mp_limb_t *) malloc (m.nlimbs * sizeof (mp_limb_t));
+ if (m.limbs == NULL)
+ return NULL;
+ /* Split into exponential part and mantissa. */
+ y = frexp (x, &exp);
+ if (!(y >= 0.0 && y < 1.0))
+ abort ();
+ /* x = 2^exp * y = 2^(exp - DBL_MANT_BIT) * (y * 2^DBL_MANT_BIT), and the
+ latter is an integer. */
+ /* Convert the mantissa (y * 2^DBL_MANT_BIT) to a sequence of limbs.
+ I'm not sure whether it's safe to cast a 'double' value between
+ 2^31 and 2^32 to 'unsigned int', therefore play safe and cast only
+ 'double' values between 0 and 2^16 (to 'unsigned int' or 'int',
+ doesn't matter). */
+# if (DBL_MANT_BIT % GMP_LIMB_BITS) != 0
+# if (DBL_MANT_BIT % GMP_LIMB_BITS) > GMP_LIMB_BITS / 2
+ {
+ mp_limb_t hi, lo;
+ y *= (mp_limb_t) 1 << (DBL_MANT_BIT % (GMP_LIMB_BITS / 2));
+ hi = (int) y;
+ y -= hi;
+ if (!(y >= 0.0 && y < 1.0))
+ abort ();
+ y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2);
+ lo = (int) y;
+ y -= lo;
+ if (!(y >= 0.0 && y < 1.0))
+ abort ();
+ m.limbs[DBL_MANT_BIT / GMP_LIMB_BITS] = (hi << (GMP_LIMB_BITS / 2)) | lo;
+ }
+# else
+ {
+ mp_limb_t d;
+ y *= (mp_limb_t) 1 << (DBL_MANT_BIT % GMP_LIMB_BITS);
+ d = (int) y;
+ y -= d;
+ if (!(y >= 0.0 && y < 1.0))
+ abort ();
+ m.limbs[DBL_MANT_BIT / GMP_LIMB_BITS] = d;
+ }
+# endif
+# endif
+ for (i = DBL_MANT_BIT / GMP_LIMB_BITS; i > 0; )
+ {
+ mp_limb_t hi, lo;
+ y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2);
+ hi = (int) y;
+ y -= hi;
+ if (!(y >= 0.0 && y < 1.0))
+ abort ();
+ y *= (mp_limb_t) 1 << (GMP_LIMB_BITS / 2);
+ lo = (int) y;
+ y -= lo;
+ if (!(y >= 0.0 && y < 1.0))
+ abort ();
+ m.limbs[--i] = (hi << (GMP_LIMB_BITS / 2)) | lo;
+ }
+ if (!(y == 0.0))
+ abort ();
+ /* Normalise. */
+ while (m.nlimbs > 0 && m.limbs[m.nlimbs - 1] == 0)
+ m.nlimbs--;
+ *mp = m;
+ *ep = exp - DBL_MANT_BIT;
+ return m.limbs;
+}
+
+# endif
+
+/* Assuming x = 2^e * m is finite and >= 0, and n is an integer:
+ Returns the decimal representation of round (x * 10^n).
+ Return the allocated memory - containing the decimal digits in low-to-high
+ order, terminated with a NUL character - in case of success, NULL in case
+ of memory allocation failure. */
+static char *
+scale10_round_decimal_decoded (int e, mpn_t m, void *memory, int n)
+{
+ int s;
+ size_t extra_zeroes;
+ unsigned int abs_n;
+ unsigned int abs_s;
+ mp_limb_t *pow5_ptr;
+ size_t pow5_len;
+ unsigned int s_limbs;
+ unsigned int s_bits;
+ mpn_t pow5;
+ mpn_t z;
+ void *z_memory;
+ char *digits;
+
+ if (memory == NULL)
+ return NULL;
+ /* x = 2^e * m, hence
+ y = round (2^e * 10^n * m) = round (2^(e+n) * 5^n * m)
+ = round (2^s * 5^n * m). */
+ s = e + n;
+ extra_zeroes = 0;
+ /* Factor out a common power of 10 if possible. */
+ if (s > 0 && n > 0)
+ {
+ extra_zeroes = (s < n ? s : n);
+ s -= extra_zeroes;
+ n -= extra_zeroes;
+ }
+ /* Here y = round (2^s * 5^n * m) * 10^extra_zeroes.
+ Before converting to decimal, we need to compute
+ z = round (2^s * 5^n * m). */
+ /* Compute 5^|n|, possibly shifted by |s| bits if n and s have the same
+ sign. 2.322 is slightly larger than log(5)/log(2). */
+ abs_n = (n >= 0 ? n : -n);
+ abs_s = (s >= 0 ? s : -s);
+ pow5_ptr = (mp_limb_t *) malloc (((int)(abs_n * (2.322f / GMP_LIMB_BITS)) + 1
+ + abs_s / GMP_LIMB_BITS + 1)
+ * sizeof (mp_limb_t));
+ if (pow5_ptr == NULL)
+ {
+ free (memory);
+ return NULL;
+ }
+ /* Initialize with 1. */
+ pow5_ptr[0] = 1;
+ pow5_len = 1;
+ /* Multiply with 5^|n|. */
+ if (abs_n > 0)
+ {
+ static mp_limb_t const small_pow5[13 + 1] =
+ {
+ 1, 5, 25, 125, 625, 3125, 15625, 78125, 390625, 1953125, 9765625,
+ 48828125, 244140625, 1220703125
+ };
+ unsigned int n13;
+ for (n13 = 0; n13 <= abs_n; n13 += 13)
+ {
+ mp_limb_t digit1 = small_pow5[n13 + 13 <= abs_n ? 13 : abs_n - n13];
+ size_t j;
+ mp_twolimb_t carry = 0;
+ for (j = 0; j < pow5_len; j++)
+ {
+ mp_limb_t digit2 = pow5_ptr[j];
+ carry += (mp_twolimb_t) digit1 * (mp_twolimb_t) digit2;
+ pow5_ptr[j] = (mp_limb_t) carry;
+ carry = carry >> GMP_LIMB_BITS;
+ }
+ if (carry > 0)
+ pow5_ptr[pow5_len++] = (mp_limb_t) carry;
+ }
+ }
+ s_limbs = abs_s / GMP_LIMB_BITS;
+ s_bits = abs_s % GMP_LIMB_BITS;
+ if (n >= 0 ? s >= 0 : s <= 0)
+ {
+ /* Multiply with 2^|s|. */
+ if (s_bits > 0)
+ {
+ mp_limb_t *ptr = pow5_ptr;
+ mp_twolimb_t accu = 0;
+ size_t count;
+ for (count = pow5_len; count > 0; count--)
+ {
+ accu += (mp_twolimb_t) *ptr << s_bits;
+ *ptr++ = (mp_limb_t) accu;
+ accu = accu >> GMP_LIMB_BITS;
+ }
+ if (accu > 0)
+ {
+ *ptr = (mp_limb_t) accu;
+ pow5_len++;
+ }
+ }
+ if (s_limbs > 0)
+ {
+ size_t count;
+ for (count = pow5_len; count > 0;)
+ {
+ count--;
+ pow5_ptr[s_limbs + count] = pow5_ptr[count];
+ }
+ for (count = s_limbs; count > 0;)
+ {
+ count--;
+ pow5_ptr[count] = 0;
+ }
+ pow5_len += s_limbs;
+ }
+ pow5.limbs = pow5_ptr;
+ pow5.nlimbs = pow5_len;
+ if (n >= 0)
+ {
+ /* Multiply m with pow5. No division needed. */
+ z_memory = multiply (m, pow5, &z);
+ }
+ else
+ {
+ /* Divide m by pow5 and round. */
+ z_memory = divide (m, pow5, &z);
+ }
+ }
+ else
+ {
+ pow5.limbs = pow5_ptr;
+ pow5.nlimbs = pow5_len;
+ if (n >= 0)
+ {
+ /* n >= 0, s < 0.
+ Multiply m with pow5, then divide by 2^|s|. */
+ mpn_t numerator;
+ mpn_t denominator;
+ void *tmp_memory;
+ tmp_memory = multiply (m, pow5, &numerator);
+ if (tmp_memory == NULL)
+ {
+ free (pow5_ptr);
+ free (memory);
+ return NULL;
+ }
+ /* Construct 2^|s|. */
+ {
+ mp_limb_t *ptr = pow5_ptr + pow5_len;
+ size_t i;
+ for (i = 0; i < s_limbs; i++)
+ ptr[i] = 0;
+ ptr[s_limbs] = (mp_limb_t) 1 << s_bits;
+ denominator.limbs = ptr;
+ denominator.nlimbs = s_limbs + 1;
+ }
+ z_memory = divide (numerator, denominator, &z);
+ free (tmp_memory);
+ }
+ else
+ {
+ /* n < 0, s > 0.
+ Multiply m with 2^s, then divide by pow5. */
+ mpn_t numerator;
+ mp_limb_t *num_ptr;
+ num_ptr = (mp_limb_t *) malloc ((m.nlimbs + s_limbs + 1)
+ * sizeof (mp_limb_t));
+ if (num_ptr == NULL)
+ {
+ free (pow5_ptr);
+ free (memory);
+ return NULL;
+ }
+ {
+ mp_limb_t *destptr = num_ptr;
+ {
+ size_t i;
+ for (i = 0; i < s_limbs; i++)
+ *destptr++ = 0;
+ }
+ if (s_bits > 0)
+ {
+ const mp_limb_t *sourceptr = m.limbs;
+ mp_twolimb_t accu = 0;
+ size_t count;
+ for (count = m.nlimbs; count > 0; count--)
+ {
+ accu += (mp_twolimb_t) *sourceptr++ << s_bits;
+ *destptr++ = (mp_limb_t) accu;
+ accu = accu >> GMP_LIMB_BITS;
+ }
+ if (accu > 0)
+ *destptr++ = (mp_limb_t) accu;
+ }
+ else
+ {
+ const mp_limb_t *sourceptr = m.limbs;
+ size_t count;
+ for (count = m.nlimbs; count > 0; count--)
+ *destptr++ = *sourceptr++;
+ }
+ numerator.limbs = num_ptr;
+ numerator.nlimbs = destptr - num_ptr;
+ }
+ z_memory = divide (numerator, pow5, &z);
+ free (num_ptr);
+ }
+ }
+ free (pow5_ptr);
+ free (memory);
+
+ /* Here y = round (x * 10^n) = z * 10^extra_zeroes. */
+
+ if (z_memory == NULL)
+ return NULL;
+ digits = convert_to_decimal (z, extra_zeroes);
+ free (z_memory);
+ return digits;
+}
+
+# if NEED_PRINTF_LONG_DOUBLE
+
+/* Assuming x is finite and >= 0, and n is an integer:
+ Returns the decimal representation of round (x * 10^n).
+ Return the allocated memory - containing the decimal digits in low-to-high
+ order, terminated with a NUL character - in case of success, NULL in case
+ of memory allocation failure. */
+static char *
+scale10_round_decimal_long_double (long double x, int n)
+{
+ int e IF_LINT(= 0);
+ mpn_t m;
+ void *memory = decode_long_double (x, &e, &m);
+ return scale10_round_decimal_decoded (e, m, memory, n);
+}
+
+# endif
+
+# if NEED_PRINTF_DOUBLE
+
+/* Assuming x is finite and >= 0, and n is an integer:
+ Returns the decimal representation of round (x * 10^n).
+ Return the allocated memory - containing the decimal digits in low-to-high
+ order, terminated with a NUL character - in case of success, NULL in case
+ of memory allocation failure. */
+static char *
+scale10_round_decimal_double (double x, int n)
+{
+ int e IF_LINT(= 0);
+ mpn_t m;
+ void *memory = decode_double (x, &e, &m);
+ return scale10_round_decimal_decoded (e, m, memory, n);
+}
+
+# endif
+
+# if NEED_PRINTF_LONG_DOUBLE
+
+/* Assuming x is finite and > 0:
+ Return an approximation for n with 10^n <= x < 10^(n+1).
+ The approximation is usually the right n, but may be off by 1 sometimes. */
+static int
+floorlog10l (long double x)
+{
+ int exp;
+ long double y;
+ double z;
+ double l;
+
+ /* Split into exponential part and mantissa. */
+ y = frexpl (x, &exp);
+ if (!(y >= 0.0L && y < 1.0L))
+ abort ();
+ if (y == 0.0L)
+ return INT_MIN;
+ if (y < 0.5L)
+ {
+ while (y < (1.0L / (1 << (GMP_LIMB_BITS / 2)) / (1 << (GMP_LIMB_BITS / 2))))
+ {
+ y *= 1.0L * (1 << (GMP_LIMB_BITS / 2)) * (1 << (GMP_LIMB_BITS / 2));
+ exp -= GMP_LIMB_BITS;
+ }
+ if (y < (1.0L / (1 << 16)))
+ {
+ y *= 1.0L * (1 << 16);
+ exp -= 16;
+ }
+ if (y < (1.0L / (1 << 8)))
+ {
+ y *= 1.0L * (1 << 8);
+ exp -= 8;
+ }
+ if (y < (1.0L / (1 << 4)))
+ {
+ y *= 1.0L * (1 << 4);
+ exp -= 4;
+ }
+ if (y < (1.0L / (1 << 2)))
+ {
+ y *= 1.0L * (1 << 2);
+ exp -= 2;
+ }
+ if (y < (1.0L / (1 << 1)))
+ {
+ y *= 1.0L * (1 << 1);
+ exp -= 1;
+ }
+ }
+ if (!(y >= 0.5L && y < 1.0L))
+ abort ();
+ /* Compute an approximation for l = log2(x) = exp + log2(y). */
+ l = exp;
+ z = y;
+ if (z < 0.70710678118654752444)
+ {
+ z *= 1.4142135623730950488;
+ l -= 0.5;
+ }
+ if (z < 0.8408964152537145431)
+ {
+ z *= 1.1892071150027210667;
+ l -= 0.25;
+ }
+ if (z < 0.91700404320467123175)
+ {
+ z *= 1.0905077326652576592;
+ l -= 0.125;
+ }
+ if (z < 0.9576032806985736469)
+ {
+ z *= 1.0442737824274138403;
+ l -= 0.0625;
+ }
+ /* Now 0.95 <= z <= 1.01. */
+ z = 1 - z;
+ /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...)
+ Four terms are enough to get an approximation with error < 10^-7. */
+ l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25)));
+ /* Finally multiply with log(2)/log(10), yields an approximation for
+ log10(x). */
+ l *= 0.30102999566398119523;
+ /* Round down to the next integer. */
+ return (int) l + (l < 0 ? -1 : 0);
+}
+
+# endif
+
+# if NEED_PRINTF_DOUBLE
+
+/* Assuming x is finite and > 0:
+ Return an approximation for n with 10^n <= x < 10^(n+1).
+ The approximation is usually the right n, but may be off by 1 sometimes. */
+static int
+floorlog10 (double x)
+{
+ int exp;
+ double y;
+ double z;
+ double l;
+
+ /* Split into exponential part and mantissa. */
+ y = frexp (x, &exp);
+ if (!(y >= 0.0 && y < 1.0))
+ abort ();
+ if (y == 0.0)
+ return INT_MIN;
+ if (y < 0.5)
+ {
+ while (y < (1.0 / (1 << (GMP_LIMB_BITS / 2)) / (1 << (GMP_LIMB_BITS / 2))))
+ {
+ y *= 1.0 * (1 << (GMP_LIMB_BITS / 2)) * (1 << (GMP_LIMB_BITS / 2));
+ exp -= GMP_LIMB_BITS;
+ }
+ if (y < (1.0 / (1 << 16)))
+ {
+ y *= 1.0 * (1 << 16);
+ exp -= 16;
+ }
+ if (y < (1.0 / (1 << 8)))
+ {
+ y *= 1.0 * (1 << 8);
+ exp -= 8;
+ }
+ if (y < (1.0 / (1 << 4)))
+ {
+ y *= 1.0 * (1 << 4);
+ exp -= 4;
+ }
+ if (y < (1.0 / (1 << 2)))
+ {
+ y *= 1.0 * (1 << 2);
+ exp -= 2;
+ }
+ if (y < (1.0 / (1 << 1)))
+ {
+ y *= 1.0 * (1 << 1);
+ exp -= 1;
+ }
+ }
+ if (!(y >= 0.5 && y < 1.0))
+ abort ();
+ /* Compute an approximation for l = log2(x) = exp + log2(y). */
+ l = exp;
+ z = y;
+ if (z < 0.70710678118654752444)
+ {
+ z *= 1.4142135623730950488;
+ l -= 0.5;
+ }
+ if (z < 0.8408964152537145431)
+ {
+ z *= 1.1892071150027210667;
+ l -= 0.25;
+ }
+ if (z < 0.91700404320467123175)
+ {
+ z *= 1.0905077326652576592;
+ l -= 0.125;
+ }
+ if (z < 0.9576032806985736469)
+ {
+ z *= 1.0442737824274138403;
+ l -= 0.0625;
+ }
+ /* Now 0.95 <= z <= 1.01. */
+ z = 1 - z;
+ /* log2(1-z) = 1/log(2) * (- z - z^2/2 - z^3/3 - z^4/4 - ...)
+ Four terms are enough to get an approximation with error < 10^-7. */
+ l -= 1.4426950408889634074 * z * (1.0 + z * (0.5 + z * ((1.0 / 3) + z * 0.25)));
+ /* Finally multiply with log(2)/log(10), yields an approximation for
+ log10(x). */
+ l *= 0.30102999566398119523;
+ /* Round down to the next integer. */
+ return (int) l + (l < 0 ? -1 : 0);
+}
+
+# endif
+
+/* Tests whether a string of digits consists of exactly PRECISION zeroes and
+ a single '1' digit. */
+static int
+is_borderline (const char *digits, size_t precision)
+{
+ for (; precision > 0; precision--, digits++)
+ if (*digits != '0')
+ return 0;
+ if (*digits != '1')
+ return 0;
+ digits++;
+ return *digits == '\0';
+}
+
+#endif
+
+#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF
+
+/* Use a different function name, to make it possible that the 'wchar_t'
+ parametrization and the 'char' parametrization get compiled in the same
+ translation unit. */
+# if WIDE_CHAR_VERSION
+# define MAX_ROOM_NEEDED wmax_room_needed
+# else
+# define MAX_ROOM_NEEDED max_room_needed
+# endif
+
+/* Returns the number of TCHAR_T units needed as temporary space for the result
+ of sprintf or SNPRINTF of a single conversion directive. */
+static size_t
+MAX_ROOM_NEEDED (const arguments *ap, size_t arg_index, FCHAR_T conversion,
+ arg_type type, int flags, size_t width, int has_precision,
+ size_t precision, int pad_ourselves)
+{
+ size_t tmp_length;
+
+ switch (conversion)
+ {
+ case 'd': case 'i': case 'u':
+ if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
+ tmp_length =
+ (unsigned int) (sizeof (unsigned long long) * CHAR_BIT
+ * 0.30103 /* binary -> decimal */
+ )
+ + 1; /* turn floor into ceil */
+ else if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
+ tmp_length =
+ (unsigned int) (sizeof (unsigned long) * CHAR_BIT
+ * 0.30103 /* binary -> decimal */
+ )
+ + 1; /* turn floor into ceil */
+ else
+ tmp_length =
+ (unsigned int) (sizeof (unsigned int) * CHAR_BIT
+ * 0.30103 /* binary -> decimal */
+ )
+ + 1; /* turn floor into ceil */
+ if (tmp_length < precision)
+ tmp_length = precision;
+ /* Multiply by 2, as an estimate for FLAG_GROUP. */
+ tmp_length = xsum (tmp_length, tmp_length);
+ /* Add 1, to account for a leading sign. */
+ tmp_length = xsum (tmp_length, 1);
+ break;
+
+ case 'o':
+ if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
+ tmp_length =
+ (unsigned int) (sizeof (unsigned long long) * CHAR_BIT
+ * 0.333334 /* binary -> octal */
+ )
+ + 1; /* turn floor into ceil */
+ else if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
+ tmp_length =
+ (unsigned int) (sizeof (unsigned long) * CHAR_BIT
+ * 0.333334 /* binary -> octal */
+ )
+ + 1; /* turn floor into ceil */
+ else
+ tmp_length =
+ (unsigned int) (sizeof (unsigned int) * CHAR_BIT
+ * 0.333334 /* binary -> octal */
+ )
+ + 1; /* turn floor into ceil */
+ if (tmp_length < precision)
+ tmp_length = precision;
+ /* Add 1, to account for a leading sign. */
+ tmp_length = xsum (tmp_length, 1);
+ break;
+
+ case 'x': case 'X':
+ if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
+ tmp_length =
+ (unsigned int) (sizeof (unsigned long long) * CHAR_BIT
+ * 0.25 /* binary -> hexadecimal */
+ )
+ + 1; /* turn floor into ceil */
+ else if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
+ tmp_length =
+ (unsigned int) (sizeof (unsigned long) * CHAR_BIT
+ * 0.25 /* binary -> hexadecimal */
+ )
+ + 1; /* turn floor into ceil */
+ else
+ tmp_length =
+ (unsigned int) (sizeof (unsigned int) * CHAR_BIT
+ * 0.25 /* binary -> hexadecimal */
+ )
+ + 1; /* turn floor into ceil */
+ if (tmp_length < precision)
+ tmp_length = precision;
+ /* Add 2, to account for a leading sign or alternate form. */
+ tmp_length = xsum (tmp_length, 2);
+ break;
+
+ case 'f': case 'F':
+ if (type == TYPE_LONGDOUBLE)
+ tmp_length =
+ (unsigned int) (LDBL_MAX_EXP
+ * 0.30103 /* binary -> decimal */
+ * 2 /* estimate for FLAG_GROUP */
+ )
+ + 1 /* turn floor into ceil */
+ + 10; /* sign, decimal point etc. */
+ else
+ tmp_length =
+ (unsigned int) (DBL_MAX_EXP
+ * 0.30103 /* binary -> decimal */
+ * 2 /* estimate for FLAG_GROUP */
+ )
+ + 1 /* turn floor into ceil */
+ + 10; /* sign, decimal point etc. */
+ tmp_length = xsum (tmp_length, precision);
+ break;
+
+ case 'e': case 'E': case 'g': case 'G':
+ tmp_length =
+ 12; /* sign, decimal point, exponent etc. */
+ tmp_length = xsum (tmp_length, precision);
+ break;
+
+ case 'a': case 'A':
+ if (type == TYPE_LONGDOUBLE)
+ tmp_length =
+ (unsigned int) (LDBL_DIG
+ * 0.831 /* decimal -> hexadecimal */
+ )
+ + 1; /* turn floor into ceil */
+ else
+ tmp_length =
+ (unsigned int) (DBL_DIG
+ * 0.831 /* decimal -> hexadecimal */
+ )
+ + 1; /* turn floor into ceil */
+ if (tmp_length < precision)
+ tmp_length = precision;
+ /* Account for sign, decimal point etc. */
+ tmp_length = xsum (tmp_length, 12);
+ break;
+
+ case 'c':
+# if HAVE_WINT_T && !WIDE_CHAR_VERSION
+ if (type == TYPE_WIDE_CHAR)
+ {
+ tmp_length = MB_CUR_MAX;
+# if ENABLE_WCHAR_FALLBACK
+ if (tmp_length < (sizeof (wchar_t) > 2 ? 10 : 6))
+ tmp_length = (sizeof (wchar_t) > 2 ? 10 : 6);
+# endif
+ }
+ else
+# endif
+ tmp_length = 1;
+ break;
+
+ case 's':
+# if HAVE_WCHAR_T
+ if (type == TYPE_WIDE_STRING)
+ {
+# if WIDE_CHAR_VERSION
+ /* ISO C says about %ls in fwprintf:
+ "If the precision is not specified or is greater than the size
+ of the array, the array shall contain a null wide character."
+ So if there is a precision, we must not use wcslen. */
+ const wchar_t *arg = ap->arg[arg_index].a.a_wide_string;
+
+ if (has_precision)
+ tmp_length = local_wcsnlen (arg, precision);
+ else
+ tmp_length = local_wcslen (arg);
+# else
+ /* ISO C says about %ls in fprintf:
+ "If a precision is specified, no more than that many bytes are
+ written (including shift sequences, if any), and the array
+ shall contain a null wide character if, to equal the multibyte
+ character sequence length given by the precision, the function
+ would need to access a wide character one past the end of the
+ array."
+ So if there is a precision, we must not use wcslen. */
+ /* This case has already been handled separately in VASNPRINTF. */
+ abort ();
+# endif
+ }
+ else
+# endif
+ {
+# if WIDE_CHAR_VERSION
+ /* ISO C says about %s in fwprintf:
+ "If the precision is not specified or is greater than the size
+ of the converted array, the converted array shall contain a
+ null wide character."
+ So if there is a precision, we must not use strlen. */
+ /* This case has already been handled separately in VASNPRINTF. */
+ abort ();
+# else
+ /* ISO C says about %s in fprintf:
+ "If the precision is not specified or greater than the size of
+ the array, the array shall contain a null character."
+ So if there is a precision, we must not use strlen. */
+ const char *arg = ap->arg[arg_index].a.a_string;
+
+ if (has_precision)
+ tmp_length = local_strnlen (arg, precision);
+ else
+ tmp_length = strlen (arg);
+# endif
+ }
+ break;
+
+ case 'p':
+ tmp_length =
+ (unsigned int) (sizeof (void *) * CHAR_BIT
+ * 0.25 /* binary -> hexadecimal */
+ )
+ + 1 /* turn floor into ceil */
+ + 2; /* account for leading 0x */
+ break;
+
+ default:
+ abort ();
+ }
+
+ if (!pad_ourselves)
+ {
+# if ENABLE_UNISTDIO
+ /* Padding considers the number of characters, therefore the number of
+ elements after padding may be
+ > max (tmp_length, width)
+ but is certainly
+ <= tmp_length + width. */
+ tmp_length = xsum (tmp_length, width);
+# else
+ /* Padding considers the number of elements, says POSIX. */
+ if (tmp_length < width)
+ tmp_length = width;
+# endif
+ }
+
+ tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */
+
+ return tmp_length;
+}
+
+#endif
+
+DCHAR_T *
+VASNPRINTF (DCHAR_T *resultbuf, size_t *lengthp,
+ const FCHAR_T *format, va_list args)
+{
+ DIRECTIVES d;
+ arguments a;
+
+ if (PRINTF_PARSE (format, &d, &a) < 0)
+ /* errno is already set. */
+ return NULL;
+
+ /* Frees the memory allocated by this function. Preserves errno. */
+#define CLEANUP() \
+ if (d.dir != d.direct_alloc_dir) \
+ free (d.dir); \
+ if (a.arg != a.direct_alloc_arg) \
+ free (a.arg);
+
+ if (PRINTF_FETCHARGS (args, &a) < 0)
+ goto fail_1_with_EINVAL;
+
+ {
+ size_t buf_neededlength;
+ TCHAR_T *buf;
+ TCHAR_T *buf_malloced;
+ const FCHAR_T *cp;
+ size_t i;
+ DIRECTIVE *dp;
+ /* Output string accumulator. */
+ DCHAR_T *result;
+ size_t allocated;
+ size_t length;
+
+ /* Allocate a small buffer that will hold a directive passed to
+ sprintf or snprintf. */
+ buf_neededlength =
+ xsum4 (7, d.max_width_length, d.max_precision_length, 6);
+#if HAVE_ALLOCA
+ if (buf_neededlength < 4000 / sizeof (TCHAR_T))
+ {
+ buf = (TCHAR_T *) alloca (buf_neededlength * sizeof (TCHAR_T));
+ buf_malloced = NULL;
+ }
+ else
+#endif
+ {
+ size_t buf_memsize = xtimes (buf_neededlength, sizeof (TCHAR_T));
+ if (size_overflow_p (buf_memsize))
+ goto out_of_memory_1;
+ buf = (TCHAR_T *) malloc (buf_memsize);
+ if (buf == NULL)
+ goto out_of_memory_1;
+ buf_malloced = buf;
+ }
+
+ result = resultbuf;
+ allocated = (resultbuf != NULL ? *lengthp : 0);
+ length = 0;
+ /* Invariants:
+ result is either == resultbuf or malloc-allocated.
+ If result == NULL, resultbuf is == NULL as well.
+ If length > 0, then result != NULL. */
+
+ /* Ensures that allocated >= needed. Aborts through a jump to
+ out_of_memory if needed is SIZE_MAX or otherwise too big. */
+#define ENSURE_ALLOCATION_ELSE(needed, oom_statement) \
+ if ((needed) > allocated) \
+ { \
+ size_t memory_size; \
+ DCHAR_T *memory; \
+ \
+ allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \
+ if ((needed) > allocated) \
+ allocated = (needed); \
+ memory_size = xtimes (allocated, sizeof (DCHAR_T)); \
+ if (size_overflow_p (memory_size)) \
+ oom_statement \
+ if (result == resultbuf) \
+ memory = (DCHAR_T *) malloc (memory_size); \
+ else \
+ memory = (DCHAR_T *) realloc (result, memory_size); \
+ if (memory == NULL) \
+ oom_statement \
+ if (result == resultbuf && length > 0) \
+ DCHAR_CPY (memory, result, length); \
+ result = memory; \
+ }
+#define ENSURE_ALLOCATION(needed) \
+ ENSURE_ALLOCATION_ELSE((needed), goto out_of_memory; )
+
+ for (cp = format, i = 0, dp = &d.dir[0]; ; cp = dp->dir_end, i++, dp++)
+ {
+ if (cp != dp->dir_start)
+ {
+ size_t n = dp->dir_start - cp;
+ size_t augmented_length = xsum (length, n);
+
+ ENSURE_ALLOCATION (augmented_length);
+ /* This copies a piece of FCHAR_T[] into a DCHAR_T[]. Here we
+ need that the format string contains only ASCII characters
+ if FCHAR_T and DCHAR_T are not the same type. */
+ if (sizeof (FCHAR_T) == sizeof (DCHAR_T))
+ {
+ DCHAR_CPY (result + length, (const DCHAR_T *) cp, n);
+ length = augmented_length;
+ }
+ else
+ {
+ do
+ result[length++] = *cp++;
+ while (--n > 0);
+ }
+ }
+ if (i == d.count)
+ break;
+
+ /* Execute a single directive. */
+ if (dp->conversion == '%')
+ {
+ size_t augmented_length;
+
+ if (!(dp->arg_index == ARG_NONE))
+ abort ();
+ augmented_length = xsum (length, 1);
+ ENSURE_ALLOCATION (augmented_length);
+ result[length] = '%';
+ length = augmented_length;
+ }
+ else
+ {
+ if (!(dp->arg_index != ARG_NONE))
+ abort ();
+
+ if (dp->conversion == 'n')
+ {
+ switch (a.arg[dp->arg_index].type)
+ {
+ case TYPE_COUNT_SCHAR_POINTER:
+ *a.arg[dp->arg_index].a.a_count_schar_pointer = length;
+ break;
+ case TYPE_COUNT_SHORT_POINTER:
+ *a.arg[dp->arg_index].a.a_count_short_pointer = length;
+ break;
+ case TYPE_COUNT_INT_POINTER:
+ *a.arg[dp->arg_index].a.a_count_int_pointer = length;
+ break;
+ case TYPE_COUNT_LONGINT_POINTER:
+ *a.arg[dp->arg_index].a.a_count_longint_pointer = length;
+ break;
+ case TYPE_COUNT_LONGLONGINT_POINTER:
+ *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length;
+ break;
+ default:
+ abort ();
+ }
+ }
+#if ENABLE_UNISTDIO
+ /* The unistdio extensions. */
+ else if (dp->conversion == 'U')
+ {
+ arg_type type = a.arg[dp->arg_index].type;
+ int flags = dp->flags;
+ int has_width;
+ size_t width;
+ int has_precision;
+ size_t precision;
+
+ has_width = 0;
+ width = 0;
+ if (dp->width_start != dp->width_end)
+ {
+ if (dp->width_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
+ if (arg < 0)
+ {
+ /* "A negative field width is taken as a '-' flag
+ followed by a positive field width." */
+ flags |= FLAG_LEFT;
+ width = -width;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->width_start;
+
+ do
+ width = xsum (xtimes (width, 10), *digitp++ - '0');
+ while (digitp != dp->width_end);
+ }
+ has_width = 1;
+ }
+
+ has_precision = 0;
+ precision = 0;
+ if (dp->precision_start != dp->precision_end)
+ {
+ if (dp->precision_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->precision_arg_index].a.a_int;
+ /* "A negative precision is taken as if the precision
+ were omitted." */
+ if (arg >= 0)
+ {
+ precision = arg;
+ has_precision = 1;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->precision_start + 1;
+
+ precision = 0;
+ while (digitp != dp->precision_end)
+ precision = xsum (xtimes (precision, 10), *digitp++ - '0');
+ has_precision = 1;
+ }
+ }
+
+ switch (type)
+ {
+ case TYPE_U8_STRING:
+ {
+ const uint8_t *arg = a.arg[dp->arg_index].a.a_u8_string;
+ const uint8_t *arg_end;
+ size_t characters;
+
+ if (has_precision)
+ {
+ /* Use only PRECISION characters, from the left. */
+ arg_end = arg;
+ characters = 0;
+ for (; precision > 0; precision--)
+ {
+ int count = u8_strmblen (arg_end);
+ if (count == 0)
+ break;
+ if (count < 0)
+ goto fail_with_EILSEQ;
+ arg_end += count;
+ characters++;
+ }
+ }
+ else if (has_width)
+ {
+ /* Use the entire string, and count the number of
+ characters. */
+ arg_end = arg;
+ characters = 0;
+ for (;;)
+ {
+ int count = u8_strmblen (arg_end);
+ if (count == 0)
+ break;
+ if (count < 0)
+ goto fail_with_EILSEQ;
+ arg_end += count;
+ characters++;
+ }
+ }
+ else
+ {
+ /* Use the entire string. */
+ arg_end = arg + u8_strlen (arg);
+ /* The number of characters doesn't matter. */
+ characters = 0;
+ }
+
+ if (characters < width && !(flags & FLAG_LEFT))
+ {
+ size_t n = width - characters;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+
+# if DCHAR_IS_UINT8_T
+ {
+ size_t n = arg_end - arg;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_CPY (result + length, arg, n);
+ length += n;
+ }
+# else
+ { /* Convert. */
+ DCHAR_T *converted = result + length;
+ size_t converted_len = allocated - length;
+# if DCHAR_IS_TCHAR
+ /* Convert from UTF-8 to locale encoding. */
+ converted =
+ u8_conv_to_encoding (locale_charset (),
+ iconveh_question_mark,
+ arg, arg_end - arg, NULL,
+ converted, &converted_len);
+# else
+ /* Convert from UTF-8 to UTF-16/UTF-32. */
+ converted =
+ U8_TO_DCHAR (arg, arg_end - arg,
+ converted, &converted_len);
+# endif
+ if (converted == NULL)
+ goto fail_with_errno;
+ if (converted != result + length)
+ {
+ ENSURE_ALLOCATION_ELSE (xsum (length, converted_len),
+ { free (converted); goto out_of_memory; });
+ DCHAR_CPY (result + length, converted, converted_len);
+ free (converted);
+ }
+ length += converted_len;
+ }
+# endif
+
+ if (characters < width && (flags & FLAG_LEFT))
+ {
+ size_t n = width - characters;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+ }
+ break;
+
+ case TYPE_U16_STRING:
+ {
+ const uint16_t *arg = a.arg[dp->arg_index].a.a_u16_string;
+ const uint16_t *arg_end;
+ size_t characters;
+
+ if (has_precision)
+ {
+ /* Use only PRECISION characters, from the left. */
+ arg_end = arg;
+ characters = 0;
+ for (; precision > 0; precision--)
+ {
+ int count = u16_strmblen (arg_end);
+ if (count == 0)
+ break;
+ if (count < 0)
+ goto fail_with_EILSEQ;
+ arg_end += count;
+ characters++;
+ }
+ }
+ else if (has_width)
+ {
+ /* Use the entire string, and count the number of
+ characters. */
+ arg_end = arg;
+ characters = 0;
+ for (;;)
+ {
+ int count = u16_strmblen (arg_end);
+ if (count == 0)
+ break;
+ if (count < 0)
+ goto fail_with_EILSEQ;
+ arg_end += count;
+ characters++;
+ }
+ }
+ else
+ {
+ /* Use the entire string. */
+ arg_end = arg + u16_strlen (arg);
+ /* The number of characters doesn't matter. */
+ characters = 0;
+ }
+
+ if (characters < width && !(flags & FLAG_LEFT))
+ {
+ size_t n = width - characters;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+
+# if DCHAR_IS_UINT16_T
+ {
+ size_t n = arg_end - arg;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_CPY (result + length, arg, n);
+ length += n;
+ }
+# else
+ { /* Convert. */
+ DCHAR_T *converted = result + length;
+ size_t converted_len = allocated - length;
+# if DCHAR_IS_TCHAR
+ /* Convert from UTF-16 to locale encoding. */
+ converted =
+ u16_conv_to_encoding (locale_charset (),
+ iconveh_question_mark,
+ arg, arg_end - arg, NULL,
+ converted, &converted_len);
+# else
+ /* Convert from UTF-16 to UTF-8/UTF-32. */
+ converted =
+ U16_TO_DCHAR (arg, arg_end - arg,
+ converted, &converted_len);
+# endif
+ if (converted == NULL)
+ goto fail_with_errno;
+ if (converted != result + length)
+ {
+ ENSURE_ALLOCATION_ELSE (xsum (length, converted_len),
+ { free (converted); goto out_of_memory; });
+ DCHAR_CPY (result + length, converted, converted_len);
+ free (converted);
+ }
+ length += converted_len;
+ }
+# endif
+
+ if (characters < width && (flags & FLAG_LEFT))
+ {
+ size_t n = width - characters;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+ }
+ break;
+
+ case TYPE_U32_STRING:
+ {
+ const uint32_t *arg = a.arg[dp->arg_index].a.a_u32_string;
+ const uint32_t *arg_end;
+ size_t characters;
+
+ if (has_precision)
+ {
+ /* Use only PRECISION characters, from the left. */
+ arg_end = arg;
+ characters = 0;
+ for (; precision > 0; precision--)
+ {
+ int count = u32_strmblen (arg_end);
+ if (count == 0)
+ break;
+ if (count < 0)
+ goto fail_with_EILSEQ;
+ arg_end += count;
+ characters++;
+ }
+ }
+ else if (has_width)
+ {
+ /* Use the entire string, and count the number of
+ characters. */
+ arg_end = arg;
+ characters = 0;
+ for (;;)
+ {
+ int count = u32_strmblen (arg_end);
+ if (count == 0)
+ break;
+ if (count < 0)
+ goto fail_with_EILSEQ;
+ arg_end += count;
+ characters++;
+ }
+ }
+ else
+ {
+ /* Use the entire string. */
+ arg_end = arg + u32_strlen (arg);
+ /* The number of characters doesn't matter. */
+ characters = 0;
+ }
+
+ if (characters < width && !(flags & FLAG_LEFT))
+ {
+ size_t n = width - characters;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+
+# if DCHAR_IS_UINT32_T
+ {
+ size_t n = arg_end - arg;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_CPY (result + length, arg, n);
+ length += n;
+ }
+# else
+ { /* Convert. */
+ DCHAR_T *converted = result + length;
+ size_t converted_len = allocated - length;
+# if DCHAR_IS_TCHAR
+ /* Convert from UTF-32 to locale encoding. */
+ converted =
+ u32_conv_to_encoding (locale_charset (),
+ iconveh_question_mark,
+ arg, arg_end - arg, NULL,
+ converted, &converted_len);
+# else
+ /* Convert from UTF-32 to UTF-8/UTF-16. */
+ converted =
+ U32_TO_DCHAR (arg, arg_end - arg,
+ converted, &converted_len);
+# endif
+ if (converted == NULL)
+ goto fail_with_errno;
+ if (converted != result + length)
+ {
+ ENSURE_ALLOCATION_ELSE (xsum (length, converted_len),
+ { free (converted); goto out_of_memory; });
+ DCHAR_CPY (result + length, converted, converted_len);
+ free (converted);
+ }
+ length += converted_len;
+ }
+# endif
+
+ if (characters < width && (flags & FLAG_LEFT))
+ {
+ size_t n = width - characters;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+ }
+ break;
+
+ default:
+ abort ();
+ }
+ }
+#endif
+#if (!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL) || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T
+ else if (dp->conversion == 's'
+# if WIDE_CHAR_VERSION
+ && a.arg[dp->arg_index].type != TYPE_WIDE_STRING
+# else
+ && a.arg[dp->arg_index].type == TYPE_WIDE_STRING
+# endif
+ )
+ {
+ /* The normal handling of the 's' directive below requires
+ allocating a temporary buffer. The determination of its
+ length (tmp_length), in the case when a precision is
+ specified, below requires a conversion between a char[]
+ string and a wchar_t[] wide string. It could be done, but
+ we have no guarantee that the implementation of sprintf will
+ use the exactly same algorithm. Without this guarantee, it
+ is possible to have buffer overrun bugs. In order to avoid
+ such bugs, we implement the entire processing of the 's'
+ directive ourselves. */
+ int flags = dp->flags;
+ int has_width;
+ size_t width;
+ int has_precision;
+ size_t precision;
+
+ has_width = 0;
+ width = 0;
+ if (dp->width_start != dp->width_end)
+ {
+ if (dp->width_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
+ if (arg < 0)
+ {
+ /* "A negative field width is taken as a '-' flag
+ followed by a positive field width." */
+ flags |= FLAG_LEFT;
+ width = -width;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->width_start;
+
+ do
+ width = xsum (xtimes (width, 10), *digitp++ - '0');
+ while (digitp != dp->width_end);
+ }
+ has_width = 1;
+ }
+
+ has_precision = 0;
+ precision = 6;
+ if (dp->precision_start != dp->precision_end)
+ {
+ if (dp->precision_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->precision_arg_index].a.a_int;
+ /* "A negative precision is taken as if the precision
+ were omitted." */
+ if (arg >= 0)
+ {
+ precision = arg;
+ has_precision = 1;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->precision_start + 1;
+
+ precision = 0;
+ while (digitp != dp->precision_end)
+ precision = xsum (xtimes (precision, 10), *digitp++ - '0');
+ has_precision = 1;
+ }
+ }
+
+# if WIDE_CHAR_VERSION
+ /* %s in vasnwprintf. See the specification of fwprintf. */
+ {
+ const char *arg = a.arg[dp->arg_index].a.a_string;
+ const char *arg_end;
+ size_t characters;
+
+ if (has_precision)
+ {
+ /* Use only as many bytes as needed to produce PRECISION
+ wide characters, from the left. */
+# if HAVE_MBRTOWC
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+ arg_end = arg;
+ characters = 0;
+ for (; precision > 0; precision--)
+ {
+ int count;
+# if HAVE_MBRTOWC
+ count = mbrlen (arg_end, MB_CUR_MAX, &state);
+# else
+ count = mblen (arg_end, MB_CUR_MAX);
+# endif
+ if (count == 0)
+ /* Found the terminating NUL. */
+ break;
+ if (count < 0)
+ /* Invalid or incomplete multibyte character. */
+ goto fail_with_EILSEQ;
+ arg_end += count;
+ characters++;
+ }
+ }
+ else if (has_width)
+ {
+ /* Use the entire string, and count the number of wide
+ characters. */
+# if HAVE_MBRTOWC
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+ arg_end = arg;
+ characters = 0;
+ for (;;)
+ {
+ int count;
+# if HAVE_MBRTOWC
+ count = mbrlen (arg_end, MB_CUR_MAX, &state);
+# else
+ count = mblen (arg_end, MB_CUR_MAX);
+# endif
+ if (count == 0)
+ /* Found the terminating NUL. */
+ break;
+ if (count < 0)
+ /* Invalid or incomplete multibyte character. */
+ goto fail_with_EILSEQ;
+ arg_end += count;
+ characters++;
+ }
+ }
+ else
+ {
+ /* Use the entire string. */
+ arg_end = arg + strlen (arg);
+ /* The number of characters doesn't matter. */
+ characters = 0;
+ }
+
+ if (characters < width && !(flags & FLAG_LEFT))
+ {
+ size_t n = width - characters;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+
+ if (has_precision || has_width)
+ {
+ /* We know the number of wide characters in advance. */
+ size_t remaining;
+# if HAVE_MBRTOWC
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+ ENSURE_ALLOCATION (xsum (length, characters));
+ for (remaining = characters; remaining > 0; remaining--)
+ {
+ wchar_t wc;
+ int count;
+# if HAVE_MBRTOWC
+ count = mbrtowc (&wc, arg, arg_end - arg, &state);
+# else
+ count = mbtowc (&wc, arg, arg_end - arg);
+# endif
+ if (count <= 0)
+ /* mbrtowc not consistent with mbrlen, or mbtowc
+ not consistent with mblen. */
+ abort ();
+ result[length++] = wc;
+ arg += count;
+ }
+ if (!(arg == arg_end))
+ abort ();
+ }
+ else
+ {
+# if HAVE_MBRTOWC
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+ while (arg < arg_end)
+ {
+ wchar_t wc;
+ int count;
+# if HAVE_MBRTOWC
+ count = mbrtowc (&wc, arg, arg_end - arg, &state);
+# else
+ count = mbtowc (&wc, arg, arg_end - arg);
+# endif
+ if (count <= 0)
+ /* mbrtowc not consistent with mbrlen, or mbtowc
+ not consistent with mblen. */
+ abort ();
+ ENSURE_ALLOCATION (xsum (length, 1));
+ result[length++] = wc;
+ arg += count;
+ }
+ }
+
+ if (characters < width && (flags & FLAG_LEFT))
+ {
+ size_t n = width - characters;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+ }
+# else
+ /* %ls in vasnprintf. See the specification of fprintf. */
+ {
+ const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string;
+ const wchar_t *arg_end;
+ size_t characters;
+# if !DCHAR_IS_TCHAR
+ /* This code assumes that TCHAR_T is 'char'. */
+ static_assert (sizeof (TCHAR_T) == 1);
+ TCHAR_T *tmpsrc;
+ DCHAR_T *tmpdst;
+ size_t tmpdst_len;
+# endif
+ size_t w;
+
+ if (has_precision)
+ {
+ /* Use only as many wide characters as needed to produce
+ at most PRECISION bytes, from the left. */
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+ arg_end = arg;
+ characters = 0;
+ while (precision > 0)
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
+
+ if (*arg_end == 0)
+ /* Found the terminating null wide character. */
+ break;
+ count = local_wcrtomb (cbuf, *arg_end, &state);
+ if (count < 0)
+ /* Cannot convert. */
+ goto fail_with_EILSEQ;
+ if (precision < (unsigned int) count)
+ break;
+ arg_end++;
+ characters += count;
+ precision -= count;
+ }
+ }
+# if DCHAR_IS_TCHAR
+ else if (has_width)
+# else
+ else
+# endif
+ {
+ /* Use the entire string, and count the number of
+ bytes. */
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+ arg_end = arg;
+ characters = 0;
+ for (;;)
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
+
+ if (*arg_end == 0)
+ /* Found the terminating null wide character. */
+ break;
+ count = local_wcrtomb (cbuf, *arg_end, &state);
+ if (count < 0)
+ /* Cannot convert. */
+ goto fail_with_EILSEQ;
+ arg_end++;
+ characters += count;
+ }
+ }
+# if DCHAR_IS_TCHAR
+ else
+ {
+ /* Use the entire string. */
+ arg_end = arg + local_wcslen (arg);
+ /* The number of bytes doesn't matter. */
+ characters = 0;
+ }
+# endif
+
+# if !DCHAR_IS_TCHAR
+ /* Convert the string into a piece of temporary memory. */
+ tmpsrc = (TCHAR_T *) malloc (characters * sizeof (TCHAR_T));
+ if (tmpsrc == NULL)
+ goto out_of_memory;
+ {
+ TCHAR_T *tmpptr = tmpsrc;
+ size_t remaining;
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+ for (remaining = characters; remaining > 0; )
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
+
+ if (*arg == 0)
+ abort ();
+ count = local_wcrtomb (cbuf, *arg, &state);
+ if (count <= 0)
+ /* Inconsistency. */
+ abort ();
+ memcpy (tmpptr, cbuf, count);
+ tmpptr += count;
+ arg++;
+ remaining -= count;
+ }
+ if (!(arg == arg_end))
+ abort ();
+ }
+
+ /* Convert from TCHAR_T[] to DCHAR_T[]. */
+ tmpdst =
+ DCHAR_CONV_FROM_ENCODING (locale_charset (),
+ iconveh_question_mark,
+ tmpsrc, characters,
+ NULL,
+ NULL, &tmpdst_len);
+ if (tmpdst == NULL)
+ {
+ free (tmpsrc);
+ goto fail_with_errno;
+ }
+ free (tmpsrc);
+# endif
+
+ if (has_width)
+ {
+# if ENABLE_UNISTDIO
+ /* Outside POSIX, it's preferable to compare the width
+ against the number of _characters_ of the converted
+ value. */
+ w = DCHAR_MBSNLEN (result + length, characters);
+# else
+ /* The width is compared against the number of _bytes_
+ of the converted value, says POSIX. */
+ w = characters;
+# endif
+ }
+ else
+ /* w doesn't matter. */
+ w = 0;
+
+ if (w < width && !(flags & FLAG_LEFT))
+ {
+ size_t n = width - w;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+
+# if DCHAR_IS_TCHAR
+ if (has_precision || has_width)
+ {
+ /* We know the number of bytes in advance. */
+ size_t remaining;
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+ ENSURE_ALLOCATION (xsum (length, characters));
+ for (remaining = characters; remaining > 0; )
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
+
+ if (*arg == 0)
+ abort ();
+ count = local_wcrtomb (cbuf, *arg, &state);
+ if (count <= 0)
+ /* Inconsistency. */
+ abort ();
+ memcpy (result + length, cbuf, count);
+ length += count;
+ arg++;
+ remaining -= count;
+ }
+ if (!(arg == arg_end))
+ abort ();
+ }
+ else
+ {
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+ while (arg < arg_end)
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
+
+ if (*arg == 0)
+ abort ();
+ count = local_wcrtomb (cbuf, *arg, &state);
+ if (count <= 0)
+ /* Cannot convert. */
+ goto fail_with_EILSEQ;
+ ENSURE_ALLOCATION (xsum (length, count));
+ memcpy (result + length, cbuf, count);
+ length += count;
+ arg++;
+ }
+ }
+# else
+ ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len),
+ { free (tmpdst); goto out_of_memory; });
+ DCHAR_CPY (result + length, tmpdst, tmpdst_len);
+ free (tmpdst);
+ length += tmpdst_len;
+# endif
+
+ if (w < width && (flags & FLAG_LEFT))
+ {
+ size_t n = width - w;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+ }
+# endif
+ }
+#endif
+#if ENABLE_WCHAR_FALLBACK && HAVE_WINT_T && !WIDE_CHAR_VERSION
+ else if (dp->conversion == 'c'
+ && a.arg[dp->arg_index].type == TYPE_WIDE_CHAR)
+ {
+ /* Implement the 'lc' directive ourselves, in order to provide
+ the fallback that avoids EILSEQ. */
+ int flags = dp->flags;
+ int has_width;
+ size_t width;
+
+ has_width = 0;
+ width = 0;
+ if (dp->width_start != dp->width_end)
+ {
+ if (dp->width_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
+ if (arg < 0)
+ {
+ /* "A negative field width is taken as a '-' flag
+ followed by a positive field width." */
+ flags |= FLAG_LEFT;
+ width = -width;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->width_start;
+
+ do
+ width = xsum (xtimes (width, 10), *digitp++ - '0');
+ while (digitp != dp->width_end);
+ }
+ has_width = 1;
+ }
+
+ /* %lc in vasnprintf. See the specification of fprintf. */
+ {
+ wchar_t arg = (wchar_t) a.arg[dp->arg_index].a.a_wide_char;
+ size_t characters;
+# if !DCHAR_IS_TCHAR
+ /* This code assumes that TCHAR_T is 'char'. */
+ static_assert (sizeof (TCHAR_T) == 1);
+ TCHAR_T tmpsrc[64]; /* Assume MB_CUR_MAX <= 64. */
+ DCHAR_T *tmpdst;
+ size_t tmpdst_len;
+# endif
+ size_t w;
+
+# if DCHAR_IS_TCHAR
+ if (has_width)
+# endif
+ {
+ /* Count the number of bytes. */
+ characters = 0;
+ if (arg != 0)
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+
+ count = local_wcrtomb (cbuf, arg, &state);
+ if (count < 0)
+ /* Inconsistency. */
+ abort ();
+ characters = count;
+ }
+ }
+# if DCHAR_IS_TCHAR
+ else
+ {
+ /* The number of bytes doesn't matter. */
+ characters = 0;
+ }
+# endif
+
+# if !DCHAR_IS_TCHAR
+ /* Convert the string into a piece of temporary memory. */
+ if (characters > 0) /* implies arg != 0 */
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+
+ count = local_wcrtomb (cbuf, arg, &state);
+ if (count <= 0)
+ /* Inconsistency. */
+ abort ();
+ memcpy (tmpsrc, cbuf, count);
+ }
+
+ /* Convert from TCHAR_T[] to DCHAR_T[]. */
+ tmpdst =
+ DCHAR_CONV_FROM_ENCODING (locale_charset (),
+ iconveh_question_mark,
+ tmpsrc, characters,
+ NULL,
+ NULL, &tmpdst_len);
+ if (tmpdst == NULL)
+ goto fail_with_errno;
+# endif
+
+ if (has_width)
+ {
+# if ENABLE_UNISTDIO
+ /* Outside POSIX, it's preferable to compare the width
+ against the number of _characters_ of the converted
+ value. */
+ w = DCHAR_MBSNLEN (result + length, characters);
+# else
+ /* The width is compared against the number of _bytes_
+ of the converted value, says POSIX. */
+ w = characters;
+# endif
+ }
+ else
+ /* w doesn't matter. */
+ w = 0;
+
+ if (w < width && !(flags & FLAG_LEFT))
+ {
+ size_t n = width - w;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+
+# if DCHAR_IS_TCHAR
+ if (has_width)
+ {
+ /* We know the number of bytes in advance. */
+ ENSURE_ALLOCATION (xsum (length, characters));
+ if (characters > 0) /* implies arg != 0 */
+ {
+ int count;
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+
+ count = local_wcrtomb (result + length, arg, &state);
+ if (count <= 0)
+ /* Inconsistency. */
+ abort ();
+ length += count;
+ }
+ }
+ else
+ {
+ if (arg != 0)
+ {
+ char cbuf[64]; /* Assume MB_CUR_MAX <= 64. */
+ int count;
+# if HAVE_WCRTOMB && !defined GNULIB_defined_mbstate_t
+ mbstate_t state;
+ memset (&state, '\0', sizeof (mbstate_t));
+# endif
+
+ count = local_wcrtomb (cbuf, arg, &state);
+ if (count <= 0)
+ /* Inconsistency. */
+ abort ();
+ ENSURE_ALLOCATION (xsum (length, count));
+ memcpy (result + length, cbuf, count);
+ length += count;
+ }
+ }
+# else
+ ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len),
+ { free (tmpdst); goto out_of_memory; });
+ DCHAR_CPY (result + length, tmpdst, tmpdst_len);
+ free (tmpdst);
+ length += tmpdst_len;
+# endif
+
+ if (w < width && (flags & FLAG_LEFT))
+ {
+ size_t n = width - w;
+ ENSURE_ALLOCATION (xsum (length, n));
+ DCHAR_SET (result + length, ' ', n);
+ length += n;
+ }
+ }
+ }
+#endif
+#if (NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE) && !defined IN_LIBINTL
+ else if ((dp->conversion == 'a' || dp->conversion == 'A')
+# if !(NEED_PRINTF_DIRECTIVE_A || (NEED_PRINTF_LONG_DOUBLE && NEED_PRINTF_DOUBLE))
+ && (0
+# if NEED_PRINTF_DOUBLE
+ || a.arg[dp->arg_index].type == TYPE_DOUBLE
+# endif
+# if NEED_PRINTF_LONG_DOUBLE
+ || a.arg[dp->arg_index].type == TYPE_LONGDOUBLE
+# endif
+ )
+# endif
+ )
+ {
+ arg_type type = a.arg[dp->arg_index].type;
+ int flags = dp->flags;
+ size_t width;
+ int has_precision;
+ size_t precision;
+ size_t tmp_length;
+ size_t count;
+ DCHAR_T tmpbuf[700];
+ DCHAR_T *tmp;
+ DCHAR_T *pad_ptr;
+ DCHAR_T *p;
+
+ width = 0;
+ if (dp->width_start != dp->width_end)
+ {
+ if (dp->width_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
+ if (arg < 0)
+ {
+ /* "A negative field width is taken as a '-' flag
+ followed by a positive field width." */
+ flags |= FLAG_LEFT;
+ width = -width;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->width_start;
+
+ do
+ width = xsum (xtimes (width, 10), *digitp++ - '0');
+ while (digitp != dp->width_end);
+ }
+ }
+
+ has_precision = 0;
+ precision = 0;
+ if (dp->precision_start != dp->precision_end)
+ {
+ if (dp->precision_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->precision_arg_index].a.a_int;
+ /* "A negative precision is taken as if the precision
+ were omitted." */
+ if (arg >= 0)
+ {
+ precision = arg;
+ has_precision = 1;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->precision_start + 1;
+
+ precision = 0;
+ while (digitp != dp->precision_end)
+ precision = xsum (xtimes (precision, 10), *digitp++ - '0');
+ has_precision = 1;
+ }
+ }
+
+ /* Allocate a temporary buffer of sufficient size. */
+ if (type == TYPE_LONGDOUBLE)
+ tmp_length =
+ (unsigned int) ((LDBL_DIG + 1)
+ * 0.831 /* decimal -> hexadecimal */
+ )
+ + 1; /* turn floor into ceil */
+ else
+ tmp_length =
+ (unsigned int) ((DBL_DIG + 1)
+ * 0.831 /* decimal -> hexadecimal */
+ )
+ + 1; /* turn floor into ceil */
+ if (tmp_length < precision)
+ tmp_length = precision;
+ /* Account for sign, decimal point etc. */
+ tmp_length = xsum (tmp_length, 12);
+
+ if (tmp_length < width)
+ tmp_length = width;
+
+ tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */
+
+ if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T))
+ tmp = tmpbuf;
+ else
+ {
+ size_t tmp_memsize = xtimes (tmp_length, sizeof (DCHAR_T));
+
+ if (size_overflow_p (tmp_memsize))
+ /* Overflow, would lead to out of memory. */
+ goto out_of_memory;
+ tmp = (DCHAR_T *) malloc (tmp_memsize);
+ if (tmp == NULL)
+ /* Out of memory. */
+ goto out_of_memory;
+ }
+
+ pad_ptr = NULL;
+ p = tmp;
+ if (type == TYPE_LONGDOUBLE)
+ {
+# if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_LONG_DOUBLE
+ long double arg = a.arg[dp->arg_index].a.a_longdouble;
+
+ if (isnanl (arg))
+ {
+ if (dp->conversion == 'A')
+ {
+ *p++ = 'N'; *p++ = 'A'; *p++ = 'N';
+ }
+ else
+ {
+ *p++ = 'n'; *p++ = 'a'; *p++ = 'n';
+ }
+ }
+ else
+ {
+ int sign = 0;
+ DECL_LONG_DOUBLE_ROUNDING
+
+ BEGIN_LONG_DOUBLE_ROUNDING ();
+
+ if (signbit (arg)) /* arg < 0.0L or negative zero */
+ {
+ sign = -1;
+ arg = -arg;
+ }
+
+ if (sign < 0)
+ *p++ = '-';
+ else if (flags & FLAG_SHOWSIGN)
+ *p++ = '+';
+ else if (flags & FLAG_SPACE)
+ *p++ = ' ';
+
+ if (arg > 0.0L && arg + arg == arg)
+ {
+ if (dp->conversion == 'A')
+ {
+ *p++ = 'I'; *p++ = 'N'; *p++ = 'F';
+ }
+ else
+ {
+ *p++ = 'i'; *p++ = 'n'; *p++ = 'f';
+ }
+ }
+ else
+ {
+ int exponent;
+ long double mantissa;
+
+ if (arg > 0.0L)
+ mantissa = printf_frexpl (arg, &exponent);
+ else
+ {
+ exponent = 0;
+ mantissa = 0.0L;
+ }
+
+ if (has_precision
+ && precision < (unsigned int) ((LDBL_DIG + 1) * 0.831) + 1)
+ {
+ /* Round the mantissa. */
+ long double tail = mantissa;
+ size_t q;
+
+ for (q = precision; ; q--)
+ {
+ int digit = (int) tail;
+ tail -= digit;
+ if (q == 0)
+ {
+ if (digit & 1 ? tail >= 0.5L : tail > 0.5L)
+ tail = 1 - tail;
+ else
+ tail = - tail;
+ break;
+ }
+ tail *= 16.0L;
+ }
+ if (tail != 0.0L)
+ for (q = precision; q > 0; q--)
+ tail *= 0.0625L;
+ mantissa += tail;
+ }
+
+ *p++ = '0';
+ *p++ = dp->conversion - 'A' + 'X';
+ pad_ptr = p;
+ {
+ int digit;
+
+ digit = (int) mantissa;
+ mantissa -= digit;
+ *p++ = '0' + digit;
+ if ((flags & FLAG_ALT)
+ || mantissa > 0.0L || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ /* This loop terminates because we assume
+ that FLT_RADIX is a power of 2. */
+ while (mantissa > 0.0L)
+ {
+ mantissa *= 16.0L;
+ digit = (int) mantissa;
+ mantissa -= digit;
+ *p++ = digit
+ + (digit < 10
+ ? '0'
+ : dp->conversion - 10);
+ if (precision > 0)
+ precision--;
+ }
+ while (precision > 0)
+ {
+ *p++ = '0';
+ precision--;
+ }
+ }
+ }
+ *p++ = dp->conversion - 'A' + 'P';
+# if WIDE_CHAR_VERSION
+ {
+ static const wchar_t decimal_format[] =
+ { '%', '+', 'd', '\0' };
+ SNPRINTF (p, 6 + 1, decimal_format, exponent);
+ }
+ while (*p != '\0')
+ p++;
+# else
+ if (sizeof (DCHAR_T) == 1)
+ {
+ sprintf ((char *) p, "%+d", exponent);
+ while (*p != '\0')
+ p++;
+ }
+ else
+ {
+ char expbuf[6 + 1];
+ const char *ep;
+ sprintf (expbuf, "%+d", exponent);
+ for (ep = expbuf; (*p = *ep) != '\0'; ep++)
+ p++;
+ }
+# endif
+ }
+
+ END_LONG_DOUBLE_ROUNDING ();
+ }
+# else
+ abort ();
+# endif
+ }
+ else
+ {
+# if NEED_PRINTF_DIRECTIVE_A || NEED_PRINTF_DOUBLE
+ double arg = a.arg[dp->arg_index].a.a_double;
+
+ if (isnand (arg))
+ {
+ if (dp->conversion == 'A')
+ {
+ *p++ = 'N'; *p++ = 'A'; *p++ = 'N';
+ }
+ else
+ {
+ *p++ = 'n'; *p++ = 'a'; *p++ = 'n';
+ }
+ }
+ else
+ {
+ int sign = 0;
+
+ if (signbit (arg)) /* arg < 0.0 or negative zero */
+ {
+ sign = -1;
+ arg = -arg;
+ }
+
+ if (sign < 0)
+ *p++ = '-';
+ else if (flags & FLAG_SHOWSIGN)
+ *p++ = '+';
+ else if (flags & FLAG_SPACE)
+ *p++ = ' ';
+
+ if (arg > 0.0 && arg + arg == arg)
+ {
+ if (dp->conversion == 'A')
+ {
+ *p++ = 'I'; *p++ = 'N'; *p++ = 'F';
+ }
+ else
+ {
+ *p++ = 'i'; *p++ = 'n'; *p++ = 'f';
+ }
+ }
+ else
+ {
+ int exponent;
+ double mantissa;
+
+ if (arg > 0.0)
+ mantissa = printf_frexp (arg, &exponent);
+ else
+ {
+ exponent = 0;
+ mantissa = 0.0;
+ }
+
+ if (has_precision
+ && precision < (unsigned int) ((DBL_DIG + 1) * 0.831) + 1)
+ {
+ /* Round the mantissa. */
+ double tail = mantissa;
+ size_t q;
+
+ for (q = precision; ; q--)
+ {
+ int digit = (int) tail;
+ tail -= digit;
+ if (q == 0)
+ {
+ if (digit & 1 ? tail >= 0.5 : tail > 0.5)
+ tail = 1 - tail;
+ else
+ tail = - tail;
+ break;
+ }
+ tail *= 16.0;
+ }
+ if (tail != 0.0)
+ for (q = precision; q > 0; q--)
+ tail *= 0.0625;
+ mantissa += tail;
+ }
+
+ *p++ = '0';
+ *p++ = dp->conversion - 'A' + 'X';
+ pad_ptr = p;
+ {
+ int digit;
+
+ digit = (int) mantissa;
+ mantissa -= digit;
+ *p++ = '0' + digit;
+ if ((flags & FLAG_ALT)
+ || mantissa > 0.0 || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ /* This loop terminates because we assume
+ that FLT_RADIX is a power of 2. */
+ while (mantissa > 0.0)
+ {
+ mantissa *= 16.0;
+ digit = (int) mantissa;
+ mantissa -= digit;
+ *p++ = digit
+ + (digit < 10
+ ? '0'
+ : dp->conversion - 10);
+ if (precision > 0)
+ precision--;
+ }
+ while (precision > 0)
+ {
+ *p++ = '0';
+ precision--;
+ }
+ }
+ }
+ *p++ = dp->conversion - 'A' + 'P';
+# if WIDE_CHAR_VERSION
+ {
+ static const wchar_t decimal_format[] =
+ { '%', '+', 'd', '\0' };
+ SNPRINTF (p, 6 + 1, decimal_format, exponent);
+ }
+ while (*p != '\0')
+ p++;
+# else
+ if (sizeof (DCHAR_T) == 1)
+ {
+ sprintf ((char *) p, "%+d", exponent);
+ while (*p != '\0')
+ p++;
+ }
+ else
+ {
+ char expbuf[6 + 1];
+ const char *ep;
+ sprintf (expbuf, "%+d", exponent);
+ for (ep = expbuf; (*p = *ep) != '\0'; ep++)
+ p++;
+ }
+# endif
+ }
+ }
+# else
+ abort ();
+# endif
+ }
+
+ /* The generated string now extends from tmp to p, with the
+ zero padding insertion point being at pad_ptr. */
+ count = p - tmp;
+
+ if (count < width)
+ {
+ size_t pad = width - count;
+ DCHAR_T *end = p + pad;
+
+ if (flags & FLAG_LEFT)
+ {
+ /* Pad with spaces on the right. */
+ for (; pad > 0; pad--)
+ *p++ = ' ';
+ }
+ else if ((flags & FLAG_ZERO) && pad_ptr != NULL)
+ {
+ /* Pad with zeroes. */
+ DCHAR_T *q = end;
+
+ while (p > pad_ptr)
+ *--q = *--p;
+ for (; pad > 0; pad--)
+ *p++ = '0';
+ }
+ else
+ {
+ /* Pad with spaces on the left. */
+ DCHAR_T *q = end;
+
+ while (p > tmp)
+ *--q = *--p;
+ for (; pad > 0; pad--)
+ *p++ = ' ';
+ }
+
+ p = end;
+ }
+
+ count = p - tmp;
+
+ if (count >= tmp_length)
+ /* tmp_length was incorrectly calculated - fix the
+ code above! */
+ abort ();
+
+ /* Make room for the result. */
+ if (count >= allocated - length)
+ {
+ size_t n = xsum (length, count);
+
+ ENSURE_ALLOCATION (n);
+ }
+
+ /* Append the result. */
+ memcpy (result + length, tmp, count * sizeof (DCHAR_T));
+ if (tmp != tmpbuf)
+ free (tmp);
+ length += count;
+ }
+#endif
+#if (NEED_PRINTF_INFINITE_DOUBLE || NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE || NEED_PRINTF_LONG_DOUBLE) && !defined IN_LIBINTL
+ else if ((dp->conversion == 'f' || dp->conversion == 'F'
+ || dp->conversion == 'e' || dp->conversion == 'E'
+ || dp->conversion == 'g' || dp->conversion == 'G'
+ || dp->conversion == 'a' || dp->conversion == 'A')
+ && (0
+# if NEED_PRINTF_DOUBLE
+ || a.arg[dp->arg_index].type == TYPE_DOUBLE
+# elif NEED_PRINTF_INFINITE_DOUBLE
+ || (a.arg[dp->arg_index].type == TYPE_DOUBLE
+ /* The systems (mingw) which produce wrong output
+ for Inf, -Inf, and NaN also do so for -0.0.
+ Therefore we treat this case here as well. */
+ && is_infinite_or_zero (a.arg[dp->arg_index].a.a_double))
+# endif
+# if NEED_PRINTF_LONG_DOUBLE
+ || a.arg[dp->arg_index].type == TYPE_LONGDOUBLE
+# elif NEED_PRINTF_INFINITE_LONG_DOUBLE
+ || (a.arg[dp->arg_index].type == TYPE_LONGDOUBLE
+ /* Some systems produce wrong output for Inf,
+ -Inf, and NaN. Some systems in this category
+ (IRIX 5.3) also do so for -0.0. Therefore we
+ treat this case here as well. */
+ && is_infinite_or_zerol (a.arg[dp->arg_index].a.a_longdouble))
+# endif
+ ))
+ {
+# if (NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE) && (NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE)
+ arg_type type = a.arg[dp->arg_index].type;
+# endif
+ int flags = dp->flags;
+ size_t width;
+ size_t count;
+ int has_precision;
+ size_t precision;
+ size_t tmp_length;
+ DCHAR_T tmpbuf[700];
+ DCHAR_T *tmp;
+ DCHAR_T *pad_ptr;
+ DCHAR_T *p;
+
+ width = 0;
+ if (dp->width_start != dp->width_end)
+ {
+ if (dp->width_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
+ if (arg < 0)
+ {
+ /* "A negative field width is taken as a '-' flag
+ followed by a positive field width." */
+ flags |= FLAG_LEFT;
+ width = -width;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->width_start;
+
+ do
+ width = xsum (xtimes (width, 10), *digitp++ - '0');
+ while (digitp != dp->width_end);
+ }
+ }
+
+ has_precision = 0;
+ precision = 0;
+ if (dp->precision_start != dp->precision_end)
+ {
+ if (dp->precision_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->precision_arg_index].a.a_int;
+ /* "A negative precision is taken as if the precision
+ were omitted." */
+ if (arg >= 0)
+ {
+ precision = arg;
+ has_precision = 1;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->precision_start + 1;
+
+ precision = 0;
+ while (digitp != dp->precision_end)
+ precision = xsum (xtimes (precision, 10), *digitp++ - '0');
+ has_precision = 1;
+ }
+ }
+
+ /* POSIX specifies the default precision to be 6 for %f, %F,
+ %e, %E, but not for %g, %G. Implementations appear to use
+ the same default precision also for %g, %G. But for %a, %A,
+ the default precision is 0. */
+ if (!has_precision)
+ if (!(dp->conversion == 'a' || dp->conversion == 'A'))
+ precision = 6;
+
+ /* Allocate a temporary buffer of sufficient size. */
+# if NEED_PRINTF_DOUBLE && NEED_PRINTF_LONG_DOUBLE
+ tmp_length = (type == TYPE_LONGDOUBLE ? LDBL_DIG + 1 : DBL_DIG + 1);
+# elif NEED_PRINTF_INFINITE_DOUBLE && NEED_PRINTF_LONG_DOUBLE
+ tmp_length = (type == TYPE_LONGDOUBLE ? LDBL_DIG + 1 : 0);
+# elif NEED_PRINTF_LONG_DOUBLE
+ tmp_length = LDBL_DIG + 1;
+# elif NEED_PRINTF_DOUBLE
+ tmp_length = DBL_DIG + 1;
+# else
+ tmp_length = 0;
+# endif
+ if (tmp_length < precision)
+ tmp_length = precision;
+# if NEED_PRINTF_LONG_DOUBLE
+# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE
+ if (type == TYPE_LONGDOUBLE)
+# endif
+ if (dp->conversion == 'f' || dp->conversion == 'F')
+ {
+ long double arg = a.arg[dp->arg_index].a.a_longdouble;
+ if (!(isnanl (arg) || arg + arg == arg))
+ {
+ /* arg is finite and nonzero. */
+ int exponent = floorlog10l (arg < 0 ? -arg : arg);
+ if (exponent >= 0 && tmp_length < exponent + precision)
+ tmp_length = exponent + precision;
+ }
+ }
+# endif
+# if NEED_PRINTF_DOUBLE
+# if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE
+ if (type == TYPE_DOUBLE)
+# endif
+ if (dp->conversion == 'f' || dp->conversion == 'F')
+ {
+ double arg = a.arg[dp->arg_index].a.a_double;
+ if (!(isnand (arg) || arg + arg == arg))
+ {
+ /* arg is finite and nonzero. */
+ int exponent = floorlog10 (arg < 0 ? -arg : arg);
+ if (exponent >= 0 && tmp_length < exponent + precision)
+ tmp_length = exponent + precision;
+ }
+ }
+# endif
+ /* Account for sign, decimal point etc. */
+ tmp_length = xsum (tmp_length, 12);
+
+ if (tmp_length < width)
+ tmp_length = width;
+
+ tmp_length = xsum (tmp_length, 1); /* account for trailing NUL */
+
+ if (tmp_length <= sizeof (tmpbuf) / sizeof (DCHAR_T))
+ tmp = tmpbuf;
+ else
+ {
+ size_t tmp_memsize = xtimes (tmp_length, sizeof (DCHAR_T));
+
+ if (size_overflow_p (tmp_memsize))
+ /* Overflow, would lead to out of memory. */
+ goto out_of_memory;
+ tmp = (DCHAR_T *) malloc (tmp_memsize);
+ if (tmp == NULL)
+ /* Out of memory. */
+ goto out_of_memory;
+ }
+
+ pad_ptr = NULL;
+ p = tmp;
+
+# if NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_INFINITE_LONG_DOUBLE
+# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE
+ if (type == TYPE_LONGDOUBLE)
+# endif
+ {
+ long double arg = a.arg[dp->arg_index].a.a_longdouble;
+
+ if (isnanl (arg))
+ {
+ if (dp->conversion >= 'A' && dp->conversion <= 'Z')
+ {
+ *p++ = 'N'; *p++ = 'A'; *p++ = 'N';
+ }
+ else
+ {
+ *p++ = 'n'; *p++ = 'a'; *p++ = 'n';
+ }
+ }
+ else
+ {
+ int sign = 0;
+ DECL_LONG_DOUBLE_ROUNDING
+
+ BEGIN_LONG_DOUBLE_ROUNDING ();
+
+ if (signbit (arg)) /* arg < 0.0L or negative zero */
+ {
+ sign = -1;
+ arg = -arg;
+ }
+
+ if (sign < 0)
+ *p++ = '-';
+ else if (flags & FLAG_SHOWSIGN)
+ *p++ = '+';
+ else if (flags & FLAG_SPACE)
+ *p++ = ' ';
+
+ if (arg > 0.0L && arg + arg == arg)
+ {
+ if (dp->conversion >= 'A' && dp->conversion <= 'Z')
+ {
+ *p++ = 'I'; *p++ = 'N'; *p++ = 'F';
+ }
+ else
+ {
+ *p++ = 'i'; *p++ = 'n'; *p++ = 'f';
+ }
+ }
+ else
+ {
+# if NEED_PRINTF_LONG_DOUBLE
+ pad_ptr = p;
+
+ if (dp->conversion == 'f' || dp->conversion == 'F')
+ {
+ char *digits;
+ size_t ndigits;
+
+ digits =
+ scale10_round_decimal_long_double (arg, precision);
+ if (digits == NULL)
+ {
+ END_LONG_DOUBLE_ROUNDING ();
+ goto out_of_memory;
+ }
+ ndigits = strlen (digits);
+
+ if (ndigits > precision)
+ do
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ while (ndigits > precision);
+ else
+ *p++ = '0';
+ /* Here ndigits <= precision. */
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ for (; precision > ndigits; precision--)
+ *p++ = '0';
+ while (ndigits > 0)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+
+ free (digits);
+ }
+ else if (dp->conversion == 'e' || dp->conversion == 'E')
+ {
+ int exponent;
+
+ if (arg == 0.0L)
+ {
+ exponent = 0;
+ *p++ = '0';
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ for (; precision > 0; precision--)
+ *p++ = '0';
+ }
+ }
+ else
+ {
+ /* arg > 0.0L. */
+ int adjusted;
+ char *digits;
+ size_t ndigits;
+
+ exponent = floorlog10l (arg);
+ adjusted = 0;
+ for (;;)
+ {
+ digits =
+ scale10_round_decimal_long_double (arg,
+ (int)precision - exponent);
+ if (digits == NULL)
+ {
+ END_LONG_DOUBLE_ROUNDING ();
+ goto out_of_memory;
+ }
+ ndigits = strlen (digits);
+
+ if (ndigits == precision + 1)
+ break;
+ if (ndigits < precision
+ || ndigits > precision + 2)
+ /* The exponent was not guessed
+ precisely enough. */
+ abort ();
+ if (adjusted)
+ /* None of two values of exponent is
+ the right one. Prevent an endless
+ loop. */
+ abort ();
+ free (digits);
+ if (ndigits == precision)
+ exponent -= 1;
+ else
+ exponent += 1;
+ adjusted = 1;
+ }
+ /* Here ndigits = precision+1. */
+ if (is_borderline (digits, precision))
+ {
+ /* Maybe the exponent guess was too high
+ and a smaller exponent can be reached
+ by turning a 10...0 into 9...9x. */
+ char *digits2 =
+ scale10_round_decimal_long_double (arg,
+ (int)precision - exponent + 1);
+ if (digits2 == NULL)
+ {
+ free (digits);
+ END_LONG_DOUBLE_ROUNDING ();
+ goto out_of_memory;
+ }
+ if (strlen (digits2) == precision + 1)
+ {
+ free (digits);
+ digits = digits2;
+ exponent -= 1;
+ }
+ else
+ free (digits2);
+ }
+ /* Here ndigits = precision+1. */
+
+ *p++ = digits[--ndigits];
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ while (ndigits > 0)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+
+ free (digits);
+ }
+
+ *p++ = dp->conversion; /* 'e' or 'E' */
+# if WIDE_CHAR_VERSION
+ {
+ static const wchar_t decimal_format[] =
+ { '%', '+', '.', '2', 'd', '\0' };
+ SNPRINTF (p, 6 + 1, decimal_format, exponent);
+ }
+ while (*p != '\0')
+ p++;
+# else
+ if (sizeof (DCHAR_T) == 1)
+ {
+ sprintf ((char *) p, "%+.2d", exponent);
+ while (*p != '\0')
+ p++;
+ }
+ else
+ {
+ char expbuf[6 + 1];
+ const char *ep;
+ sprintf (expbuf, "%+.2d", exponent);
+ for (ep = expbuf; (*p = *ep) != '\0'; ep++)
+ p++;
+ }
+# endif
+ }
+ else if (dp->conversion == 'g' || dp->conversion == 'G')
+ {
+ if (precision == 0)
+ precision = 1;
+ /* precision >= 1. */
+
+ if (arg == 0.0L)
+ /* The exponent is 0, >= -4, < precision.
+ Use fixed-point notation. */
+ {
+ size_t ndigits = precision;
+ /* Number of trailing zeroes that have to be
+ dropped. */
+ size_t nzeroes =
+ (flags & FLAG_ALT ? 0 : precision - 1);
+
+ --ndigits;
+ *p++ = '0';
+ if ((flags & FLAG_ALT) || ndigits > nzeroes)
+ {
+ *p++ = decimal_point_char ();
+ while (ndigits > nzeroes)
+ {
+ --ndigits;
+ *p++ = '0';
+ }
+ }
+ }
+ else
+ {
+ /* arg > 0.0L. */
+ int exponent;
+ int adjusted;
+ char *digits;
+ size_t ndigits;
+ size_t nzeroes;
+
+ exponent = floorlog10l (arg);
+ adjusted = 0;
+ for (;;)
+ {
+ digits =
+ scale10_round_decimal_long_double (arg,
+ (int)(precision - 1) - exponent);
+ if (digits == NULL)
+ {
+ END_LONG_DOUBLE_ROUNDING ();
+ goto out_of_memory;
+ }
+ ndigits = strlen (digits);
+
+ if (ndigits == precision)
+ break;
+ if (ndigits < precision - 1
+ || ndigits > precision + 1)
+ /* The exponent was not guessed
+ precisely enough. */
+ abort ();
+ if (adjusted)
+ /* None of two values of exponent is
+ the right one. Prevent an endless
+ loop. */
+ abort ();
+ free (digits);
+ if (ndigits < precision)
+ exponent -= 1;
+ else
+ exponent += 1;
+ adjusted = 1;
+ }
+ /* Here ndigits = precision. */
+ if (is_borderline (digits, precision - 1))
+ {
+ /* Maybe the exponent guess was too high
+ and a smaller exponent can be reached
+ by turning a 10...0 into 9...9x. */
+ char *digits2 =
+ scale10_round_decimal_long_double (arg,
+ (int)(precision - 1) - exponent + 1);
+ if (digits2 == NULL)
+ {
+ free (digits);
+ END_LONG_DOUBLE_ROUNDING ();
+ goto out_of_memory;
+ }
+ if (strlen (digits2) == precision)
+ {
+ free (digits);
+ digits = digits2;
+ exponent -= 1;
+ }
+ else
+ free (digits2);
+ }
+ /* Here ndigits = precision. */
+
+ /* Determine the number of trailing zeroes
+ that have to be dropped. */
+ nzeroes = 0;
+ if ((flags & FLAG_ALT) == 0)
+ while (nzeroes < ndigits
+ && digits[nzeroes] == '0')
+ nzeroes++;
+
+ /* The exponent is now determined. */
+ if (exponent >= -4
+ && exponent < (long)precision)
+ {
+ /* Fixed-point notation:
+ max(exponent,0)+1 digits, then the
+ decimal point, then the remaining
+ digits without trailing zeroes. */
+ if (exponent >= 0)
+ {
+ size_t ecount = exponent + 1;
+ /* Note: count <= precision = ndigits. */
+ for (; ecount > 0; ecount--)
+ *p++ = digits[--ndigits];
+ if ((flags & FLAG_ALT) || ndigits > nzeroes)
+ {
+ *p++ = decimal_point_char ();
+ while (ndigits > nzeroes)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+ }
+ else
+ {
+ size_t ecount = -exponent - 1;
+ *p++ = '0';
+ *p++ = decimal_point_char ();
+ for (; ecount > 0; ecount--)
+ *p++ = '0';
+ while (ndigits > nzeroes)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+ }
+ else
+ {
+ /* Exponential notation. */
+ *p++ = digits[--ndigits];
+ if ((flags & FLAG_ALT) || ndigits > nzeroes)
+ {
+ *p++ = decimal_point_char ();
+ while (ndigits > nzeroes)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+ *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */
+# if WIDE_CHAR_VERSION
+ {
+ static const wchar_t decimal_format[] =
+ { '%', '+', '.', '2', 'd', '\0' };
+ SNPRINTF (p, 6 + 1, decimal_format, exponent);
+ }
+ while (*p != '\0')
+ p++;
+# else
+ if (sizeof (DCHAR_T) == 1)
+ {
+ sprintf ((char *) p, "%+.2d", exponent);
+ while (*p != '\0')
+ p++;
+ }
+ else
+ {
+ char expbuf[6 + 1];
+ const char *ep;
+ sprintf (expbuf, "%+.2d", exponent);
+ for (ep = expbuf; (*p = *ep) != '\0'; ep++)
+ p++;
+ }
+# endif
+ }
+
+ free (digits);
+ }
+ }
+ else
+ abort ();
+# else
+ /* arg is finite. */
+ if (!(arg == 0.0L))
+ abort ();
+
+ pad_ptr = p;
+
+ if (dp->conversion == 'f' || dp->conversion == 'F')
+ {
+ *p++ = '0';
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ for (; precision > 0; precision--)
+ *p++ = '0';
+ }
+ }
+ else if (dp->conversion == 'e' || dp->conversion == 'E')
+ {
+ *p++ = '0';
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ for (; precision > 0; precision--)
+ *p++ = '0';
+ }
+ *p++ = dp->conversion; /* 'e' or 'E' */
+ *p++ = '+';
+ *p++ = '0';
+ *p++ = '0';
+ }
+ else if (dp->conversion == 'g' || dp->conversion == 'G')
+ {
+ *p++ = '0';
+ if (flags & FLAG_ALT)
+ {
+ size_t ndigits =
+ (precision > 0 ? precision - 1 : 0);
+ *p++ = decimal_point_char ();
+ for (; ndigits > 0; --ndigits)
+ *p++ = '0';
+ }
+ }
+ else if (dp->conversion == 'a' || dp->conversion == 'A')
+ {
+ *p++ = '0';
+ *p++ = dp->conversion - 'A' + 'X';
+ pad_ptr = p;
+ *p++ = '0';
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ for (; precision > 0; precision--)
+ *p++ = '0';
+ }
+ *p++ = dp->conversion - 'A' + 'P';
+ *p++ = '+';
+ *p++ = '0';
+ }
+ else
+ abort ();
+# endif
+ }
+
+ END_LONG_DOUBLE_ROUNDING ();
+ }
+ }
+# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE
+ else
+# endif
+# endif
+# if NEED_PRINTF_DOUBLE || NEED_PRINTF_INFINITE_DOUBLE
+ {
+ double arg = a.arg[dp->arg_index].a.a_double;
+
+ if (isnand (arg))
+ {
+ if (dp->conversion >= 'A' && dp->conversion <= 'Z')
+ {
+ *p++ = 'N'; *p++ = 'A'; *p++ = 'N';
+ }
+ else
+ {
+ *p++ = 'n'; *p++ = 'a'; *p++ = 'n';
+ }
+ }
+ else
+ {
+ int sign = 0;
+
+ if (signbit (arg)) /* arg < 0.0 or negative zero */
+ {
+ sign = -1;
+ arg = -arg;
+ }
+
+ if (sign < 0)
+ *p++ = '-';
+ else if (flags & FLAG_SHOWSIGN)
+ *p++ = '+';
+ else if (flags & FLAG_SPACE)
+ *p++ = ' ';
+
+ if (arg > 0.0 && arg + arg == arg)
+ {
+ if (dp->conversion >= 'A' && dp->conversion <= 'Z')
+ {
+ *p++ = 'I'; *p++ = 'N'; *p++ = 'F';
+ }
+ else
+ {
+ *p++ = 'i'; *p++ = 'n'; *p++ = 'f';
+ }
+ }
+ else
+ {
+# if NEED_PRINTF_DOUBLE
+ pad_ptr = p;
+
+ if (dp->conversion == 'f' || dp->conversion == 'F')
+ {
+ char *digits;
+ size_t ndigits;
+
+ digits =
+ scale10_round_decimal_double (arg, precision);
+ if (digits == NULL)
+ goto out_of_memory;
+ ndigits = strlen (digits);
+
+ if (ndigits > precision)
+ do
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ while (ndigits > precision);
+ else
+ *p++ = '0';
+ /* Here ndigits <= precision. */
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ for (; precision > ndigits; precision--)
+ *p++ = '0';
+ while (ndigits > 0)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+
+ free (digits);
+ }
+ else if (dp->conversion == 'e' || dp->conversion == 'E')
+ {
+ int exponent;
+
+ if (arg == 0.0)
+ {
+ exponent = 0;
+ *p++ = '0';
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ for (; precision > 0; precision--)
+ *p++ = '0';
+ }
+ }
+ else
+ {
+ /* arg > 0.0. */
+ int adjusted;
+ char *digits;
+ size_t ndigits;
+
+ exponent = floorlog10 (arg);
+ adjusted = 0;
+ for (;;)
+ {
+ digits =
+ scale10_round_decimal_double (arg,
+ (int)precision - exponent);
+ if (digits == NULL)
+ goto out_of_memory;
+ ndigits = strlen (digits);
+
+ if (ndigits == precision + 1)
+ break;
+ if (ndigits < precision
+ || ndigits > precision + 2)
+ /* The exponent was not guessed
+ precisely enough. */
+ abort ();
+ if (adjusted)
+ /* None of two values of exponent is
+ the right one. Prevent an endless
+ loop. */
+ abort ();
+ free (digits);
+ if (ndigits == precision)
+ exponent -= 1;
+ else
+ exponent += 1;
+ adjusted = 1;
+ }
+ /* Here ndigits = precision+1. */
+ if (is_borderline (digits, precision))
+ {
+ /* Maybe the exponent guess was too high
+ and a smaller exponent can be reached
+ by turning a 10...0 into 9...9x. */
+ char *digits2 =
+ scale10_round_decimal_double (arg,
+ (int)precision - exponent + 1);
+ if (digits2 == NULL)
+ {
+ free (digits);
+ goto out_of_memory;
+ }
+ if (strlen (digits2) == precision + 1)
+ {
+ free (digits);
+ digits = digits2;
+ exponent -= 1;
+ }
+ else
+ free (digits2);
+ }
+ /* Here ndigits = precision+1. */
+
+ *p++ = digits[--ndigits];
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ while (ndigits > 0)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+
+ free (digits);
+ }
+
+ *p++ = dp->conversion; /* 'e' or 'E' */
+# if WIDE_CHAR_VERSION
+ {
+ static const wchar_t decimal_format[] =
+ /* Produce the same number of exponent digits
+ as the native printf implementation. */
+# if defined _WIN32 && ! defined __CYGWIN__
+ { '%', '+', '.', '3', 'd', '\0' };
+# else
+ { '%', '+', '.', '2', 'd', '\0' };
+# endif
+ SNPRINTF (p, 6 + 1, decimal_format, exponent);
+ }
+ while (*p != '\0')
+ p++;
+# else
+ {
+ static const char decimal_format[] =
+ /* Produce the same number of exponent digits
+ as the native printf implementation. */
+# if defined _WIN32 && ! defined __CYGWIN__
+ "%+.3d";
+# else
+ "%+.2d";
+# endif
+ if (sizeof (DCHAR_T) == 1)
+ {
+ sprintf ((char *) p, decimal_format, exponent);
+ while (*p != '\0')
+ p++;
+ }
+ else
+ {
+ char expbuf[6 + 1];
+ const char *ep;
+ sprintf (expbuf, decimal_format, exponent);
+ for (ep = expbuf; (*p = *ep) != '\0'; ep++)
+ p++;
+ }
+ }
+# endif
+ }
+ else if (dp->conversion == 'g' || dp->conversion == 'G')
+ {
+ if (precision == 0)
+ precision = 1;
+ /* precision >= 1. */
+
+ if (arg == 0.0)
+ /* The exponent is 0, >= -4, < precision.
+ Use fixed-point notation. */
+ {
+ size_t ndigits = precision;
+ /* Number of trailing zeroes that have to be
+ dropped. */
+ size_t nzeroes =
+ (flags & FLAG_ALT ? 0 : precision - 1);
+
+ --ndigits;
+ *p++ = '0';
+ if ((flags & FLAG_ALT) || ndigits > nzeroes)
+ {
+ *p++ = decimal_point_char ();
+ while (ndigits > nzeroes)
+ {
+ --ndigits;
+ *p++ = '0';
+ }
+ }
+ }
+ else
+ {
+ /* arg > 0.0. */
+ int exponent;
+ int adjusted;
+ char *digits;
+ size_t ndigits;
+ size_t nzeroes;
+
+ exponent = floorlog10 (arg);
+ adjusted = 0;
+ for (;;)
+ {
+ digits =
+ scale10_round_decimal_double (arg,
+ (int)(precision - 1) - exponent);
+ if (digits == NULL)
+ goto out_of_memory;
+ ndigits = strlen (digits);
+
+ if (ndigits == precision)
+ break;
+ if (ndigits < precision - 1
+ || ndigits > precision + 1)
+ /* The exponent was not guessed
+ precisely enough. */
+ abort ();
+ if (adjusted)
+ /* None of two values of exponent is
+ the right one. Prevent an endless
+ loop. */
+ abort ();
+ free (digits);
+ if (ndigits < precision)
+ exponent -= 1;
+ else
+ exponent += 1;
+ adjusted = 1;
+ }
+ /* Here ndigits = precision. */
+ if (is_borderline (digits, precision - 1))
+ {
+ /* Maybe the exponent guess was too high
+ and a smaller exponent can be reached
+ by turning a 10...0 into 9...9x. */
+ char *digits2 =
+ scale10_round_decimal_double (arg,
+ (int)(precision - 1) - exponent + 1);
+ if (digits2 == NULL)
+ {
+ free (digits);
+ goto out_of_memory;
+ }
+ if (strlen (digits2) == precision)
+ {
+ free (digits);
+ digits = digits2;
+ exponent -= 1;
+ }
+ else
+ free (digits2);
+ }
+ /* Here ndigits = precision. */
+
+ /* Determine the number of trailing zeroes
+ that have to be dropped. */
+ nzeroes = 0;
+ if ((flags & FLAG_ALT) == 0)
+ while (nzeroes < ndigits
+ && digits[nzeroes] == '0')
+ nzeroes++;
+
+ /* The exponent is now determined. */
+ if (exponent >= -4
+ && exponent < (long)precision)
+ {
+ /* Fixed-point notation:
+ max(exponent,0)+1 digits, then the
+ decimal point, then the remaining
+ digits without trailing zeroes. */
+ if (exponent >= 0)
+ {
+ size_t ecount = exponent + 1;
+ /* Note: ecount <= precision = ndigits. */
+ for (; ecount > 0; ecount--)
+ *p++ = digits[--ndigits];
+ if ((flags & FLAG_ALT) || ndigits > nzeroes)
+ {
+ *p++ = decimal_point_char ();
+ while (ndigits > nzeroes)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+ }
+ else
+ {
+ size_t ecount = -exponent - 1;
+ *p++ = '0';
+ *p++ = decimal_point_char ();
+ for (; ecount > 0; ecount--)
+ *p++ = '0';
+ while (ndigits > nzeroes)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+ }
+ else
+ {
+ /* Exponential notation. */
+ *p++ = digits[--ndigits];
+ if ((flags & FLAG_ALT) || ndigits > nzeroes)
+ {
+ *p++ = decimal_point_char ();
+ while (ndigits > nzeroes)
+ {
+ --ndigits;
+ *p++ = digits[ndigits];
+ }
+ }
+ *p++ = dp->conversion - 'G' + 'E'; /* 'e' or 'E' */
+# if WIDE_CHAR_VERSION
+ {
+ static const wchar_t decimal_format[] =
+ /* Produce the same number of exponent digits
+ as the native printf implementation. */
+# if defined _WIN32 && ! defined __CYGWIN__
+ { '%', '+', '.', '3', 'd', '\0' };
+# else
+ { '%', '+', '.', '2', 'd', '\0' };
+# endif
+ SNPRINTF (p, 6 + 1, decimal_format, exponent);
+ }
+ while (*p != '\0')
+ p++;
+# else
+ {
+ static const char decimal_format[] =
+ /* Produce the same number of exponent digits
+ as the native printf implementation. */
+# if defined _WIN32 && ! defined __CYGWIN__
+ "%+.3d";
+# else
+ "%+.2d";
+# endif
+ if (sizeof (DCHAR_T) == 1)
+ {
+ sprintf ((char *) p, decimal_format, exponent);
+ while (*p != '\0')
+ p++;
+ }
+ else
+ {
+ char expbuf[6 + 1];
+ const char *ep;
+ sprintf (expbuf, decimal_format, exponent);
+ for (ep = expbuf; (*p = *ep) != '\0'; ep++)
+ p++;
+ }
+ }
+# endif
+ }
+
+ free (digits);
+ }
+ }
+ else
+ abort ();
+# else
+ /* arg is finite. */
+ if (!(arg == 0.0))
+ abort ();
+
+ pad_ptr = p;
+
+ if (dp->conversion == 'f' || dp->conversion == 'F')
+ {
+ *p++ = '0';
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ for (; precision > 0; precision--)
+ *p++ = '0';
+ }
+ }
+ else if (dp->conversion == 'e' || dp->conversion == 'E')
+ {
+ *p++ = '0';
+ if ((flags & FLAG_ALT) || precision > 0)
+ {
+ *p++ = decimal_point_char ();
+ for (; precision > 0; precision--)
+ *p++ = '0';
+ }
+ *p++ = dp->conversion; /* 'e' or 'E' */
+ *p++ = '+';
+ /* Produce the same number of exponent digits as
+ the native printf implementation. */
+# if defined _WIN32 && ! defined __CYGWIN__
+ *p++ = '0';
+# endif
+ *p++ = '0';
+ *p++ = '0';
+ }
+ else if (dp->conversion == 'g' || dp->conversion == 'G')
+ {
+ *p++ = '0';
+ if (flags & FLAG_ALT)
+ {
+ size_t ndigits =
+ (precision > 0 ? precision - 1 : 0);
+ *p++ = decimal_point_char ();
+ for (; ndigits > 0; --ndigits)
+ *p++ = '0';
+ }
+ }
+ else
+ abort ();
+# endif
+ }
+ }
+ }
+# endif
+
+ /* The generated string now extends from tmp to p, with the
+ zero padding insertion point being at pad_ptr. */
+ count = p - tmp;
+
+ if (count < width)
+ {
+ size_t pad = width - count;
+ DCHAR_T *end = p + pad;
+
+ if (flags & FLAG_LEFT)
+ {
+ /* Pad with spaces on the right. */
+ for (; pad > 0; pad--)
+ *p++ = ' ';
+ }
+ else if ((flags & FLAG_ZERO) && pad_ptr != NULL)
+ {
+ /* Pad with zeroes. */
+ DCHAR_T *q = end;
+
+ while (p > pad_ptr)
+ *--q = *--p;
+ for (; pad > 0; pad--)
+ *p++ = '0';
+ }
+ else
+ {
+ /* Pad with spaces on the left. */
+ DCHAR_T *q = end;
+
+ while (p > tmp)
+ *--q = *--p;
+ for (; pad > 0; pad--)
+ *p++ = ' ';
+ }
+
+ p = end;
+ }
+
+ count = p - tmp;
+
+ if (count >= tmp_length)
+ /* tmp_length was incorrectly calculated - fix the
+ code above! */
+ abort ();
+
+ /* Make room for the result. */
+ if (count >= allocated - length)
+ {
+ size_t n = xsum (length, count);
+
+ ENSURE_ALLOCATION (n);
+ }
+
+ /* Append the result. */
+ memcpy (result + length, tmp, count * sizeof (DCHAR_T));
+ if (tmp != tmpbuf)
+ free (tmp);
+ length += count;
+ }
+#endif
+ else
+ {
+ arg_type type = a.arg[dp->arg_index].type;
+ int flags = dp->flags;
+#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
+ int has_width;
+#endif
+#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
+ size_t width;
+#endif
+#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_UNBOUNDED_PRECISION
+ int has_precision;
+ size_t precision;
+#endif
+#if NEED_PRINTF_UNBOUNDED_PRECISION
+ int prec_ourselves;
+#else
+# define prec_ourselves 0
+#endif
+#if NEED_PRINTF_FLAG_LEFTADJUST
+# define pad_ourselves 1
+#elif !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
+ int pad_ourselves;
+#else
+# define pad_ourselves 0
+#endif
+ TCHAR_T *fbp;
+ unsigned int prefix_count;
+ int prefixes[2] IF_LINT (= { 0 });
+ int orig_errno;
+#if !USE_SNPRINTF
+ size_t tmp_length;
+ TCHAR_T tmpbuf[700];
+ TCHAR_T *tmp;
+#endif
+
+#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
+ has_width = 0;
+#endif
+#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
+ width = 0;
+ if (dp->width_start != dp->width_end)
+ {
+ if (dp->width_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->width_arg_index].a.a_int;
+ width = arg;
+ if (arg < 0)
+ {
+ /* "A negative field width is taken as a '-' flag
+ followed by a positive field width." */
+ flags |= FLAG_LEFT;
+ width = -width;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->width_start;
+
+ do
+ width = xsum (xtimes (width, 10), *digitp++ - '0');
+ while (digitp != dp->width_end);
+ }
+#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
+ has_width = 1;
+#endif
+ }
+#endif
+
+#if !USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || NEED_PRINTF_UNBOUNDED_PRECISION
+ has_precision = 0;
+ precision = 6;
+ if (dp->precision_start != dp->precision_end)
+ {
+ if (dp->precision_arg_index != ARG_NONE)
+ {
+ int arg;
+
+ if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
+ abort ();
+ arg = a.arg[dp->precision_arg_index].a.a_int;
+ /* "A negative precision is taken as if the precision
+ were omitted." */
+ if (arg >= 0)
+ {
+ precision = arg;
+ has_precision = 1;
+ }
+ }
+ else
+ {
+ const FCHAR_T *digitp = dp->precision_start + 1;
+
+ precision = 0;
+ while (digitp != dp->precision_end)
+ precision = xsum (xtimes (precision, 10), *digitp++ - '0');
+ has_precision = 1;
+ }
+ }
+#endif
+
+ /* Decide whether to handle the precision ourselves. */
+#if NEED_PRINTF_UNBOUNDED_PRECISION
+ switch (dp->conversion)
+ {
+ case 'd': case 'i': case 'u':
+ case 'o':
+ case 'x': case 'X': case 'p':
+ prec_ourselves = has_precision && (precision > 0);
+ break;
+ default:
+ prec_ourselves = 0;
+ break;
+ }
+#endif
+
+ /* Decide whether to perform the padding ourselves. */
+#if !NEED_PRINTF_FLAG_LEFTADJUST && (!DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION)
+ switch (dp->conversion)
+ {
+# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO
+ /* If we need conversion from TCHAR_T[] to DCHAR_T[], we need
+ to perform the padding after this conversion. Functions
+ with unistdio extensions perform the padding based on
+ character count rather than element count. */
+ case 'c': case 's':
+# endif
+# if NEED_PRINTF_FLAG_ZERO
+ case 'f': case 'F': case 'e': case 'E': case 'g': case 'G':
+ case 'a': case 'A':
+# endif
+ pad_ourselves = 1;
+ break;
+ default:
+ pad_ourselves = prec_ourselves;
+ break;
+ }
+#endif
+
+#if !USE_SNPRINTF
+ /* Allocate a temporary buffer of sufficient size for calling
+ sprintf. */
+ tmp_length =
+ MAX_ROOM_NEEDED (&a, dp->arg_index, dp->conversion, type,
+ flags, width, has_precision, precision,
+ pad_ourselves);
+
+ if (tmp_length <= sizeof (tmpbuf) / sizeof (TCHAR_T))
+ tmp = tmpbuf;
+ else
+ {
+ size_t tmp_memsize = xtimes (tmp_length, sizeof (TCHAR_T));
+
+ if (size_overflow_p (tmp_memsize))
+ /* Overflow, would lead to out of memory. */
+ goto out_of_memory;
+ tmp = (TCHAR_T *) malloc (tmp_memsize);
+ if (tmp == NULL)
+ /* Out of memory. */
+ goto out_of_memory;
+ }
+#endif
+
+ /* Construct the format string for calling snprintf or
+ sprintf. */
+ fbp = buf;
+ *fbp++ = '%';
+#if NEED_PRINTF_FLAG_GROUPING
+ /* The underlying implementation doesn't support the ' flag.
+ Produce no grouping characters in this case; this is
+ acceptable because the grouping is locale dependent. */
+#else
+ if (flags & FLAG_GROUP)
+ *fbp++ = '\'';
+#endif
+ if (flags & FLAG_LEFT)
+ *fbp++ = '-';
+ if (flags & FLAG_SHOWSIGN)
+ *fbp++ = '+';
+ if (flags & FLAG_SPACE)
+ *fbp++ = ' ';
+ if (flags & FLAG_ALT)
+ *fbp++ = '#';
+#if __GLIBC__ >= 2 && !defined __UCLIBC__
+ if (flags & FLAG_LOCALIZED)
+ *fbp++ = 'I';
+#endif
+ if (!pad_ourselves)
+ {
+ if (flags & FLAG_ZERO)
+ *fbp++ = '0';
+ if (dp->width_start != dp->width_end)
+ {
+ size_t n = dp->width_end - dp->width_start;
+ /* The width specification is known to consist only
+ of standard ASCII characters. */
+ if (sizeof (FCHAR_T) == sizeof (TCHAR_T))
+ {
+ memcpy (fbp, dp->width_start, n * sizeof (TCHAR_T));
+ fbp += n;
+ }
+ else
+ {
+ const FCHAR_T *mp = dp->width_start;
+ do
+ *fbp++ = *mp++;
+ while (--n > 0);
+ }
+ }
+ }
+ if (!prec_ourselves)
+ {
+ if (dp->precision_start != dp->precision_end)
+ {
+ size_t n = dp->precision_end - dp->precision_start;
+ /* The precision specification is known to consist only
+ of standard ASCII characters. */
+ if (sizeof (FCHAR_T) == sizeof (TCHAR_T))
+ {
+ memcpy (fbp, dp->precision_start, n * sizeof (TCHAR_T));
+ fbp += n;
+ }
+ else
+ {
+ const FCHAR_T *mp = dp->precision_start;
+ do
+ *fbp++ = *mp++;
+ while (--n > 0);
+ }
+ }
+ }
+
+ switch (type)
+ {
+ case TYPE_LONGLONGINT:
+ case TYPE_ULONGLONGINT:
+#if defined _WIN32 && ! defined __CYGWIN__
+ *fbp++ = 'I';
+ *fbp++ = '6';
+ *fbp++ = '4';
+ break;
+#else
+ *fbp++ = 'l';
+#endif
+ FALLTHROUGH;
+ case TYPE_LONGINT:
+ case TYPE_ULONGINT:
+#if HAVE_WINT_T
+ case TYPE_WIDE_CHAR:
+#endif
+#if HAVE_WCHAR_T
+ case TYPE_WIDE_STRING:
+#endif
+ *fbp++ = 'l';
+ break;
+ case TYPE_LONGDOUBLE:
+ *fbp++ = 'L';
+ break;
+ default:
+ break;
+ }
+#if NEED_PRINTF_DIRECTIVE_F
+ if (dp->conversion == 'F')
+ *fbp = 'f';
+ else
+#endif
+ *fbp = dp->conversion;
+#if USE_SNPRINTF
+# if ((HAVE_SNPRINTF_RETVAL_C99 && HAVE_SNPRINTF_TRUNCATION_C99) \
+ || ((__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 3)) \
+ && !defined __UCLIBC__) \
+ || (defined __APPLE__ && defined __MACH__) \
+ || defined __ANDROID__ \
+ || (defined _WIN32 && ! defined __CYGWIN__))
+ /* On systems where we know that snprintf's return value
+ conforms to ISO C 99 (HAVE_SNPRINTF_RETVAL_C99) and that
+ snprintf always produces NUL-terminated strings
+ (HAVE_SNPRINTF_TRUNCATION_C99), it is possible to avoid
+ using %n. And it is desirable to do so, because more and
+ more platforms no longer support %n, for "security reasons".
+ In particular, the following platforms:
+ - On glibc2 systems from 2004-10-18 or newer, the use of
+ %n in format strings in writable memory may crash the
+ program (if compiled with _FORTIFY_SOURCE=2).
+ - On Mac OS X 10.13 or newer, the use of %n in format
+ strings in writable memory by default crashes the
+ program.
+ - On Android, starting on 2018-03-07, the use of %n in
+ format strings produces a fatal error (see
+ <https://android.googlesource.com/platform/bionic/+/41398d03b7e8e0dfb951660ae713e682e9fc0336>).
+ On these platforms, HAVE_SNPRINTF_RETVAL_C99 and
+ HAVE_SNPRINTF_TRUNCATION_C99 are 1. We have listed them
+ explicitly in the condition above, in case of cross-
+ compilation (just to be sure). */
+ /* On native Windows systems (such as mingw), we can avoid using
+ %n because:
+ - Although the gl_SNPRINTF_TRUNCATION_C99 test fails,
+ snprintf does not write more than the specified number
+ of bytes. (snprintf (buf, 3, "%d %d", 4567, 89) writes
+ '4', '5', '6' into buf, not '4', '5', '\0'.)
+ - Although the gl_SNPRINTF_RETVAL_C99 test fails, snprintf
+ allows us to recognize the case of an insufficient
+ buffer size: it returns -1 in this case.
+ On native Windows systems (such as mingw) where the OS is
+ Windows Vista, the use of %n in format strings by default
+ crashes the program. See
+ <https://gcc.gnu.org/ml/gcc/2007-06/msg00122.html> and
+ <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/set-printf-count-output>
+ So we should avoid %n in this situation. */
+ fbp[1] = '\0';
+# else /* AIX <= 5.1, HP-UX, IRIX, OSF/1, Solaris <= 9, BeOS */
+ fbp[1] = '%';
+ fbp[2] = 'n';
+ fbp[3] = '\0';
+# endif
+#else
+ fbp[1] = '\0';
+#endif
+
+ /* Construct the arguments for calling snprintf or sprintf. */
+ prefix_count = 0;
+ if (!pad_ourselves && dp->width_arg_index != ARG_NONE)
+ {
+ if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
+ abort ();
+ prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int;
+ }
+ if (!prec_ourselves && dp->precision_arg_index != ARG_NONE)
+ {
+ if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
+ abort ();
+ prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int;
+ }
+
+#if USE_SNPRINTF
+ /* The SNPRINTF result is appended after result[0..length].
+ The latter is an array of DCHAR_T; SNPRINTF appends an
+ array of TCHAR_T to it. This is possible because
+ sizeof (TCHAR_T) divides sizeof (DCHAR_T) and
+ alignof (TCHAR_T) <= alignof (DCHAR_T). */
+# define TCHARS_PER_DCHAR (sizeof (DCHAR_T) / sizeof (TCHAR_T))
+ /* Ensure that maxlen below will be >= 2. Needed on BeOS,
+ where an snprintf() with maxlen==1 acts like sprintf(). */
+ ENSURE_ALLOCATION (xsum (length,
+ (2 + TCHARS_PER_DCHAR - 1)
+ / TCHARS_PER_DCHAR));
+ /* Prepare checking whether snprintf returns the count
+ via %n. */
+ *(TCHAR_T *) (result + length) = '\0';
+#endif
+
+ orig_errno = errno;
+
+ for (;;)
+ {
+ int count = -1;
+
+#if USE_SNPRINTF
+ int retcount = 0;
+ size_t maxlen = allocated - length;
+ /* SNPRINTF can fail if its second argument is
+ > INT_MAX. */
+ if (maxlen > INT_MAX / TCHARS_PER_DCHAR)
+ maxlen = INT_MAX / TCHARS_PER_DCHAR;
+ maxlen = maxlen * TCHARS_PER_DCHAR;
+# define SNPRINTF_BUF(arg) \
+ switch (prefix_count) \
+ { \
+ case 0: \
+ retcount = SNPRINTF ((TCHAR_T *) (result + length), \
+ maxlen, buf, \
+ arg, &count); \
+ break; \
+ case 1: \
+ retcount = SNPRINTF ((TCHAR_T *) (result + length), \
+ maxlen, buf, \
+ prefixes[0], arg, &count); \
+ break; \
+ case 2: \
+ retcount = SNPRINTF ((TCHAR_T *) (result + length), \
+ maxlen, buf, \
+ prefixes[0], prefixes[1], arg, \
+ &count); \
+ break; \
+ default: \
+ abort (); \
+ }
+#else
+# define SNPRINTF_BUF(arg) \
+ switch (prefix_count) \
+ { \
+ case 0: \
+ count = sprintf (tmp, buf, arg); \
+ break; \
+ case 1: \
+ count = sprintf (tmp, buf, prefixes[0], arg); \
+ break; \
+ case 2: \
+ count = sprintf (tmp, buf, prefixes[0], prefixes[1],\
+ arg); \
+ break; \
+ default: \
+ abort (); \
+ }
+#endif
+
+ errno = 0;
+ switch (type)
+ {
+ case TYPE_SCHAR:
+ {
+ int arg = a.arg[dp->arg_index].a.a_schar;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_UCHAR:
+ {
+ unsigned int arg = a.arg[dp->arg_index].a.a_uchar;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_SHORT:
+ {
+ int arg = a.arg[dp->arg_index].a.a_short;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_USHORT:
+ {
+ unsigned int arg = a.arg[dp->arg_index].a.a_ushort;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_INT:
+ {
+ int arg = a.arg[dp->arg_index].a.a_int;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_UINT:
+ {
+ unsigned int arg = a.arg[dp->arg_index].a.a_uint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_LONGINT:
+ {
+ long int arg = a.arg[dp->arg_index].a.a_longint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_ULONGINT:
+ {
+ unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_LONGLONGINT:
+ {
+ long long int arg = a.arg[dp->arg_index].a.a_longlongint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_ULONGLONGINT:
+ {
+ unsigned long long int arg = a.arg[dp->arg_index].a.a_ulonglongint;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_DOUBLE:
+ {
+ double arg = a.arg[dp->arg_index].a.a_double;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_LONGDOUBLE:
+ {
+ long double arg = a.arg[dp->arg_index].a.a_longdouble;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ case TYPE_CHAR:
+ {
+ int arg = a.arg[dp->arg_index].a.a_char;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#if HAVE_WINT_T
+ case TYPE_WIDE_CHAR:
+ {
+ wint_t arg = a.arg[dp->arg_index].a.a_wide_char;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#endif
+ case TYPE_STRING:
+ {
+ const char *arg = a.arg[dp->arg_index].a.a_string;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#if HAVE_WCHAR_T
+ case TYPE_WIDE_STRING:
+ {
+ const wchar_t *arg = a.arg[dp->arg_index].a.a_wide_string;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+#endif
+ case TYPE_POINTER:
+ {
+ void *arg = a.arg[dp->arg_index].a.a_pointer;
+ SNPRINTF_BUF (arg);
+ }
+ break;
+ default:
+ abort ();
+ }
+
+#if USE_SNPRINTF
+ /* Portability: Not all implementations of snprintf()
+ are ISO C 99 compliant. Determine the number of
+ bytes that snprintf() has produced or would have
+ produced. */
+ if (count >= 0)
+ {
+ /* Verify that snprintf() has NUL-terminated its
+ result. */
+ if ((unsigned int) count < maxlen
+ && ((TCHAR_T *) (result + length)) [count] != '\0')
+ abort ();
+ /* Portability hack. */
+ if (retcount > count)
+ count = retcount;
+ }
+ else
+ {
+ /* snprintf() doesn't understand the '%n'
+ directive. */
+ if (fbp[1] != '\0')
+ {
+ /* Don't use the '%n' directive; instead, look
+ at the snprintf() return value. */
+ fbp[1] = '\0';
+ continue;
+ }
+ else
+ {
+ /* Look at the snprintf() return value. */
+ if (retcount < 0)
+ {
+# if !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF
+ /* HP-UX 10.20 snprintf() is doubly deficient:
+ It doesn't understand the '%n' directive,
+ *and* it returns -1 (rather than the length
+ that would have been required) when the
+ buffer is too small.
+ But a failure at this point can also come
+ from other reasons than a too small buffer,
+ such as an invalid wide string argument to
+ the %ls directive, or possibly an invalid
+ floating-point argument. */
+ size_t tmp_length =
+ MAX_ROOM_NEEDED (&a, dp->arg_index,
+ dp->conversion, type, flags,
+ width,
+ has_precision,
+ precision, pad_ourselves);
+
+ if (maxlen < tmp_length)
+ {
+ /* Make more room. But try to do through
+ this reallocation only once. */
+ size_t bigger_need =
+ xsum (length,
+ xsum (tmp_length,
+ TCHARS_PER_DCHAR - 1)
+ / TCHARS_PER_DCHAR);
+ /* And always grow proportionally.
+ (There may be several arguments, each
+ needing a little more room than the
+ previous one.) */
+ size_t bigger_need2 =
+ xsum (xtimes (allocated, 2), 12);
+ if (bigger_need < bigger_need2)
+ bigger_need = bigger_need2;
+ ENSURE_ALLOCATION (bigger_need);
+ continue;
+ }
+# endif
+ }
+ else
+ count = retcount;
+ }
+ }
+#endif
+
+ /* Attempt to handle failure. */
+ if (count < 0)
+ {
+ /* SNPRINTF or sprintf failed. Use the errno that it
+ has set, if any. */
+ if (errno == 0)
+ {
+ if (dp->conversion == 'c' || dp->conversion == 's')
+ errno = EILSEQ;
+ else
+ errno = EINVAL;
+ }
+
+ goto fail_with_errno;
+ }
+
+#if USE_SNPRINTF
+ /* Handle overflow of the allocated buffer.
+ If such an overflow occurs, a C99 compliant snprintf()
+ returns a count >= maxlen. However, a non-compliant
+ snprintf() function returns only count = maxlen - 1. To
+ cover both cases, test whether count >= maxlen - 1. */
+ if ((unsigned int) count + 1 >= maxlen)
+ {
+ /* If maxlen already has attained its allowed maximum,
+ allocating more memory will not increase maxlen.
+ Instead of looping, bail out. */
+ if (maxlen == INT_MAX / TCHARS_PER_DCHAR)
+ goto overflow;
+ else
+ {
+ /* Need at least (count + 1) * sizeof (TCHAR_T)
+ bytes. (The +1 is for the trailing NUL.)
+ But ask for (count + 2) * sizeof (TCHAR_T)
+ bytes, so that in the next round, we likely get
+ maxlen > (unsigned int) count + 1
+ and so we don't get here again.
+ And allocate proportionally, to avoid looping
+ eternally if snprintf() reports a too small
+ count. */
+ size_t n =
+ xmax (xsum (length,
+ ((unsigned int) count + 2
+ + TCHARS_PER_DCHAR - 1)
+ / TCHARS_PER_DCHAR),
+ xtimes (allocated, 2));
+
+ ENSURE_ALLOCATION (n);
+ continue;
+ }
+ }
+#endif
+
+#if NEED_PRINTF_UNBOUNDED_PRECISION
+ if (prec_ourselves)
+ {
+ /* Handle the precision. */
+ TCHAR_T *prec_ptr =
+# if USE_SNPRINTF
+ (TCHAR_T *) (result + length);
+# else
+ tmp;
+# endif
+ size_t prefix_count;
+ size_t move;
+
+ prefix_count = 0;
+ /* Put the additional zeroes after the sign. */
+ if (count >= 1
+ && (*prec_ptr == '-' || *prec_ptr == '+'
+ || *prec_ptr == ' '))
+ prefix_count = 1;
+ /* Put the additional zeroes after the 0x prefix if
+ (flags & FLAG_ALT) || (dp->conversion == 'p'). */
+ else if (count >= 2
+ && prec_ptr[0] == '0'
+ && (prec_ptr[1] == 'x' || prec_ptr[1] == 'X'))
+ prefix_count = 2;
+
+ move = count - prefix_count;
+ if (precision > move)
+ {
+ /* Insert zeroes. */
+ size_t insert = precision - move;
+ TCHAR_T *prec_end;
+
+# if USE_SNPRINTF
+ size_t n =
+ xsum (length,
+ (count + insert + TCHARS_PER_DCHAR - 1)
+ / TCHARS_PER_DCHAR);
+ length += (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR;
+ ENSURE_ALLOCATION (n);
+ length -= (count + TCHARS_PER_DCHAR - 1) / TCHARS_PER_DCHAR;
+ prec_ptr = (TCHAR_T *) (result + length);
+# endif
+
+ prec_end = prec_ptr + count;
+ prec_ptr += prefix_count;
+
+ while (prec_end > prec_ptr)
+ {
+ prec_end--;
+ prec_end[insert] = prec_end[0];
+ }
+
+ prec_end += insert;
+ do
+ *--prec_end = '0';
+ while (prec_end > prec_ptr);
+
+ count += insert;
+ }
+ }
+#endif
+
+#if !USE_SNPRINTF
+ if (count >= tmp_length)
+ /* tmp_length was incorrectly calculated - fix the
+ code above! */
+ abort ();
+#endif
+
+#if !DCHAR_IS_TCHAR
+ /* Convert from TCHAR_T[] to DCHAR_T[]. */
+ if (dp->conversion == 'c' || dp->conversion == 's')
+ {
+ /* type = TYPE_CHAR or TYPE_WIDE_CHAR or TYPE_STRING
+ TYPE_WIDE_STRING.
+ The result string is not certainly ASCII. */
+ const TCHAR_T *tmpsrc;
+ DCHAR_T *tmpdst;
+ size_t tmpdst_len;
+ /* This code assumes that TCHAR_T is 'char'. */
+ static_assert (sizeof (TCHAR_T) == 1);
+# if USE_SNPRINTF
+ tmpsrc = (TCHAR_T *) (result + length);
+# else
+ tmpsrc = tmp;
+# endif
+ tmpdst =
+ DCHAR_CONV_FROM_ENCODING (locale_charset (),
+ iconveh_question_mark,
+ tmpsrc, count,
+ NULL,
+ NULL, &tmpdst_len);
+ if (tmpdst == NULL)
+ goto fail_with_errno;
+ ENSURE_ALLOCATION_ELSE (xsum (length, tmpdst_len),
+ { free (tmpdst); goto out_of_memory; });
+ DCHAR_CPY (result + length, tmpdst, tmpdst_len);
+ free (tmpdst);
+ count = tmpdst_len;
+ }
+ else
+ {
+ /* The result string is ASCII.
+ Simple 1:1 conversion. */
+# if USE_SNPRINTF
+ /* If sizeof (DCHAR_T) == sizeof (TCHAR_T), it's a
+ no-op conversion, in-place on the array starting
+ at (result + length). */
+ if (sizeof (DCHAR_T) != sizeof (TCHAR_T))
+# endif
+ {
+ const TCHAR_T *tmpsrc;
+ DCHAR_T *tmpdst;
+ size_t n;
+
+# if USE_SNPRINTF
+ if (result == resultbuf)
+ {
+ tmpsrc = (TCHAR_T *) (result + length);
+ /* ENSURE_ALLOCATION will not move tmpsrc
+ (because it's part of resultbuf). */
+ ENSURE_ALLOCATION (xsum (length, count));
+ }
+ else
+ {
+ /* ENSURE_ALLOCATION will move the array
+ (because it uses realloc(). */
+ ENSURE_ALLOCATION (xsum (length, count));
+ tmpsrc = (TCHAR_T *) (result + length);
+ }
+# else
+ tmpsrc = tmp;
+ ENSURE_ALLOCATION (xsum (length, count));
+# endif
+ tmpdst = result + length;
+ /* Copy backwards, because of overlapping. */
+ tmpsrc += count;
+ tmpdst += count;
+ for (n = count; n > 0; n--)
+ *--tmpdst = *--tmpsrc;
+ }
+ }
+#endif
+
+#if DCHAR_IS_TCHAR && !USE_SNPRINTF
+ /* Make room for the result. */
+ if (count > allocated - length)
+ {
+ /* Need at least count elements. But allocate
+ proportionally. */
+ size_t n =
+ xmax (xsum (length, count), xtimes (allocated, 2));
+
+ ENSURE_ALLOCATION (n);
+ }
+#endif
+
+ /* Here count <= allocated - length. */
+
+ /* Perform padding. */
+#if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO || NEED_PRINTF_FLAG_LEFTADJUST || NEED_PRINTF_FLAG_ZERO || NEED_PRINTF_UNBOUNDED_PRECISION
+ if (pad_ourselves && has_width)
+ {
+ size_t w;
+# if ENABLE_UNISTDIO
+ /* Outside POSIX, it's preferable to compare the width
+ against the number of _characters_ of the converted
+ value. */
+ w = DCHAR_MBSNLEN (result + length, count);
+# else
+ /* The width is compared against the number of _bytes_
+ of the converted value, says POSIX. */
+ w = count;
+# endif
+ if (w < width)
+ {
+ size_t pad = width - w;
+
+ /* Make room for the result. */
+ if (xsum (count, pad) > allocated - length)
+ {
+ /* Need at least count + pad elements. But
+ allocate proportionally. */
+ size_t n =
+ xmax (xsum3 (length, count, pad),
+ xtimes (allocated, 2));
+
+# if USE_SNPRINTF
+ length += count;
+ ENSURE_ALLOCATION (n);
+ length -= count;
+# else
+ ENSURE_ALLOCATION (n);
+# endif
+ }
+ /* Here count + pad <= allocated - length. */
+
+ {
+# if !DCHAR_IS_TCHAR || USE_SNPRINTF
+ DCHAR_T * const rp = result + length;
+# else
+ DCHAR_T * const rp = tmp;
+# endif
+ DCHAR_T *p = rp + count;
+ DCHAR_T *end = p + pad;
+ DCHAR_T *pad_ptr;
+# if !DCHAR_IS_TCHAR || ENABLE_UNISTDIO
+ if (dp->conversion == 'c'
+ || dp->conversion == 's')
+ /* No zero-padding for string directives. */
+ pad_ptr = NULL;
+ else
+# endif
+ {
+ pad_ptr = (*rp == '-' ? rp + 1 : rp);
+ /* No zero-padding of "inf" and "nan". */
+ if ((*pad_ptr >= 'A' && *pad_ptr <= 'Z')
+ || (*pad_ptr >= 'a' && *pad_ptr <= 'z'))
+ pad_ptr = NULL;
+ }
+ /* The generated string now extends from rp to p,
+ with the zero padding insertion point being at
+ pad_ptr. */
+
+ count = count + pad; /* = end - rp */
+
+ if (flags & FLAG_LEFT)
+ {
+ /* Pad with spaces on the right. */
+ for (; pad > 0; pad--)
+ *p++ = ' ';
+ }
+ else if ((flags & FLAG_ZERO) && pad_ptr != NULL)
+ {
+ /* Pad with zeroes. */
+ DCHAR_T *q = end;
+
+ while (p > pad_ptr)
+ *--q = *--p;
+ for (; pad > 0; pad--)
+ *p++ = '0';
+ }
+ else
+ {
+ /* Pad with spaces on the left. */
+ DCHAR_T *q = end;
+
+ while (p > rp)
+ *--q = *--p;
+ for (; pad > 0; pad--)
+ *p++ = ' ';
+ }
+ }
+ }
+ }
+#endif
+
+ /* Here still count <= allocated - length. */
+
+#if !DCHAR_IS_TCHAR || USE_SNPRINTF
+ /* The snprintf() result did fit. */
+#else
+ /* Append the sprintf() result. */
+ memcpy (result + length, tmp, count * sizeof (DCHAR_T));
+#endif
+#if !USE_SNPRINTF
+ if (tmp != tmpbuf)
+ free (tmp);
+#endif
+
+#if NEED_PRINTF_DIRECTIVE_F
+ if (dp->conversion == 'F')
+ {
+ /* Convert the %f result to upper case for %F. */
+ DCHAR_T *rp = result + length;
+ size_t rc;
+ for (rc = count; rc > 0; rc--, rp++)
+ if (*rp >= 'a' && *rp <= 'z')
+ *rp = *rp - 'a' + 'A';
+ }
+#endif
+
+ length += count;
+ break;
+ }
+ errno = orig_errno;
+#undef pad_ourselves
+#undef prec_ourselves
+ }
+ }
+ }
+
+ /* Add the final NUL. */
+ ENSURE_ALLOCATION (xsum (length, 1));
+ result[length] = '\0';
+
+ if (result != resultbuf && length + 1 < allocated)
+ {
+ /* Shrink the allocated memory if possible. */
+ DCHAR_T *memory;
+
+ memory = (DCHAR_T *) realloc (result, (length + 1) * sizeof (DCHAR_T));
+ if (memory != NULL)
+ result = memory;
+ }
+
+ if (buf_malloced != NULL)
+ free (buf_malloced);
+ CLEANUP ();
+ *lengthp = length;
+ /* Note that we can produce a big string of a length > INT_MAX. POSIX
+ says that snprintf() fails with errno = EOVERFLOW in this case, but
+ that's only because snprintf() returns an 'int'. This function does
+ not have this limitation. */
+ return result;
+
+#if USE_SNPRINTF
+ overflow:
+ errno = EOVERFLOW;
+ goto fail_with_errno;
+#endif
+
+ out_of_memory:
+ errno = ENOMEM;
+ goto fail_with_errno;
+
+#if ENABLE_UNISTDIO || ((!USE_SNPRINTF || !HAVE_SNPRINTF_RETVAL_C99 || USE_MSVC__SNPRINTF || (NEED_PRINTF_DIRECTIVE_LS && !defined IN_LIBINTL) || ENABLE_WCHAR_FALLBACK) && HAVE_WCHAR_T)
+ fail_with_EILSEQ:
+ errno = EILSEQ;
+ goto fail_with_errno;
+#endif
+
+ fail_with_errno:
+ if (result != resultbuf)
+ free (result);
+ if (buf_malloced != NULL)
+ free (buf_malloced);
+ CLEANUP ();
+ return NULL;
+ }
+
+ out_of_memory_1:
+ errno = ENOMEM;
+ goto fail_1_with_errno;
+
+ fail_1_with_EINVAL:
+ errno = EINVAL;
+ goto fail_1_with_errno;
+
+ fail_1_with_errno:
+ CLEANUP ();
+ return NULL;
+}
+
+#undef MAX_ROOM_NEEDED
+#undef TCHARS_PER_DCHAR
+#undef SNPRINTF
+#undef USE_SNPRINTF
+#undef DCHAR_SET
+#undef DCHAR_CPY
+#undef PRINTF_PARSE
+#undef DIRECTIVES
+#undef DIRECTIVE
+#undef DCHAR_IS_TCHAR
+#undef TCHAR_T
+#undef DCHAR_T
+#undef FCHAR_T
+#undef VASNPRINTF
diff --git a/lib/vasnprintf.h b/lib/vasnprintf.h
new file mode 100644
index 00000000000..f69649fb457
--- /dev/null
+++ b/lib/vasnprintf.h
@@ -0,0 +1,72 @@
+/* vsprintf with automatic memory allocation.
+ Copyright (C) 2002-2004, 2007-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/>. */
+
+#ifndef _VASNPRINTF_H
+#define _VASNPRINTF_H
+
+/* Get va_list. */
+#include <stdarg.h>
+
+/* Get size_t. */
+#include <stddef.h>
+
+/* Get _GL_ATTRIBUTE_SPEC_PRINTF_STANDARD. */
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Write formatted output to a string dynamically allocated with malloc().
+ You can pass a preallocated buffer for the result in RESULTBUF and its
+ size in *LENGTHP; otherwise you pass RESULTBUF = NULL.
+ If successful, return the address of the string (this may be = RESULTBUF
+ if no dynamic memory allocation was necessary) and set *LENGTHP to the
+ number of resulting bytes, excluding the trailing NUL. Upon error, set
+ errno and return NULL.
+
+ When dynamic memory allocation occurs, the preallocated buffer is left
+ alone (with possibly modified contents). This makes it possible to use
+ a statically allocated or stack-allocated buffer, like this:
+
+ char buf[100];
+ size_t len = sizeof (buf);
+ char *output = vasnprintf (buf, &len, format, args);
+ if (output == NULL)
+ ... error handling ...;
+ else
+ {
+ ... use the output string ...;
+ if (output != buf)
+ free (output);
+ }
+ */
+#if REPLACE_VASNPRINTF
+# define asnprintf rpl_asnprintf
+# define vasnprintf rpl_vasnprintf
+#endif
+extern char * asnprintf (char *restrict resultbuf, size_t *lengthp,
+ const char *format, ...)
+ _GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_STANDARD, 3, 4));
+extern char * vasnprintf (char *restrict resultbuf, size_t *lengthp,
+ const char *format, va_list args)
+ _GL_ATTRIBUTE_FORMAT ((_GL_ATTRIBUTE_SPEC_PRINTF_STANDARD, 3, 0));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _VASNPRINTF_H */
diff --git a/lib/vasprintf.c b/lib/vasprintf.c
new file mode 100644
index 00000000000..d2878cd91d8
--- /dev/null
+++ b/lib/vasprintf.c
@@ -0,0 +1,50 @@
+/* Formatted output to strings.
+ Copyright (C) 1999, 2002, 2006-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/>. */
+
+#include <config.h>
+
+/* Specification. */
+#ifdef IN_LIBASPRINTF
+# include "vasprintf.h"
+#else
+# include <stdio.h>
+#endif
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+
+#include "vasnprintf.h"
+
+int
+vasprintf (char **resultp, const char *format, va_list args)
+{
+ size_t length;
+ char *result = vasnprintf (NULL, &length, format, args);
+ if (result == NULL)
+ return -1;
+
+ if (length > INT_MAX)
+ {
+ free (result);
+ errno = EOVERFLOW;
+ return -1;
+ }
+
+ *resultp = result;
+ /* Return the number of resulting bytes, excluding the trailing NUL. */
+ return length;
+}
diff --git a/lib/vfprintf.c b/lib/vfprintf.c
new file mode 100644
index 00000000000..01d79a2beca
--- /dev/null
+++ b/lib/vfprintf.c
@@ -0,0 +1,70 @@
+/* Formatted output to a stream.
+ Copyright (C) 2004, 2006-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 3 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/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+/* Specification. */
+#include <stdio.h>
+
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "fseterr.h"
+#include "vasnprintf.h"
+
+/* Print formatted output to the stream FP.
+ Return string length of formatted string. On error, return a negative
+ value. */
+int
+vfprintf (FILE *fp, const char *format, va_list args)
+{
+ char buf[2000];
+ char *output;
+ size_t len;
+ size_t lenbuf = sizeof (buf);
+
+ output = vasnprintf (buf, &lenbuf, format, args);
+ len = lenbuf;
+
+ if (!output)
+ {
+ fseterr (fp);
+ return -1;
+ }
+
+ if (fwrite (output, 1, len, fp) < len)
+ {
+ if (output != buf)
+ free (output);
+ return -1;
+ }
+
+ if (output != buf)
+ free (output);
+
+ if (len > INT_MAX)
+ {
+ errno = EOVERFLOW;
+ fseterr (fp);
+ return -1;
+ }
+
+ return len;
+}
diff --git a/lib/xsize.c b/lib/xsize.c
new file mode 100644
index 00000000000..279ae824f87
--- /dev/null
+++ b/lib/xsize.c
@@ -0,0 +1,21 @@
+/* Checked size_t computations.
+
+ Copyright (C) 2012-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/>. */
+
+#include <config.h>
+
+#define XSIZE_INLINE _GL_EXTERN_INLINE
+#include "xsize.h"
diff --git a/lib/xsize.h b/lib/xsize.h
new file mode 100644
index 00000000000..1ec78e776fc
--- /dev/null
+++ b/lib/xsize.h
@@ -0,0 +1,108 @@
+/* xsize.h -- Checked size_t computations.
+
+ Copyright (C) 2003, 2008-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/>. */
+
+#ifndef _XSIZE_H
+#define _XSIZE_H
+
+/* Get size_t. */
+#include <stddef.h>
+
+/* Get SIZE_MAX. */
+#include <limits.h>
+#if HAVE_STDINT_H
+# include <stdint.h>
+#endif
+
+/* Get ATTRIBUTE_PURE. */
+#include "attribute.h"
+
+#ifndef _GL_INLINE_HEADER_BEGIN
+ #error "Please include config.h first."
+#endif
+_GL_INLINE_HEADER_BEGIN
+#ifndef XSIZE_INLINE
+# define XSIZE_INLINE _GL_INLINE
+#endif
+
+/* The size of memory objects is often computed through expressions of
+ type size_t. Example:
+ void* p = malloc (header_size + n * element_size).
+ These computations can lead to overflow. When this happens, malloc()
+ returns a piece of memory that is way too small, and the program then
+ crashes while attempting to fill the memory.
+ To avoid this, the functions and macros in this file check for overflow.
+ The convention is that SIZE_MAX represents overflow.
+ malloc (SIZE_MAX) is not guaranteed to fail -- think of a malloc
+ implementation that uses mmap --, it's recommended to use size_overflow_p()
+ or size_in_bounds_p() before invoking malloc().
+ The example thus becomes:
+ size_t size = xsum (header_size, xtimes (n, element_size));
+ void *p = (size_in_bounds_p (size) ? malloc (size) : NULL);
+*/
+
+/* Convert an arbitrary value >= 0 to type size_t. */
+#define xcast_size_t(N) \
+ ((N) <= SIZE_MAX ? (size_t) (N) : SIZE_MAX)
+
+/* Sum of two sizes, with overflow check. */
+XSIZE_INLINE size_t ATTRIBUTE_PURE
+xsum (size_t size1, size_t size2)
+{
+ size_t sum = size1 + size2;
+ return (sum >= size1 ? sum : SIZE_MAX);
+}
+
+/* Sum of three sizes, with overflow check. */
+XSIZE_INLINE size_t ATTRIBUTE_PURE
+xsum3 (size_t size1, size_t size2, size_t size3)
+{
+ return xsum (xsum (size1, size2), size3);
+}
+
+/* Sum of four sizes, with overflow check. */
+XSIZE_INLINE size_t ATTRIBUTE_PURE
+xsum4 (size_t size1, size_t size2, size_t size3, size_t size4)
+{
+ return xsum (xsum (xsum (size1, size2), size3), size4);
+}
+
+/* Maximum of two sizes, with overflow check. */
+XSIZE_INLINE size_t ATTRIBUTE_PURE
+xmax (size_t size1, size_t size2)
+{
+ /* No explicit check is needed here, because for any n:
+ max (SIZE_MAX, n) == SIZE_MAX and max (n, SIZE_MAX) == SIZE_MAX. */
+ return (size1 >= size2 ? size1 : size2);
+}
+
+/* Multiplication of a count with an element size, with overflow check.
+ The count must be >= 0 and the element size must be > 0.
+ This is a macro, not a function, so that it works correctly even
+ when N is of a wider type and N > SIZE_MAX. */
+#define xtimes(N, ELSIZE) \
+ ((N) <= SIZE_MAX / (ELSIZE) ? (size_t) (N) * (ELSIZE) : SIZE_MAX)
+
+/* Check for overflow. */
+#define size_overflow_p(SIZE) \
+ ((SIZE) == SIZE_MAX)
+/* Check against overflow. */
+#define size_in_bounds_p(SIZE) \
+ ((SIZE) != SIZE_MAX)
+
+_GL_INLINE_HEADER_END
+
+#endif /* _XSIZE_H */
diff --git a/lisp/battery.el b/lisp/battery.el
index 4306d5b2058..c55fcbbee8c 100644
--- a/lisp/battery.el
+++ b/lisp/battery.el
@@ -29,9 +29,11 @@
;; - The `/sys/class/power_supply/' files of Linux >= 2.6.39.
;; - The `/proc/acpi/' directory structure of Linux 2.4.20 and 2.6.
;; - The `/proc/apm' file format of Linux version 1.3.58 or newer.
+;; - The Haiku ACPI battery driver.
;; - BSD by using the `apm' program.
;; - Darwin (macOS) by using the `pmset' program.
;; - Windows via the GetSystemPowerStatus API call.
+;; - Android 5 or later via the BatteryManager APIs.
;;; Code:
@@ -106,6 +108,12 @@ Value does not include \".\" or \"..\"."
(file-readable-p "/proc/")
(file-readable-p "/proc/apm"))
#'battery-linux-proc-apm)
+ ;; Now try the Android battery status function.
+ ;; Note that even though the Linux kernel APIs are sometimes
+ ;; available on Android, they are badly implemented by Android
+ ;; kernels, so avoid using those.
+ ((eq system-type 'android)
+ #'battery-android)
((and (eq system-type 'berkeley-unix)
(file-executable-p "/usr/sbin/apm"))
#'battery-bsd-apm)
@@ -1072,6 +1080,78 @@ The following %-sequences are provided:
(cons ?t (or remaining-time "N/A")))))
+;;; `BatteryManager' interface for Android.
+
+(declare-function android-query-battery "androidfns.c")
+
+(defun battery-android ()
+ "Get battery status information using Android.
+
+The following %-sequences are provided:
+%c Current capacity (mAh)
+%r Current rate of charge or discharge (mA)
+%L AC line status (verbose).
+%B Battery status (verbose)
+%b Battery status, empty means high, `-' means low,
+ `+' means charging and `?' means unknown.
+%d Temperature (in degrees Celsius)
+%p Battery load percentage.
+%m Remaining time (to charge) in minutes.
+%h Remaining time (to charge) in hours.
+%t Remaining time (to charge) in the form `h:min'."
+ (when-let* ((status (android-query-battery)))
+ (let* ((percentage nil)
+ (capacity nil)
+ (sym-status nil)
+ (symbol nil)
+ (rate nil)
+ (remaining nil)
+ (hours nil)
+ (minutes nil))
+ ;; Figure out the percentage.
+ (setq percentage (number-to-string (car status)))
+ ;; Figure out the capacity
+ (setq capacity (number-to-string (/ (cadr status) 1000)))
+ ;; Figure out the battery status.
+ (let ((percentage (car status)))
+ (cl-ecase (nth 4 status)
+ (2 (setq sym-status "charging" symbol "+"))
+ (3 (setq sym-status "discharging"
+ symbol (if (< percentage 15) "-" " ")))
+ (5 (setq sym-status "full" symbol " "))
+ (4 (setq sym-status "not charging"
+ symbol (if (< percentage 15) "-" " ")))
+ (1 (setq sym-status "unknown" symbol "?"))))
+ ;; Figure out the rate of charge.
+ (setq rate (/ (nth 3 status) 1000))
+ ;; Figure out the remaining time.
+ (let* ((time (nth 5 status))
+ (mins (/ time (* 1000 60)))
+ (hours-left (/ mins 60))
+ (mins (mod mins 60)))
+ (unless (eq time -1)
+ (setq remaining (format "%d:%d" hours-left mins)
+ hours (number-to-string hours-left)
+ minutes (number-to-string mins))))
+ ;; Return results.
+ (list (cons ?c capacity)
+ (cons ?p percentage)
+ (cons ?r rate)
+ (cons ?B sym-status)
+ (cons ?b symbol)
+ (cons ?m (or minutes "N/A"))
+ (cons ?h (or hours "N/A"))
+ (cons ?t (or remaining "N/A"))
+ (cons ?L (cl-case (nth 6 status)
+ (0 "off-line")
+ (1 "on-line")
+ (2 "on-line (dock)")
+ (3 "on-line (USB)")
+ (4 "on-line (wireless)")
+ (t "unknown")))
+ (cons ?t (/ (or (nth 7 status) 0) 10.0))))))
+
+
;;; Private functions.
(defun battery-format (format alist)
diff --git a/lisp/bindings.el b/lisp/bindings.el
index c77b64c05da..eec51a4e413 100644
--- a/lisp/bindings.el
+++ b/lisp/bindings.el
@@ -1521,6 +1521,9 @@ if `inhibit-field-text-motion' is non-nil."
(define-key special-event-map [sigusr1] 'ignore)
(define-key special-event-map [sigusr2] 'ignore)
+;; Text conversion
+(define-key global-map [text-conversion] 'analyze-text-conversion)
+
;; Don't look for autoload cookies in this file.
;; Local Variables:
;; no-update-autoloads: t
diff --git a/lisp/button.el b/lisp/button.el
index f043073ea86..65abb81ec46 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/cedet/semantic/db-ebrowse.el b/lisp/cedet/semantic/db-ebrowse.el
index f8ea73cbdde..3e54b9e76cf 100644
--- a/lisp/cedet/semantic/db-ebrowse.el
+++ b/lisp/cedet/semantic/db-ebrowse.el
@@ -158,7 +158,8 @@ is specified by `semanticdb-default-save-directory'."
;; Call the EBROWSE command.
(message "Creating ebrowse file: %s ..." savein)
(call-process-region (point-min) (point-max)
- "ebrowse" nil "*EBROWSE OUTPUT*" nil
+ ebrowse-program-name
+ nil "*EBROWSE OUTPUT*" nil
(concat "--output-file=" savein)
"--very-verbose")
)
diff --git a/lisp/comint.el b/lisp/comint.el
index 9d2c245247f..e1786e7e670 100644
--- a/lisp/comint.el
+++ b/lisp/comint.el
@@ -694,6 +694,9 @@ Entry to this mode runs the hooks on `comint-mode-hook'."
(setq-local comint-last-input-start (point-min-marker))
(setq-local comint-last-input-end (point-min-marker))
(setq-local comint-last-output-start (make-marker))
+ ;; It is ok to let the input method edit prompt text, but RET must
+ ;; be processed by Emacs.
+ (setq text-conversion-style 'action)
(make-local-variable 'comint-last-prompt)
(make-local-variable 'comint-prompt-regexp) ; Don't set; default
(make-local-variable 'comint-input-ring-size) ; ...to global val.
diff --git a/lisp/cus-edit.el b/lisp/cus-edit.el
index dbef5f47cd6..d0eba71e754 100644
--- a/lisp/cus-edit.el
+++ b/lisp/cus-edit.el
@@ -2209,7 +2209,7 @@ and `face'."
;;; The `custom' Widget.
(defface custom-button
- '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "lightgrey" :foreground "black"))
"Face for custom buffer buttons if `custom-raised-buttons' is non-nil."
@@ -2217,7 +2217,7 @@ and `face'."
:group 'custom-faces)
(defface custom-button-mouse
- '((((type x w32 ns haiku pgtk) (class color))
+ '((((type x w32 ns haiku pgtk android) (class color))
:box (:line-width 2 :style released-button)
:background "grey90" :foreground "black")
(t
@@ -2242,7 +2242,7 @@ and `face'."
(if custom-raised-buttons 'custom-button-mouse 'highlight))
(defface custom-button-pressed
- '((((type x w32 ns haiku pgtk) (class color))
+ '((((type x w32 ns haiku pgtk android) (class color))
:box (:line-width 2 :style pressed-button)
:background "lightgrey" :foreground "black")
(t :inverse-video t))
diff --git a/lisp/elec-pair.el b/lisp/elec-pair.el
index b894965eae4..a7fd8e4a70e 100644
--- a/lisp/elec-pair.el
+++ b/lisp/elec-pair.el
@@ -162,6 +162,20 @@ Before attempting a skip, if `electric-pair-skip-whitespace' is
non-nil, this function is called. It move point to a new buffer
position, presumably skipping only whitespace in between.")
+(defun electric-pair-analyze-conversion (string)
+ "Notice that STRING has been deleted by an input method.
+If the last character of STRING is an electric pair character,
+and the character after point is too, then delete that other
+character."
+ (let* ((prev (aref string (1- (length string))))
+ (next (char-after))
+ (syntax-info (electric-pair-syntax-info prev))
+ (syntax (car syntax-info))
+ (pair (cadr syntax-info)))
+ (when (and next pair (memq syntax '(?\( ?\" ?\$))
+ (eq pair next))
+ (delete-char 1))))
+
(defun electric-pair--skip-whitespace ()
"Skip whitespace forward, not crossing comment or string boundaries."
(let ((saved (point))
diff --git a/lisp/electric.el b/lisp/electric.el
index cef5326852c..8b379ed16e2 100644
--- a/lisp/electric.el
+++ b/lisp/electric.el
@@ -294,6 +294,7 @@ or comment."
;;;###autoload
(define-key global-map "\C-j" 'electric-newline-and-maybe-indent)
+
;;;###autoload
(defun electric-newline-and-maybe-indent ()
"Insert a newline.
diff --git a/lisp/faces.el b/lisp/faces.el
index 739e5bdf310..dabe847698c 100644
--- a/lisp/faces.el
+++ b/lisp/faces.el
@@ -2920,7 +2920,7 @@ Note: Other faces cannot inherit from the cursor face."
(((type haiku))
:foreground "B_MENU_ITEM_TEXT_COLOR"
:background "B_MENU_BACKGROUND_COLOR")
- (((type x w32 ns pgtk) (class color))
+ (((type x w32 ns pgtk android) (class color))
:background "grey75")
(((type x) (class mono))
:background "grey"))
diff --git a/lisp/files.el b/lisp/files.el
index 148f47cbc97..e93181499b7 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -5764,9 +5764,14 @@ Before and after saving the buffer, this function runs
(run-hook-with-args-until-success 'write-file-functions)
;; If a hook returned t, file is already "written".
;; Otherwise, write it the usual way now.
- (let ((dir (file-name-directory
+ (let ((file (buffer-file-name))
+ (dir (file-name-directory
(expand-file-name buffer-file-name))))
- (unless (file-exists-p dir)
+ ;; Some systems have directories (like /content on
+ ;; Android) in which files can exist without a
+ ;; corresponding parent directory.
+ (unless (or (file-exists-p file)
+ (file-exists-p dir))
(if (y-or-n-p
(format-message
"Directory `%s' does not exist; create? " dir))
diff --git a/lisp/frame.el b/lisp/frame.el
index 39e8a4c88b8..ba5d1caafa2 100644
--- a/lisp/frame.el
+++ b/lisp/frame.el
@@ -1653,6 +1653,7 @@ live frame and defaults to the selected one."
(declare-function ns-frame-geometry "nsfns.m" (&optional frame))
(declare-function pgtk-frame-geometry "pgtkfns.c" (&optional frame))
(declare-function haiku-frame-geometry "haikufns.c" (&optional frame))
+(declare-function android-frame-geometry "androidfns.c" (&optional frame))
(defun frame-geometry (&optional frame)
"Return geometric attributes of FRAME.
@@ -1706,6 +1707,8 @@ and width values are in pixels.
(pgtk-frame-geometry frame))
((eq frame-type 'haiku)
(haiku-frame-geometry frame))
+ ((eq frame-type 'android)
+ (android-frame-geometry frame))
(t
(list
'(outer-position 0 . 0)
@@ -1832,6 +1835,7 @@ of frames like calls to map a frame or change its visibility."
(declare-function ns-frame-edges "nsfns.m" (&optional frame type))
(declare-function pgtk-frame-edges "pgtkfns.c" (&optional frame type))
(declare-function haiku-frame-edges "haikufns.c" (&optional frame type))
+(declare-function android-frame-edges "androidfns.c" (&optional frame type))
(defun frame-edges (&optional frame type)
"Return coordinates of FRAME's edges.
@@ -1859,6 +1863,8 @@ FRAME."
(pgtk-frame-edges frame type))
((eq frame-type 'haiku)
(haiku-frame-edges frame type))
+ ((eq frame-type 'android)
+ (android-frame-edges frame type))
(t
(list 0 0 (frame-width frame) (frame-height frame))))))
@@ -1867,6 +1873,7 @@ FRAME."
(declare-function ns-mouse-absolute-pixel-position "nsfns.m")
(declare-function pgtk-mouse-absolute-pixel-position "pgtkfns.c")
(declare-function haiku-mouse-absolute-pixel-position "haikufns.c")
+(declare-function android-mouse-absolute-pixel-position "androidfns.c")
(defun mouse-absolute-pixel-position ()
"Return absolute position of mouse cursor in pixels.
@@ -1885,6 +1892,8 @@ position (0, 0) of the selected frame's terminal."
(pgtk-mouse-absolute-pixel-position))
((eq frame-type 'haiku)
(haiku-mouse-absolute-pixel-position))
+ ((eq frame-type 'android)
+ (android-mouse-absolute-pixel-position))
(t
(cons 0 0)))))
@@ -1893,6 +1902,8 @@ position (0, 0) of the selected frame's terminal."
(declare-function w32-set-mouse-absolute-pixel-position "w32fns.c" (x y))
(declare-function x-set-mouse-absolute-pixel-position "xfns.c" (x y))
(declare-function haiku-set-mouse-absolute-pixel-position "haikufns.c" (x y))
+(declare-function android-set-mouse-absolute-pixel-position
+ "androidfns.c" (x y))
(defun set-mouse-absolute-pixel-position (x y)
"Move mouse pointer to absolute pixel position (X, Y).
@@ -1909,7 +1920,9 @@ position (0, 0) of the selected frame's terminal."
((eq frame-type 'w32)
(w32-set-mouse-absolute-pixel-position x y))
((eq frame-type 'haiku)
- (haiku-set-mouse-absolute-pixel-position x y)))))
+ (haiku-set-mouse-absolute-pixel-position x y))
+ ((eq frame-type 'android)
+ (android-set-mouse-absolute-pixel-position x y)))))
(defun frame-monitor-attributes (&optional frame)
"Return the attributes of the physical monitor dominating FRAME.
@@ -2005,6 +2018,7 @@ workarea attribute."
;; TODO: implement this on PGTK.
;; (declare-function pgtk-frame-list-z-order "pgtkfns.c" (&optional display))
(declare-function haiku-frame-list-z-order "haikufns.c" (&optional display))
+(declare-function android-frame-list-z-order "androidfns.c" (&optional display))
(defun frame-list-z-order (&optional display)
"Return list of Emacs' frames, in Z (stacking) order.
@@ -2030,13 +2044,17 @@ Return nil if DISPLAY contains no Emacs frame."
;; (pgtk-frame-list-z-order display)
nil)
((eq frame-type 'haiku)
- (haiku-frame-list-z-order display)))))
+ (haiku-frame-list-z-order display))
+ ((eq frame-type 'android)
+ (android-frame-list-z-order display)))))
(declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above))
(declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above))
(declare-function ns-frame-restack "nsfns.m" (frame1 frame2 &optional above))
(declare-function pgtk-frame-restack "pgtkfns.c" (frame1 frame2 &optional above))
(declare-function haiku-frame-restack "haikufns.c" (frame1 frame2 &optional above))
+(declare-function android-frame-restack "androidfns.c" (frame1 frame2
+ &optional above))
(defun frame-restack (frame1 frame2 &optional above)
"Restack FRAME1 below FRAME2.
@@ -2070,7 +2088,9 @@ Some window managers may refuse to restack windows."
((eq frame-type 'haiku)
(haiku-frame-restack frame1 frame2 above))
((eq frame-type 'pgtk)
- (pgtk-frame-restack frame1 frame2 above))))
+ (pgtk-frame-restack frame1 frame2 above))
+ ((eq frame-type 'android)
+ (android-frame-restack frame1 frame2 above))))
(error "Cannot restack frames")))
(defun frame-size-changed-p (&optional frame)
@@ -2105,6 +2125,7 @@ for FRAME."
;; or in https://debbugs.gnu.org/cgi/bugreport.cgi?bug=35058#17.
(declare-function msdos-mouse-p "dosfns.c")
+(declare-function android-detect-mouse "androidfns.c")
(defun display-mouse-p (&optional display)
"Return non-nil if DISPLAY has a mouse available.
@@ -2119,6 +2140,8 @@ frame's display)."
(> w32-num-mouse-buttons 0)))
((memq frame-type '(x ns haiku pgtk))
t) ;; We assume X, NeXTstep, GTK, and Haiku *always* have a pointing device
+ ((eq frame-type 'android)
+ (android-detect-mouse))
(t
(or (and (featurep 'xt-mouse)
xterm-mouse-mode)
@@ -2134,8 +2157,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.
@@ -2144,7 +2171,8 @@ frames and several different fonts at once. This is true for displays
that use a window system such as X, and false for text-only terminals.
DISPLAY can be a display name, a frame, or nil (meaning the selected
frame's display)."
- (not (null (memq (framep-on-display display) '(x w32 ns pgtk haiku)))))
+ (not (null (memq (framep-on-display display) '(x w32 ns pgtk haiku
+ android)))))
(defun display-images-p (&optional display)
"Return non-nil if DISPLAY can display images.
@@ -2196,7 +2224,7 @@ frame's display)."
This means that, for example, DISPLAY can differentiate between
the keybinding RET and [return]."
(let ((frame-type (framep-on-display display)))
- (or (memq frame-type '(x w32 ns pc pgtk haiku))
+ (or (memq frame-type '(x w32 ns pc pgtk haiku android))
;; MS-DOS and MS-Windows terminals have built-in support for
;; function (symbol) keys
(memq system-type '(ms-dos windows-nt)))))
@@ -2209,7 +2237,7 @@ DISPLAY should be either a frame or a display name (a string).
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku pgtk))
+ ((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-screens display))
(t
1))))
@@ -2229,7 +2257,7 @@ with DISPLAY. To get information for each physical monitor, use
`display-monitor-attributes-list'."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku pgtk))
+ ((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-pixel-height display))
(t
(frame-height (if (framep display) display (selected-frame)))))))
@@ -2249,7 +2277,7 @@ with DISPLAY. To get information for each physical monitor, use
`display-monitor-attributes-list'."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku pgtk))
+ ((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-pixel-width display))
(t
(frame-width (if (framep display) display (selected-frame)))))))
@@ -2287,7 +2315,7 @@ For graphical terminals, note that on \"multi-monitor\" setups this
refers to the height in millimeters for all physical monitors
associated with DISPLAY. To get information for each physical
monitor, use `display-monitor-attributes-list'."
- (and (memq (framep-on-display display) '(x w32 ns haiku pgtk))
+ (and (memq (framep-on-display display) '(x w32 ns haiku pgtk android))
(or (cddr (assoc (or display (frame-parameter nil 'display))
display-mm-dimensions-alist))
(cddr (assoc t display-mm-dimensions-alist))
@@ -2308,7 +2336,7 @@ For graphical terminals, note that on \"multi-monitor\" setups this
refers to the width in millimeters for all physical monitors
associated with DISPLAY. To get information for each physical
monitor, use `display-monitor-attributes-list'."
- (and (memq (framep-on-display display) '(x w32 ns haiku pgtk))
+ (and (memq (framep-on-display display) '(x w32 ns haiku pgtk android))
(or (cadr (assoc (or display (frame-parameter nil 'display))
display-mm-dimensions-alist))
(cadr (assoc t display-mm-dimensions-alist))
@@ -2326,7 +2354,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku pgtk))
+ ((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-backing-store display))
(t
'not-useful))))
@@ -2339,7 +2367,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku pgtk))
+ ((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-save-under display))
(t
'not-useful))))
@@ -2352,7 +2380,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku pgtk))
+ ((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-planes display))
((eq frame-type 'pc)
4)
@@ -2367,7 +2395,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku pgtk))
+ ((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-color-cells display))
((eq frame-type 'pc)
16)
@@ -2384,7 +2412,7 @@ DISPLAY can be a display name or a frame.
If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(let ((frame-type (framep-on-display display)))
(cond
- ((memq frame-type '(x w32 ns haiku pgtk))
+ ((memq frame-type '(x w32 ns haiku pgtk android))
(x-display-visual-class display))
((and (memq frame-type '(pc t))
(tty-display-color-p display))
@@ -2402,6 +2430,8 @@ If DISPLAY is omitted or nil, it defaults to the selected frame's display."
(&optional terminal))
(declare-function haiku-display-monitor-attributes-list "haikufns.c"
(&optional terminal))
+(declare-function android-display-monitor-attributes-list "androidfns.c"
+ (&optional terminal))
(defun display-monitor-attributes-list (&optional display)
"Return a list of physical monitor attributes on DISPLAY.
@@ -2455,6 +2485,8 @@ monitors."
(pgtk-display-monitor-attributes-list display))
((eq frame-type 'haiku)
(haiku-display-monitor-attributes-list display))
+ ((eq frame-type 'android)
+ (android-display-monitor-attributes-list display))
(t
(let ((geometry (list 0 0 (display-pixel-width display)
(display-pixel-height display))))
@@ -2530,6 +2562,28 @@ symbols."
'core-keyboard))))))
+;;;; On-screen keyboard management.
+
+(declare-function android-toggle-on-screen-keyboard "androidfns.c")
+
+(defun frame-toggle-on-screen-keyboard (frame hide)
+ "Display or hide the on-screen keyboard.
+On systems with an on-screen keyboard, display the on screen
+keyboard on behalf of the frame FRAME if HIDE is nil. Else, hide
+the on screen keyboard.
+
+Return whether or not the on screen keyboard may have been
+displayed; that is, return t on systems with an on screen
+keyboard, and nil on those without.
+
+FRAME must already have the input focus for this to work
+ reliably."
+ (let ((frame-type (framep-on-display frame)))
+ (cond ((eq frame-type 'android)
+ (android-toggle-on-screen-keyboard frame hide) t)
+ (t nil))))
+
+
;;;; Frame geometry values
(defun frame-geom-value-cons (type value &optional frame)
diff --git a/lisp/gnus/mail-source.el b/lisp/gnus/mail-source.el
index 582c598ac22..f870c0b8274 100644
--- a/lisp/gnus/mail-source.el
+++ b/lisp/gnus/mail-source.el
@@ -285,7 +285,7 @@ number."
"Number of idle seconds to wait before checking for new mail."
:type 'number)
-(defcustom mail-source-movemail-program "movemail"
+(defcustom mail-source-movemail-program movemail-program-name
"If non-nil, name of program for fetching new mail."
:version "26.2"
:type '(choice (const nil) string))
diff --git a/lisp/hexl.el b/lisp/hexl.el
index bb57f4ac4c3..5fa09459a46 100644
--- a/lisp/hexl.el
+++ b/lisp/hexl.el
@@ -60,7 +60,7 @@
(const 64))
:version "24.3")
-(defcustom hexl-program "hexl"
+(defcustom hexl-program hexl-program-name
"The program that will hexlify and dehexlify its stdin.
`hexl-program' will always be concatenated with `hexl-options'
and \"-de\" when dehexlifying a buffer."
diff --git a/lisp/htmlfontify.el b/lisp/htmlfontify.el
index f0e38242e48..6c303226e54 100644
--- a/lisp/htmlfontify.el
+++ b/lisp/htmlfontify.el
@@ -308,13 +308,14 @@ done;")
:tag "etags-cmd-alist"
:type '(alist :key-type (string) :value-type (string)))
-(defcustom hfy-etags-bin "etags"
+(defcustom hfy-etags-bin etags-program-name
"Location of etags binary (we begin by assuming it's in your path).
Note that if etags is not in your path, you will need to alter the shell
commands in `hfy-etags-cmd-alist'."
:tag "etags-bin"
- :type '(file))
+ :type '(file)
+ :version "30.1")
(defcustom hfy-shell-file-name "/bin/sh"
"Shell (Bourne or compatible) to invoke for complex shell operations."
diff --git a/lisp/ielm.el b/lisp/ielm.el
index 5c370733c05..1eeec5fbb84 100644
--- a/lisp/ielm.el
+++ b/lisp/ielm.el
@@ -605,7 +605,7 @@ Customized bindings may be defined in `ielm-map', which currently contains:
;; Was cat, but on non-Unix platforms that might not exist, so
;; use hexl instead, which is part of the Emacs distribution.
(condition-case nil
- (start-process "ielm" (current-buffer) "hexl")
+ (start-process "ielm" (current-buffer) hexl-program-name)
(file-error (start-process "ielm" (current-buffer) "cat")))
(set-process-query-on-exit-flag (ielm-process) nil)
(goto-char (point-max))
diff --git a/lisp/image/wallpaper.el b/lisp/image/wallpaper.el
index a2f175e4628..c778264d3ef 100644
--- a/lisp/image/wallpaper.el
+++ b/lisp/image/wallpaper.el
@@ -432,6 +432,8 @@ See also `wallpaper-default-width'.")
;;; wallpaper-set
+(declare-function x-open-connection "xfns.c")
+
(defun wallpaper--x-monitor-name ()
"Get the monitor name for `wallpaper-set'.
On a graphical display, try using the same monitor as the current
diff --git a/lisp/international/fontset.el b/lisp/international/fontset.el
index b72c68d9d59..bbb1993ba3c 100644
--- a/lisp/international/fontset.el
+++ b/lisp/international/fontset.el
@@ -200,7 +200,10 @@
(symbol . [#x201C #x2200 #x2500])
(braille #x2800)
(ideographic-description #x2FF0)
- (cjk-misc #x300E)
+ ;; Noto Sans Phags Pa is broken and reuses the CJK misc code
+ ;; points for some of its own characters. Add one actual CJK
+ ;; character to prevent finding such broken fonts.
+ (cjk-misc #x300E #xff0c #x300a #xff09 #x5b50)
(kana #x304B)
(bopomofo #x3105)
(kanbun #x319D)
@@ -685,7 +688,11 @@
(nil . "JISX0213.2000-2")
(nil . "JISX0213.2004-1")
,(font-spec :registry "iso10646-1" :lang 'ja)
- ,(font-spec :registry "iso10646-1" :lang 'zh))
+ ,(font-spec :registry "iso10646-1" :lang 'zh)
+ ;; This is required, as otherwise many TrueType fonts with
+ ;; CJK characters but no corresponding ``design language''
+ ;; declaration can't be found.
+ ,(font-spec :registry "iso10646-1" :script 'han))
(cjk-misc (nil . "GB2312.1980-0")
(nil . "JISX0208*")
@@ -704,7 +711,11 @@
(nil . "JISX0213.2000-1")
(nil . "JISX0213.2000-2")
,(font-spec :registry "iso10646-1" :lang 'ja)
- ,(font-spec :registry "iso10646-1" :lang 'zh))
+ ,(font-spec :registry "iso10646-1" :lang 'zh)
+ ;; This is required, as otherwise many TrueType fonts
+ ;; with CJK characters but no corresponding ``design
+ ;; language'' declaration can't be found.
+ ,(font-spec :registry "iso10646-1" :script 'cjk-misc))
(hangul (nil . "KSC5601.1987-0")
,(font-spec :registry "iso10646-1" :lang 'ko))
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 094e02d605e..a17b22fd627 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -244,6 +244,10 @@ If you use `add-function' to modify this variable, you can use the
`isearch-message-prefix' advice property to specify the prefix string
displayed in the search message.")
+(defvar isearch-text-conversion-style nil
+ "Value of `text-conversion-style' before Isearch mode
+was enabled in this buffer.")
+
;; Search ring.
(defvar search-ring nil
@@ -1221,6 +1225,8 @@ active region is added to the search string."
;; isearch-forward-regexp isearch-backward-regexp)
;; "List of commands for which isearch-mode does not recursive-edit.")
+(declare-function set-text-conversion-style "textconv.c")
+
(defun isearch-mode (forward &optional regexp op-fun recursive-edit regexp-function)
"Start Isearch minor mode.
It is called by the function `isearch-forward' and other related functions.
@@ -1237,6 +1243,8 @@ does not return to the calling function until the search is completed.
To behave this way it enters a recursive edit and exits it when done
isearching.
+Also display the on-screen keyboard if necessary.
+
The arg REGEXP-FUNCTION, if non-nil, should be a function. It is
used to set the value of `isearch-regexp-function'."
@@ -1332,6 +1340,21 @@ used to set the value of `isearch-regexp-function'."
(add-hook 'mouse-leave-buffer-hook 'isearch-mouse-leave-buffer)
(add-hook 'kbd-macro-termination-hook 'isearch-done)
+ ;; If the keyboard is not up and the last event did not come from
+ ;; a keyboard, bring it up so that the user can type.
+ (when (or (not last-event-frame)
+ (not (eq (device-class last-event-frame
+ last-event-device)
+ 'keyboard)))
+ (frame-toggle-on-screen-keyboard (selected-frame) nil))
+
+ ;; Disable text conversion so that isearch can behave correctly.
+
+ (when (fboundp 'set-text-conversion-style)
+ (setq isearch-text-conversion-style
+ text-conversion-style)
+ (set-text-conversion-style nil))
+
;; isearch-mode can be made modal (in the sense of not returning to
;; the calling function until searching is completed) by entering
;; a recursive-edit and exiting it when done isearching.
@@ -1465,6 +1488,10 @@ NOPUSH is t and EDIT is t."
(setq isearch-tool-bar-old-map nil))
(kill-local-variable 'tool-bar-map))
+ ;; Restore the previous text conversion style.
+ (when (fboundp 'set-text-conversion-style)
+ (set-text-conversion-style isearch-text-conversion-style))
+
(force-mode-line-update)
;; If we ended in the middle of some intangible text,
diff --git a/lisp/loadup.el b/lisp/loadup.el
index 1cc70348267..0a28c0592d0 100644
--- a/lisp/loadup.el
+++ b/lisp/loadup.el
@@ -258,6 +258,9 @@
(load "jit-lock")
(load "mouse")
+;; This loading happens on Android despite scroll bars being
+;; unsupported, because scroll-bar-mode (the variable) must be
+;; defined.
(if (boundp 'x-toolkit-scroll-bars)
(load "scroll-bar"))
(load "select")
@@ -295,6 +298,10 @@
(if (featurep 'dynamic-setting)
(load "dynamic-setting"))
+;; touch-screen.el is tiny and is used liberally throughout the button
+;; code etc, so it may as well be preloaded everywhere.
+(load "touch-screen")
+
(if (featurep 'x)
(progn
(load "x-dnd")
@@ -306,6 +313,12 @@
(load "term/common-win")
(load "term/haiku-win")))
+(if (featurep 'android)
+ (progn
+ (load "ls-lisp")
+ (load "term/common-win")
+ (load "term/android-win")))
+
(if (or (eq system-type 'windows-nt)
(featurep 'w32))
(progn
@@ -429,6 +442,13 @@ lost after dumping")))
(defconst emacs-build-number
(if versions (1+ (apply #'max versions)) 1))))
+;; Just set the repository branch during initial dumping on Android.
+(if (and (eq system-type 'android)
+ (not (pdumper-stats)))
+ (setq emacs-repository-version
+ (ignore-errors (emacs-repository-get-version))
+ emacs-repository-branch
+ (ignore-errors (emacs-repository-get-branch))))
(message "Finding pointers to doc strings...")
(if (and (or (and (fboundp 'dump-emacs)
@@ -546,66 +566,97 @@ lost after dumping")))
-(if dump-mode
- (let ((output (cond ((equal dump-mode "pdump") "emacs.pdmp")
- ((equal dump-mode "dump") "emacs")
- ((equal dump-mode "bootstrap") "emacs")
- ((equal dump-mode "pbootstrap") "bootstrap-emacs.pdmp")
- (t (error "Unrecognized dump mode %s" dump-mode)))))
- (when (and (featurep 'native-compile)
- (equal dump-mode "pdump"))
- ;; Don't enable this before bootstrap is completed, as the
- ;; compiler infrastructure may not be usable yet.
- (setq native-comp-enable-subr-trampolines t))
- (message "Dumping under the name %s" output)
- (condition-case ()
- (delete-file output)
- (file-error nil))
- ;; On MS-Windows, the current directory is not necessarily the
- ;; same as invocation-directory.
- (let (success)
- (unwind-protect
- (let ((tmp-dump-mode dump-mode)
- (dump-mode nil)
- (lexical-binding nil))
- (if (member tmp-dump-mode '("pdump" "pbootstrap"))
- (dump-emacs-portable (expand-file-name output invocation-directory))
- (dump-emacs output (if (eq system-type 'ms-dos)
- "temacs.exe"
- "temacs"))
- (message "%d pure bytes used" pure-bytes-used))
- (setq success t))
- (unless success
- (ignore-errors
- (delete-file output)))))
- ;; Recompute NAME now, so that it isn't set when we dump.
- (if (not (or (eq system-type 'ms-dos)
- (eq system-type 'haiku) ;; BFS doesn't support hard links
- ;; Don't bother adding another name if we're just
- ;; building bootstrap-emacs.
- (member dump-mode '("pbootstrap" "bootstrap"))))
- (let ((name (format "emacs-%s.%d" emacs-version emacs-build-number))
- (exe (if (eq system-type 'windows-nt) ".exe" "")))
- (while (string-match "[^-+_.a-zA-Z0-9]+" name)
- (setq name (concat (downcase (substring name 0 (match-beginning 0)))
- "-"
- (substring name (match-end 0)))))
- (message "Adding name %s" (concat name exe))
- ;; When this runs on Windows, invocation-directory is not
- ;; necessarily the current directory.
- (add-name-to-file (expand-file-name (concat "emacs" exe)
- invocation-directory)
- (expand-file-name (concat name exe)
- invocation-directory)
- t)
- (when (equal dump-mode "pdump")
- (message "Adding name %s" (concat name ".pdmp"))
- (add-name-to-file (expand-file-name "emacs.pdmp"
+(if (eq system-type 'android)
+ (progn
+ ;; Dumping Emacs on Android works slightly differently from
+ ;; everywhere else. The first time Emacs starts, Emacs dumps
+ ;; itself to "emacs-%s.pdump", and then proceeds with loadup,
+ ;; where %s is replaced by the dump fingerprint.
+ ;; EmacsApplication.java removes any pdump files with a
+ ;; different build fingerprint upon being created, which happens
+ ;; the moment the Android system starts Emacs. Then, it passes
+ ;; the appropriate "--dump-file" to libemacs.so as it starts.
+ (when (not noninteractive)
+ (let ((temp-dir (getenv "TEMP"))
+ (dump-file-name (format "%semacs-%s.pdmp"
+ (file-name-as-directory "~")
+ pdumper-fingerprint))
+ (dump-temp-file-name (format "%s~emacs-%s.pdmp"
+ (file-name-as-directory "~")
+ pdumper-fingerprint)))
+ (unless (pdumper-stats)
+ (condition-case ()
+ (progn
+ (dump-emacs-portable dump-temp-file-name)
+ ;; Move the dumped file to the actual dump file name.
+ (rename-file dump-temp-file-name dump-file-name)
+ ;; Continue with loadup.
+ nil)
+ (error nil))))))
+ (if dump-mode
+ (let ((output (cond ((equal dump-mode "pdump") "emacs.pdmp")
+ ((equal dump-mode "dump") "emacs")
+ ((equal dump-mode "bootstrap") "emacs")
+ ((equal dump-mode "pbootstrap") "bootstrap-emacs.pdmp")
+ (t (error "Unrecognized dump mode %s" dump-mode)))))
+ (when (and (featurep 'native-compile)
+ (equal dump-mode "pdump"))
+ ;; Don't enable this before bootstrap is completed, as the
+ ;; compiler infrastructure may not be usable yet.
+ (setq comp-enable-subr-trampolines t))
+ (message "Dumping under the name %s" output)
+ (condition-case ()
+ (delete-file output)
+ (file-error nil))
+ ;; On MS-Windows, the current directory is not necessarily the
+ ;; same as invocation-directory.
+ (let (success)
+ (unwind-protect
+ (let ((tmp-dump-mode dump-mode)
+ (dump-mode nil)
+ (lexical-binding nil))
+ (if (member tmp-dump-mode '("pdump" "pbootstrap"))
+ (dump-emacs-portable (expand-file-name output invocation-directory))
+ (dump-emacs output (if (eq system-type 'ms-dos)
+ "temacs.exe"
+ "temacs"))
+ (message "%d pure bytes used" pure-bytes-used))
+ (setq success t))
+ (unless success
+ (ignore-errors
+ (delete-file output)))))
+ ;; Recompute NAME now, so that it isn't set when we dump.
+ (if (not (or (eq system-type 'ms-dos)
+ (eq system-type 'haiku) ;; BFS doesn't support hard links
+ ;; There's no point keeping old dumps around for
+ ;; the binary used to build Lisp on the build
+ ;; machine.
+ (featurep 'android)
+ ;; Don't bother adding another name if we're just
+ ;; building bootstrap-emacs.
+ (member dump-mode '("pbootstrap" "bootstrap"))))
+ (let ((name (format "emacs-%s.%d" emacs-version emacs-build-number))
+ (exe (if (eq system-type 'windows-nt) ".exe" "")))
+ (while (string-match "[^-+_.a-zA-Z0-9]+" name)
+ (setq name (concat (downcase (substring name 0 (match-beginning 0)))
+ "-"
+ (substring name (match-end 0)))))
+ (message "Adding name %s" (concat name exe))
+ ;; When this runs on Windows, invocation-directory is not
+ ;; necessarily the current directory.
+ (add-name-to-file (expand-file-name (concat "emacs" exe)
invocation-directory)
- (expand-file-name (concat name ".pdmp")
+ (expand-file-name (concat name exe)
invocation-directory)
- t))))
- (kill-emacs)))
+ t)
+ (when (equal dump-mode "pdump")
+ (message "Adding name %s" (concat name ".pdmp"))
+ (add-name-to-file (expand-file-name "emacs.pdmp"
+ invocation-directory)
+ (expand-file-name (concat name ".pdmp")
+ invocation-directory)
+ t))))
+ (kill-emacs))))
;; This file must be loaded each time Emacs is run from scratch, e.g., temacs.
;; So run the startup code now. First, remove `-l loadup' from args.
@@ -621,6 +672,13 @@ lost after dumping")))
(setq load-file-name nil)
(eval top-level t)
+;; loadup.el is loaded at startup, but clobbers current-load-list.
+;; Set current-load-list to a list containing no definitions and only
+;; its name, to prevent invalid entries from ending up in
+;; Vload_history when running temacs interactively.
+
+(setq current-load-list (list "loadup.el"))
+
;; Local Variables:
;; no-byte-compile: t
diff --git a/lisp/ls-lisp.el b/lisp/ls-lisp.el
index 485ac4476ef..b0f86839740 100644
--- a/lisp/ls-lisp.el
+++ b/lisp/ls-lisp.el
@@ -184,7 +184,7 @@ if emulation is GNU then default is `(links uid gid)'."
:group 'ls-lisp)
(defcustom ls-lisp-use-insert-directory-program
- (not (memq system-type '(ms-dos windows-nt)))
+ (not (memq system-type '(ms-dos windows-nt android)))
"Non-nil causes ls-lisp to revert back to using `insert-directory-program'.
This is useful on platforms where ls-lisp is dumped into Emacs, such as
Microsoft Windows, but you would still like to use a program to list
diff --git a/lisp/mail/emacsbug.el b/lisp/mail/emacsbug.el
index f686c04536c..68f9dcfea0b 100644
--- a/lisp/mail/emacsbug.el
+++ b/lisp/mail/emacsbug.el
@@ -408,6 +408,12 @@ copy text to your preferred mail program.\n"
"', version "
(mapconcat #'number-to-string (x-server-version) ".") "\n")
(error t)))
+ (when (and (boundp 'android-build-fingerprint)
+ (symbol-value 'android-build-fingerprint))
+ ;; This is used on Android.
+ (insert "Android version and manufacturer: "
+ (symbol-value 'android-build-fingerprint)
+ "\n"))
(let ((os (ignore-errors (report-emacs-bug--os-description))))
(if (stringp os)
(insert "System Description: " os "\n\n")))
diff --git a/lisp/mail/rmail.el b/lisp/mail/rmail.el
index c56f4ce62dc..2952db2a6bf 100644
--- a/lisp/mail/rmail.el
+++ b/lisp/mail/rmail.el
@@ -263,7 +263,7 @@ Otherwise, look for `movemail' in the directories in
;; assuming it would work.
;; https://lists.gnu.org/r/bug-gnu-emacs/2008-02/msg00087.html
(let ((progname (expand-file-name
- (concat "movemail"
+ (concat movemail-program-name
(if (memq system-type '(ms-dos windows-nt))
".exe")) dir)))
(when (and (not (file-directory-p progname))
@@ -1989,7 +1989,9 @@ Value is the size of the newly read mail after conversion."
(buffer-disable-undo errors)
(let ((args
(append
- (list (or rmail-movemail-program "movemail") nil errors nil)
+ (list (or rmail-movemail-program
+ movemail-program-name)
+ nil errors nil)
(if rmail-preserve-inbox
(list "-p")
nil)
diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el
index d020cf6e90a..949d805465d 100644
--- a/lisp/menu-bar.el
+++ b/lisp/menu-bar.el
@@ -79,6 +79,14 @@
:help "Print current buffer with page headings"))
menu))
+(defcustom menu-bar-close-window nil
+ "Whether or not to close the current window from the menu bar.
+If non-nil, selecting Close from the File menu or clicking Close
+in the tool bar will close the current window where possible."
+ :type 'boolean
+ :group 'menu
+ :version "30.1")
+
(defvar menu-bar-file-menu
(let ((menu (make-sparse-keymap "File")))
@@ -472,6 +480,11 @@
(defvar menu-bar-edit-menu
(let ((menu (make-sparse-keymap "Edit")))
+ (bindings--define-key menu [execute-extended-command]
+ '(menu-item "Execute Command" execute-extended-command
+ :enable t
+ :help "Read a command name, its arguments, then call it."))
+
;; ns-win.el said: Add spell for platform consistency.
(if (featurep 'ns)
(bindings--define-key menu [spell]
@@ -2163,12 +2176,19 @@ otherwise it could decide to silently do nothing."
;; (Bug#8184).
((not (menu-bar-menu-frame-live-and-visible-p)))
((menu-bar-non-minibuffer-window-p)
- (kill-buffer (current-buffer)))
+ (kill-buffer (current-buffer))
+ ;; Also close the current window if `menu-bar-close-windows' is
+ ;; set.
+ (when menu-bar-close-window
+ (ignore-errors (delete-window))))
(t
(abort-recursive-edit))))
(defun kill-this-buffer-enabled-p ()
- "Return non-nil if the `kill-this-buffer' menu item should be enabled."
+ "Return non-nil if the `kill-this-buffer' menu item should be enabled.
+It should be enabled there is at least one non-hidden buffer, or if
+`menu-bar-close-window' is non-nil and there is more than one window on
+this frame."
(or (not (menu-bar-non-minibuffer-window-p))
(let (found-1)
;; Instead of looping over entire buffer list, stop once we've
@@ -2178,7 +2198,9 @@ otherwise it could decide to silently do nothing."
(unless (string-match-p "^ " (buffer-name buffer))
(if (not found-1)
(setq found-1 t)
- (throw 'found-2 t))))))))
+ (throw 'found-2 t))))))
+ (and menu-bar-close-window
+ (window-parent (selected-window)))))
(put 'dired 'menu-enable '(menu-bar-non-minibuffer-window-p))
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index a3dc1b0cfbf..36fff1520ac 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -2938,7 +2938,10 @@ For customizing this mode, it is better to use
`minibuffer-setup-hook' and `minibuffer-exit-hook' rather than
the mode hook of this mode."
:syntax-table nil
- :interactive nil)
+ :interactive nil
+ ;; Enable text conversion, but always make sure `RET' does
+ ;; something.
+ (setq text-conversion-style 'action))
;;; Completion tables.
@@ -4592,6 +4595,59 @@ is included in the return value."
default)))
": "))
+
+;;; On screen keyboard support.
+;; Try to display the on screen keyboard whenever entering the
+;; mini-buffer, and hide it whenever leaving.
+
+(defvar minibuffer-on-screen-keyboard-timer nil
+ "Timer run upon exiting the minibuffer.
+It will hide the on screen keyboard when necessary.")
+
+(defvar minibuffer-on-screen-keyboard-displayed nil
+ "Whether or not the on-screen keyboard has been displayed.
+Set inside `minibuffer-setup-on-screen-keyboard'.")
+
+(defun minibuffer-setup-on-screen-keyboard ()
+ "Maybe display the on-screen keyboard in the current frame.
+Display the on-screen keyboard in the current frame if the
+last device to have sent an input event is not a keyboard.
+This is run upon minibuffer setup."
+ ;; Don't hide the on screen keyboard later on.
+ (when minibuffer-on-screen-keyboard-timer
+ (cancel-timer minibuffer-on-screen-keyboard-timer)
+ (setq minibuffer-on-screen-keyboard-timer nil))
+ (setq minibuffer-on-screen-keyboard-displayed nil)
+ (when (and (framep last-event-frame)
+ (not (memq (device-class last-event-frame
+ last-event-device)
+ '(keyboard core-keyboard))))
+ (setq minibuffer-on-screen-keyboard-displayed
+ (frame-toggle-on-screen-keyboard (selected-frame) nil))))
+
+(defun minibuffer-exit-on-screen-keyboard ()
+ "Hide the on-screen keyboard if it was displayed.
+Hide the on-screen keyboard in a timer set to run in 0.1 seconds.
+It will be cancelled if the minibuffer is displayed again within
+that timeframe.
+
+Do not hide the on screen keyboard inside a recursive edit.
+Likewise, do not hide the on screen keyboard if point in the
+window that will be selected after exiting the minibuffer is not
+on read-only text.
+
+The latter is implemented in `touch-screen.el'."
+ (unless (or (not minibuffer-on-screen-keyboard-displayed)
+ (> (recursion-depth) 1))
+ (when minibuffer-on-screen-keyboard-timer
+ (cancel-timer minibuffer-on-screen-keyboard-timer))
+ (setq minibuffer-on-screen-keyboard-timer
+ (run-with-timer 0.1 nil #'frame-toggle-on-screen-keyboard
+ (selected-frame) t))))
+
+(add-hook 'minibuffer-setup-hook #'minibuffer-setup-on-screen-keyboard)
+(add-hook 'minibuffer-exit-hook #'minibuffer-exit-on-screen-keyboard)
+
(provide 'minibuffer)
;;; minibuffer.el ends here
diff --git a/lisp/mwheel.el b/lisp/mwheel.el
index caa74159ecd..86ed7393a17 100644
--- a/lisp/mwheel.el
+++ b/lisp/mwheel.el
@@ -58,7 +58,8 @@
(defcustom mouse-wheel-down-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
- (featurep 'haiku-win) (featurep 'pgtk-win))
+ (featurep 'haiku-win) (featurep 'pgtk-win)
+ (featurep 'android-win))
'wheel-up
'mouse-4)
"Event used for scrolling down."
@@ -79,7 +80,8 @@
(defcustom mouse-wheel-up-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
- (featurep 'haiku-win) (featurep 'pgtk-win))
+ (featurep 'haiku-win) (featurep 'pgtk-win)
+ (featurep 'android-win))
'wheel-down
'mouse-5)
"Event used for scrolling up."
@@ -254,7 +256,8 @@ Also see `mouse-wheel-tilt-scroll'."
(defvar mouse-wheel-left-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
- (featurep 'haiku-win) (featurep 'pgtk-win))
+ (featurep 'haiku-win) (featurep 'pgtk-win)
+ (featurep 'android-win))
'wheel-left
'mouse-6)
"Event used for scrolling left.")
@@ -268,7 +271,8 @@ Also see `mouse-wheel-tilt-scroll'."
(defvar mouse-wheel-right-event
(if (or (featurep 'w32-win) (featurep 'ns-win)
- (featurep 'haiku-win) (featurep 'pgtk-win))
+ (featurep 'haiku-win) (featurep 'pgtk-win)
+ (featurep 'android-win))
'wheel-right
'mouse-7)
"Event used for scrolling right.")
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index 0177d12f236..c2629b69d59 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -52,6 +52,7 @@
;; browse-url-xdg-open freedesktop.org xdg-open
;; browse-url-kde KDE konqueror (kfm)
;; browse-url-elinks Elinks Don't know (tried with 0.12.GIT)
+;; browse-url-default-android-browser Android 2.3.3 (should work on 2.2 too)
;; eww-browse-url Emacs Web Wowser
;; Browsers can cache web pages so it may be necessary to tell them to
@@ -173,6 +174,9 @@
,@(when (eq system-type 'darwin)
(list '(function-item :tag "Default macOS browser"
:value browse-url-default-macosx-browser)))
+ ,@(when (eq system-type 'android)
+ (list '(function-item :tag "Default Android browser"
+ :value browse-url-default-android-browser)))
(function-item :tag "Default browser"
:value browse-url-default-browser)
(function :tag "Your own function")
@@ -1057,6 +1061,8 @@ instead of `browse-url-new-window-flag'."
'browse-url-default-macosx-browser)
((featurep 'haiku)
'browse-url-default-haiku-browser)
+ ((eq system-type 'android)
+ 'browse-url-default-android-browser)
((browse-url-can-use-xdg-open) 'browse-url-xdg-open)
;;; ((executable-find browse-url-gnome-moz-program) 'browse-url-gnome-moz)
((executable-find browse-url-firefox-program) 'browse-url-firefox)
@@ -1294,6 +1300,22 @@ Default to the URL around or before point."
(function-put 'browse-url-default-haiku-browser
'browse-url-browser-kind 'external)
+(declare-function android-browse-url "androidselect.c")
+
+;;;###autoload
+(defun browse-url-default-android-browser (url &optional _new-window)
+ "Browse URL with the system default browser.
+Default to the URL around or before point."
+ (interactive (browse-url-interactive-arg "URL: "))
+ (setq url (browse-url-encode-url url))
+ ;; Make sure the URL starts with an appropriate scheme.
+ (unless (string-match "\\(.+\\):/" url)
+ (setq url (concat "http://" url)))
+ (android-browse-url url))
+
+(function-put 'browse-url-default-android-browser
+ 'browse-url-browser-kind 'external)
+
;;;###autoload
(defun browse-url-emacs (url &optional same-window)
"Ask Emacs to load URL into a buffer and show it in another window.
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 61f0f47373d..26aee532255 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -249,7 +249,7 @@ parameter, and should return the (possibly) transformed URL."
:version "29.1")
(defface eww-form-submit
- '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "#808080" :foreground "black"))
"Face for eww buffer buttons."
@@ -257,7 +257,7 @@ parameter, and should return the (possibly) transformed URL."
:group 'eww)
(defface eww-form-file
- '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "#808080" :foreground "black"))
"Face for eww buffer buttons."
@@ -265,7 +265,7 @@ parameter, and should return the (possibly) transformed URL."
:group 'eww)
(defface eww-form-checkbox
- '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "lightgrey" :foreground "black"))
"Face for eww buffer buttons."
@@ -273,7 +273,7 @@ parameter, and should return the (possibly) transformed URL."
:group 'eww)
(defface eww-form-select
- '((((type x w32 ns haiku pgtk) (class color)) ; Like default mode line
+ '((((type x w32 ns haiku pgtk android) (class color)) ; Like default mode line
:box (:line-width 2 :style released-button)
:background "lightgrey" :foreground "black"))
"Face for eww buffer buttons."
diff --git a/lisp/org/org-ctags.el b/lisp/org/org-ctags.el
index 5dd2bfd59cd..990214f4117 100644
--- a/lisp/org/org-ctags.el
+++ b/lisp/org/org-ctags.el
@@ -156,7 +156,9 @@ Format is: /REGEXP/TAGNAME/FLAGS,TAGTYPE/
See the ctags documentation for more information.")
(defcustom org-ctags-path-to-ctags
- (if (executable-find "ctags-exuberant") "ctags-exuberant" "ctags")
+ (if (executable-find "ctags-exuberant")
+ "ctags-exuberant"
+ ctags-program-name)
"Name of the ctags executable file."
:version "24.1"
:type 'file)
diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el
index 487144144f5..d3287d936bb 100644
--- a/lisp/pixel-scroll.el
+++ b/lisp/pixel-scroll.el
@@ -500,6 +500,7 @@ Otherwise, redisplay will reset the window's vscroll."
(set-window-start nil (pixel-point-at-unseen-line) t)
(set-window-vscroll nil vscroll t))
+;;;###autoload
(defun pixel-scroll-precision-scroll-down-page (delta)
"Scroll the current window down by DELTA pixels.
Note that this function doesn't work if DELTA is larger than
@@ -556,6 +557,7 @@ the height of the current window."
(setq delta (- delta max-height)))
(pixel-scroll-precision-scroll-down-page delta)))
+;;;###autoload
(defun pixel-scroll-precision-scroll-up-page (delta)
"Scroll the current window up by DELTA pixels.
Note that this function doesn't work if DELTA is larger than
diff --git a/lisp/play/doctor.el b/lisp/play/doctor.el
index dcf36c5c330..891274448d3 100644
--- a/lisp/play/doctor.el
+++ b/lisp/play/doctor.el
@@ -129,6 +129,9 @@
"C-j" #'doctor-read-print
"RET" #'doctor-ret-or-read)
+;; Actually defined in textconv.c.
+(defvar text-conversion-style)
+
(define-derived-mode doctor-mode text-mode "Doctor"
"Major mode for running the Doctor (Eliza) program.
Like Text mode with Auto Fill mode
@@ -137,6 +140,8 @@ reads the sentence before point, and prints the Doctor's answer."
:interactive nil
(doctor-make-variables)
(turn-on-auto-fill)
+ ;; Make sure RET is processed by Emacs.
+ (setq text-conversion-style 'action)
(doctor-type '(i am the psychotherapist \.
(doc$ doctor--please) (doc$ doctor--describe) your (doc$ doctor--problems) \.
each time you are finished talking\, type \R\E\T twice \.))
diff --git a/lisp/play/dunnet.el b/lisp/play/dunnet.el
index 837508779e7..e290a9d73ec 100644
--- a/lisp/play/dunnet.el
+++ b/lisp/play/dunnet.el
@@ -1132,9 +1132,14 @@ treasures for points?" "4" "four")
;;;; Mode definitions for interactive mode
+;; Actually defined in textconv.c.
+(defvar text-conversion-style)
+
(define-derived-mode dun-mode text-mode "Dungeon"
"Major mode for running dunnet."
:interactive nil
+ ;; Make sure RET is processed by Emacs.
+ (setq text-conversion-style 'action)
(setq-local scroll-step 2))
(defun dun-parse (_arg)
diff --git a/lisp/play/gamegrid.el b/lisp/play/gamegrid.el
index 54df983740e..bf195d6a0ec 100644
--- a/lisp/play/gamegrid.el
+++ b/lisp/play/gamegrid.el
@@ -411,7 +411,9 @@ convert to an Emacs image-spec instead")
pixel-size (floor (* resy (/ point-size 72.27)))
point-size (* (/ pixel-size resy) 72.27))
(face-spec-set gamegrid-face
- `((t :height ,(floor (* point-size 10))))))))))
+ ;; With some very high resolution displays,
+ ;; point-size floored can be zero.
+ `((t :height ,(max 8 (floor (* point-size 10)))))))))))
(defun gamegrid-initialize-display ()
(setq gamegrid-display-mode (gamegrid-display-type))
diff --git a/lisp/progmodes/cperl-mode.el b/lisp/progmodes/cperl-mode.el
index ed021a7ebc9..d6efd5d75a4 100644
--- a/lisp/progmodes/cperl-mode.el
+++ b/lisp/progmodes/cperl-mode.el
@@ -6534,7 +6534,7 @@ in subdirectories too."
;; of etags has been commented out in the menu since ... well,
;; forever. So, let's just stick to ASCII here. -- haj, 2021-09-14
(interactive)
- (let ((cmd "etags")
+ (let ((cmd etags-program-name)
(args `("-l" "none" "-r"
;; 1=fullname 2=package? 3=name 4=proto? 5=attrs? (VERY APPROX!)
,(concat
diff --git a/lisp/progmodes/prog-mode.el b/lisp/progmodes/prog-mode.el
index a434c7e9058..16497097061 100644
--- a/lisp/progmodes/prog-mode.el
+++ b/lisp/progmodes/prog-mode.el
@@ -337,6 +337,8 @@ support it."
(setq-local require-final-newline mode-require-final-newline)
(setq-local parse-sexp-ignore-comments t)
(add-hook 'context-menu-functions 'prog-context-menu 10 t)
+ ;; Enable text conversion in this buffer.
+ (setq-local text-conversion-style t)
;; Any programming language is always written left to right.
(setq bidi-paragraph-direction 'left-to-right))
diff --git a/lisp/shell.el b/lisp/shell.el
index b74442f1961..c7979e84ba0 100644
--- a/lisp/shell.el
+++ b/lisp/shell.el
@@ -1372,7 +1372,12 @@ Returns t if successful."
(while path-dirs
(setq dir (file-name-as-directory (comint-directory (or (car path-dirs) ".")))
comps-in-dir (and (file-accessible-directory-p dir)
- (file-name-all-completions filenondir dir)))
+ (condition-case nil
+ (file-name-all-completions filenondir dir)
+ ;; Systems such as Android sometimes
+ ;; put inaccessible directories in
+ ;; PATH.
+ (permission-denied nil))))
;; Go thru each completion found, to see whether it should be used.
(while comps-in-dir
(setq file (car comps-in-dir)
diff --git a/lisp/simple.el b/lisp/simple.el
index 73c2dfa365d..e79fa3f84e5 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -10536,7 +10536,7 @@ call `normal-erase-is-backspace-mode' (which see) instead."
(if (if (eq normal-erase-is-backspace 'maybe)
(and (not noninteractive)
(or (memq system-type '(ms-dos windows-nt))
- (memq window-system '(w32 ns pgtk haiku))
+ (memq window-system '(w32 ns pgtk haiku android))
(and (eq window-system 'x)
(fboundp 'x-backspace-delete-keys-p)
(x-backspace-delete-keys-p))
@@ -10961,6 +10961,89 @@ If the buffer doesn't exist, create it first."
"Change value in PLIST of PROP to VAL, comparing with `equal'."
(declare (obsolete plist-put "29.1"))
(plist-put plist prop val #'equal))
+
+
+
+;; Text conversion support. See textconv.c for more details about
+;; what this is.
+
+
+;; Actually in textconv.c.
+(defvar text-conversion-edits)
+
+;; Actually in elec-pair.el.
+(defvar electric-pair-preserve-balance)
+(declare-function electric-pair-analyze-conversion "elec-pair.el")
+
+(defun analyze-text-conversion ()
+ "Analyze the results of the previous text conversion event.
+
+For each insertion:
+
+ - Look for the insertion of a string starting or ending with a
+ character inside `auto-fill-chars', and fill the text around
+ it if `auto-fill-mode' is enabled.
+
+ - Look for the insertion of a new line, and cause automatic
+ line breaking of the previous line when `auto-fill-mode' is
+ enabled.
+
+ - Look for the deletion of a single electric pair character,
+ and delete the adjascent pair if
+ `electric-pair-delete-adjacent-pairs'.
+
+ - Run `post-self-insert-functions' for the last character of
+ any inserted text so that modes such as `electric-pair-mode'
+ can work."
+ (interactive)
+ ;; The list must be processed in reverse.
+ (dolist (edit (reverse text-conversion-edits))
+ ;; Filter out ephemeral edits and deletions.
+ (when (and (stringp (nth 3 edit)))
+ (with-current-buffer (car edit)
+ (if (not (eq (nth 1 edit) (nth 2 edit)))
+ ;; Process this insertion. (nth 3 edit) is the text which
+ ;; was inserted.
+ (let* ((inserted (nth 3 edit))
+ ;; Get the first and last characters.
+ (start (aref inserted 0))
+ (end (aref inserted (1- (length inserted))))
+ ;; Figure out whether or not to auto-fill.
+ (auto-fill-p (or (aref auto-fill-chars start)
+ (aref auto-fill-chars end)))
+ ;; Figure out whether or not a newline was inserted.
+ (newline-p (string-search "\n" inserted))
+ ;; FIXME: this leads to an error in
+ ;; `atomic-change-group', seemingly because
+ ;; buffer-undo-list is being modified or
+ ;; prematurely truncated. Turn it off for now.
+ (electric-pair-preserve-balance nil))
+ (save-excursion
+ (if (and auto-fill-function newline-p)
+ (progn (goto-char (nth 2 edit))
+ (previous-logical-line)
+ (funcall auto-fill-function))
+ (when (and auto-fill-function auto-fill-p)
+ (progn (goto-char (nth 2 edit))
+ (funcall auto-fill-function)))))
+ (goto-char (nth 2 edit))
+ (let ((last-command-event end))
+ (run-hooks 'post-self-insert-hook)))
+ ;; Process this deletion before point. (nth 2 edit) is the
+ ;; text which was deleted. Input methods typically prefer
+ ;; to edit words instead of deleting characters off their
+ ;; ends, but they seem to always send proper requests for
+ ;; deletion for punctuation.
+ (when (and (boundp 'electric-pair-delete-adjacent-pairs)
+ (symbol-value 'electric-pair-delete-adjacent-pairs)
+ ;; Make sure elec-pair is loaded.
+ (fboundp 'electric-pair-analyze-conversion)
+ ;; Only do this if only a single edit happened.
+ text-conversion-edits)
+ (save-excursion
+ (goto-char (nth 2 edit))
+ (electric-pair-analyze-conversion (nth 3 edit)))))))))
+
(provide 'simple)
diff --git a/lisp/speedbar.el b/lisp/speedbar.el
index 0115a6f4ae4..f56c3915521 100644
--- a/lisp/speedbar.el
+++ b/lisp/speedbar.el
@@ -3531,7 +3531,7 @@ to be at the beginning of a line in the etags buffer.
This variable is ignored if `speedbar-use-imenu-flag' is non-nil.")
-(defcustom speedbar-fetch-etags-command "etags"
+(defcustom speedbar-fetch-etags-command etags-program-name
"Command used to create an etags file.
This variable is ignored if `speedbar-use-imenu-flag' is t."
:group 'speedbar
diff --git a/lisp/startup.el b/lisp/startup.el
index 9ae53f4e50b..fb14fbad17f 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -574,11 +574,24 @@ the updated value."
(setq startup--original-eln-load-path
(copy-sequence native-comp-eln-load-path))))
+(defvar android-fonts-enumerated nil
+ "Whether or not fonts have been enumerated already.
+On Android, Emacs uses this variable internally at startup.")
+
(defun normal-top-level ()
"Emacs calls this function when it first starts up.
It sets `command-line-processed', processes the command-line,
reads the initialization files, etc.
It is the default value of the variable `top-level'."
+ ;; Initialize the Android font driver late.
+ ;; This is done here because it needs the `mac-roman' coding system
+ ;; to be loaded.
+ (when (and (featurep 'android)
+ (fboundp 'android-enumerate-fonts)
+ (not android-fonts-enumerated))
+ (funcall 'android-enumerate-fonts)
+ (setq android-fonts-enumerated t))
+
(if command-line-processed
(message internal--top-level-message)
(setq command-line-processed t)
diff --git a/lisp/subr.el b/lisp/subr.el
index 03d3324f3d8..d49c9cb155e 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -1654,7 +1654,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)
@@ -1662,7 +1668,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)
@@ -3445,6 +3455,9 @@ an error message."
(minibuffer-message "Wrong answer")
(sit-for 2)))
+;; Defined in textconv.c.
+(defvar overriding-text-conversion-style)
+
(defun read-char-from-minibuffer (prompt &optional chars history)
"Read a character from the minibuffer, prompting for it with PROMPT.
Like `read-char', but uses the minibuffer to read and return a character.
@@ -3459,7 +3472,15 @@ while calling this function, then pressing `help-char'
causes it to evaluate `help-form' and display the result.
There is no need to explicitly add `help-char' to CHARS;
`help-char' is bound automatically to `help-form-show'."
- (let* ((map (if (consp chars)
+
+ ;; If text conversion is enabled in this buffer, then it will only
+ ;; be disabled the next time `force-mode-line-update' happens.
+ (when (and (bound-and-true-p overriding-text-conversion-style)
+ (bound-and-true-p text-conversion-style))
+ (force-mode-line-update))
+
+ (let* ((overriding-text-conversion-style nil)
+ (map (if (consp chars)
(or (gethash (list help-form (cons help-char chars))
read-char-from-minibuffer-map-hash)
(let ((map (make-sparse-keymap))
@@ -3471,15 +3492,15 @@ There is no need to explicitly add `help-char' to CHARS;
;; being a command char.
(when help-form
(define-key map (vector help-char)
- (lambda ()
- (interactive)
- (let ((help-form msg)) ; lexically bound msg
- (help-form-show)))))
+ (lambda ()
+ (interactive)
+ (let ((help-form msg)) ; lexically bound msg
+ (help-form-show)))))
(dolist (char chars)
(define-key map (vector char)
- #'read-char-from-minibuffer-insert-char))
+ #'read-char-from-minibuffer-insert-char))
(define-key map [remap self-insert-command]
- #'read-char-from-minibuffer-insert-other)
+ #'read-char-from-minibuffer-insert-other)
(puthash (list help-form (cons help-char chars))
map read-char-from-minibuffer-map-hash)
map))
@@ -3581,10 +3602,15 @@ confusing to some users.")
(defun use-dialog-box-p ()
"Return non-nil if the current command should prompt the user via a dialog box."
(and last-input-event ; not during startup
- (or (consp last-nonmenu-event) ; invoked by a mouse event
+ (or (featurep 'android) ; prefer dialog boxes on Android
+ (consp last-nonmenu-event) ; invoked by a mouse event
from--tty-menu-p) ; invoked via TTY menu
use-dialog-box))
+;; Actually in textconv.c.
+(defvar overriding-text-conversion-style)
+(declare-function set-text-conversion-style "textconv.c")
+
(defun y-or-n-p (prompt)
"Ask user a \"y or n\" question.
Return t if answer is \"y\" and nil if it is \"n\".
@@ -3662,6 +3688,9 @@ like) while `y-or-n-p' is running)."
(while
(let* ((scroll-actions '(recenter scroll-up scroll-down
scroll-other-window scroll-other-window-down))
+ ;; Disable text conversion so that real key events
+ ;; are sent.
+ (overriding-text-conversion-style nil)
(key
(let ((cursor-in-echo-area t))
(when minibuffer-auto-raise
@@ -3693,6 +3722,9 @@ like) while `y-or-n-p' is running)."
(setq prompt (funcall padded prompt))
(let* ((enable-recursive-minibuffers t)
(msg help-form)
+ ;; Disable text conversion so that real Y or N events are
+ ;; sent.
+ (overriding-text-conversion-style nil)
(keymap (let ((map (make-composed-keymap
y-or-n-p-map query-replace-map)))
(when help-form
@@ -3706,9 +3738,15 @@ like) while `y-or-n-p' is running)."
map))
;; Protect this-command when called from pre-command-hook (bug#45029)
(this-command this-command)
- (str (read-from-minibuffer
- prompt nil keymap nil
- (or y-or-n-p-history-variable t))))
+ (str (progn
+ (when (active-minibuffer-window)
+ ;; If the minibuffer is already active, the
+ ;; selected window might not change. Disable
+ ;; text conversion by hand.
+ (set-text-conversion-style text-conversion-style))
+ (read-from-minibuffer
+ prompt nil keymap nil
+ (or y-or-n-p-history-variable t)))))
(setq answer (if (member str '("y" "Y")) 'act 'skip)))))
(let ((ret (eq answer 'act)))
(unless noninteractive
diff --git a/lisp/term.el b/lisp/term.el
index e1392908b90..8fad9705c98 100644
--- a/lisp/term.el
+++ b/lisp/term.el
@@ -1136,6 +1136,9 @@ Entry to this mode runs the hooks on `term-mode-hook'."
(setq-local term-last-input-end (make-marker))
(setq-local term-last-input-match "")
+ ;; Always display the onscreen keyboard.
+ (setq-local touch-screen-display-keyboard t)
+
;; These local variables are set to their local values:
(make-local-variable 'term-saved-home-marker)
(make-local-variable 'term-saved-cursor)
diff --git a/lisp/term/android-win.el b/lisp/term/android-win.el
new file mode 100644
index 00000000000..d425ea401a9
--- /dev/null
+++ b/lisp/term/android-win.el
@@ -0,0 +1,237 @@
+;;; x-win.el --- parse relevant switches and set up for Android -*- lexical-binding:t -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; Author: FSF
+;; Keywords: terminals, i18n, android
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file contains the support for initializing the Lisp side of
+;; Android windowing.
+
+;;; Code:
+
+
+(unless (featurep 'android)
+ (error "%s: Loading android-win without having Android"
+ invocation-name))
+
+;; Documentation-purposes only: actually loaded in loadup.el.
+(require 'frame)
+(require 'mouse)
+(require 'fontset)
+(require 'dnd)
+(require 'touch-screen)
+
+(add-to-list 'display-format-alist '(".*" . android))
+
+(declare-function android-get-connection "androidfns.c")
+
+;; Window system initialization. This is extremely simple because all
+;; initialization is done in android_term_init.
+
+(cl-defmethod window-system-initialization (&context (window-system android)
+ &optional _ignored)
+ "Set up the window system. WINDOW-SYSTEM must be ANDROID.
+DISPLAY is ignored on Android."
+ ;; Create the default fontset.
+ (create-default-fontset)
+ ;; Just make sure the window system was initialized at startup.
+ (android-get-connection))
+
+(cl-defmethod frame-creation-function (params &context (window-system android))
+ (x-create-frame-with-faces params))
+
+(cl-defmethod handle-args-function (args &context (window-system android))
+ ;; Android has no command line to provide arguments on.
+ ;; However, call x-handle-args to handle file name args.
+ (x-handle-args args))
+
+
+;;; Selection support.
+
+(declare-function android-clipboard-exists-p "androidselect.c")
+(declare-function android-get-clipboard "androidselect.c")
+(declare-function android-set-clipboard "androidselect.c")
+(declare-function android-clipboard-owner-p "androidselect.c")
+(declare-function android-get-clipboard-targets "androidselect.c")
+(declare-function android-get-clipboard-data "androidselect.c")
+
+(defvar android-primary-selection nil
+ "The last string placed in the primary selection.
+Nil if there was no such string.
+
+Android does not have a primary selection of its own, so Emacs
+emulates one inside Lisp.")
+
+(defun android-get-clipboard-1 (data-type)
+ "Return the clipboard data.
+DATA-TYPE is a selection conversion target. `STRING' means to
+return the contents of the clipboard as a string. `TARGETS'
+means to return supported data types as a vector.
+
+Interpret any other symbol as a MIME type, and return its
+corresponding data."
+ (or (and (eq data-type 'STRING)
+ (android-get-clipboard))
+ (and (eq data-type 'TARGETS)
+ (android-clipboard-exists-p)
+ (vconcat [TARGETS STRING]
+ (let ((i nil))
+ (dolist (type (android-get-clipboard-targets))
+ ;; Don't report plain text as a valid target.
+ (unless (equal type "text/plain")
+ (push (intern type) i)))
+ (nreverse i))))
+ (and (symbolp data-type)
+ (android-get-clipboard-data (symbol-name data-type)))))
+
+(defun android-get-primary (data-type)
+ "Return the last string placed in the primary selection, or nil.
+Return nil if DATA-TYPE is anything other than STRING or TARGETS."
+ (when android-primary-selection
+ (or (and (eq data-type 'STRING)
+ android-primary-selection)
+ (and (eq data-type 'TARGETS)
+ [TARGETS]))))
+
+(defun android-selection-bounds (value)
+ "Return bounds of selection value VALUE.
+The return value is a list (BEG END BUF) if VALUE is a cons of
+two markers or an overlay. Otherwise, it is nil."
+ (cond ((bufferp value)
+ (with-current-buffer value
+ (when (mark t)
+ (list (mark t) (point) value))))
+ ((and (consp value)
+ (markerp (car value))
+ (markerp (cdr value)))
+ (when (and (marker-buffer (car value))
+ (buffer-name (marker-buffer (car value)))
+ (eq (marker-buffer (car value))
+ (marker-buffer (cdr value))))
+ (list (marker-position (car value))
+ (marker-position (cdr value))
+ (marker-buffer (car value)))))
+ ((overlayp value)
+ (when (overlay-buffer value)
+ (list (overlay-start value)
+ (overlay-end value)
+ (overlay-buffer value))))))
+
+(defun android-encode-select-string (value)
+ "Turn VALUE into a string suitable for placing in the clipboard.
+VALUE should be something suitable for passing to
+`gui-set-selection'."
+ (unless (stringp value)
+ (when-let ((bounds (android-selection-bounds value)))
+ (setq value (ignore-errors
+ (with-current-buffer (nth 2 bounds)
+ (buffer-substring (nth 0 bounds)
+ (nth 1 bounds)))))))
+ value)
+
+(cl-defmethod gui-backend-get-selection (type data-type
+ &context (window-system android))
+ (cond ((eq type 'CLIPBOARD)
+ (android-get-clipboard-1 data-type))
+ ((eq type 'PRIMARY)
+ (android-get-primary data-type))))
+
+(cl-defmethod gui-backend-selection-exists-p (selection
+ &context (window-system android))
+ (cond ((eq selection 'CLIPBOARD)
+ (android-clipboard-exists-p))
+ ((eq selection 'PRIMARY)
+ (not (null android-primary-selection)))))
+
+(cl-defmethod gui-backend-selection-owner-p (selection
+ &context (window-system android))
+ (cond ((eq selection 'CLIPBOARD)
+ (let ((ownership (android-clipboard-owner-p)))
+ ;; If ownership is `lambda', then Emacs couldn't determine
+ ;; whether or not it owns the clipboard.
+ (and (not (eq ownership 'lambda)) ownership)))
+ ((eq selection 'PRIMARY)
+ ;; Emacs always owns its own primary selection as long as it
+ ;; exists.
+ (not (null android-primary-selection)))))
+
+(cl-defmethod gui-backend-set-selection (type value
+ &context (window-system android))
+ ;; First, try to turn value into a string.
+ ;; Don't set anything if that did not work.
+ (when-let ((string (android-encode-select-string value)))
+ (cond ((eq type 'CLIPBOARD)
+ (android-set-clipboard string))
+ ((eq type 'PRIMARY)
+ (setq android-primary-selection string)))))
+
+;;; Character composition display.
+
+(defvar android-preedit-overlay nil
+ "The overlay currently used to display preedit text from a compose sequence.")
+
+;; With some input methods, text gets inserted before Emacs is told to
+;; remove any preedit text that was displayed, which causes both the
+;; preedit overlay and the text to be visible for a brief period of
+;; time. This pre-command-hook clears the overlay before any command
+;; and should be set whenever a preedit overlay is visible.
+(defun android-clear-preedit-text ()
+ "Clear the pre-edit overlay and remove itself from pre-command-hook.
+This function should be installed in `pre-command-hook' whenever
+preedit text is displayed."
+ (when android-preedit-overlay
+ (delete-overlay android-preedit-overlay)
+ (setq android-preedit-overlay nil))
+ (remove-hook 'pre-command-hook #'android-clear-preedit-text))
+
+(defun android-preedit-text (event)
+ "Display preedit text from a compose sequence in EVENT.
+EVENT is a preedit-text event."
+ (interactive "e")
+ (when android-preedit-overlay
+ (delete-overlay android-preedit-overlay)
+ (setq android-preedit-overlay nil)
+ (remove-hook 'pre-command-hook #'android-clear-preedit-text))
+ (when (nth 1 event)
+ (let ((string (propertize (nth 1 event) 'face '(:underline t))))
+ (setq android-preedit-overlay (make-overlay (point) (point)))
+ (add-hook 'pre-command-hook #'android-clear-preedit-text)
+ (overlay-put android-preedit-overlay 'window (selected-window))
+ (overlay-put android-preedit-overlay 'before-string string))))
+
+(define-key special-event-map [preedit-text] 'android-preedit-text)
+
+
+;; Android cursor shapes, named according to the X scheme.
+;; Many X cursors are missing.
+
+(defconst x-pointer-arrow 1000)
+(defconst x-pointer-left-ptr 1000)
+(defconst x-pointer-left-side 1020)
+(defconst x-pointer-sb-h-double-arrow 1014)
+(defconst x-pointer-sb-v-double-arrow 1015)
+(defconst x-pointer-watch 1004)
+(defconst x-pointer-xterm 1008)
+(defconst x-pointer-invisible 0)
+
+
+(provide 'android-win)
+;; android-win.el ends here.
diff --git a/lisp/textmodes/reftex-global.el b/lisp/textmodes/reftex-global.el
index acf0891432f..b8b0ae6a061 100644
--- a/lisp/textmodes/reftex-global.el
+++ b/lisp/textmodes/reftex-global.el
@@ -39,8 +39,10 @@ The TAGS file is also immediately visited with `visit-tags-table'."
(reftex-access-scan-info current-prefix-arg)
(let* ((master (reftex-TeX-master-file))
(files (reftex-all-document-files))
- (cmd (format "etags %s" (mapconcat #'shell-quote-argument
- files " "))))
+ (cmd (format "%s %s"
+ etags-program-name
+ (mapconcat #'shell-quote-argument
+ files " "))))
(with-current-buffer (reftex-get-file-buffer-force master)
(message "Running etags to create TAGS file...")
(shell-command cmd)
diff --git a/lisp/textmodes/text-mode.el b/lisp/textmodes/text-mode.el
index 48cefc74d06..ccba1b063ab 100644
--- a/lisp/textmodes/text-mode.el
+++ b/lisp/textmodes/text-mode.el
@@ -41,6 +41,9 @@
"Non-nil if this buffer's major mode is a variant of Text mode.")
(make-obsolete-variable 'text-mode-variant 'derived-mode-p "27.1")
+;; Actually defined in textconv.c.
+(defvar text-conversion-style)
+
(defvar text-mode-syntax-table
(let ((st (make-syntax-table)))
(modify-syntax-entry ?\" ". " st)
@@ -125,6 +128,9 @@ You can thus get the full benefit of adaptive filling
Turning on Text mode runs the normal hook `text-mode-hook'."
(setq-local text-mode-variant t)
(setq-local require-final-newline mode-require-final-newline)
+
+ ;; Enable text conversion in this buffer.
+ (setq-local text-conversion-style t)
(add-hook 'context-menu-functions 'text-mode-context-menu 10 t))
(define-derived-mode paragraph-indent-text-mode text-mode "Parindent"
diff --git a/lisp/touch-screen.el b/lisp/touch-screen.el
new file mode 100644
index 00000000000..31d46b062ed
--- /dev/null
+++ b/lisp/touch-screen.el
@@ -0,0 +1,667 @@
+;;; touch-screen.el --- touch screen support for X and Android -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2023 Free Software Foundation, Inc.
+
+;; Maintainer: emacs-devel@gnu.org
+;; Package: emacs
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file provides code to recognize simple touch screen gestures.
+;; It is used on X and Android, where the platform cannot recognize
+;; them for us.
+
+;;; Code:
+
+(defvar touch-screen-current-tool nil
+ "The touch point currently being tracked, or nil.
+If non-nil, this is a list of nine elements: the ID of the touch
+point being tracked, the window where the touch began, a cons
+containing the last known position of the touch point, relative
+to that window, a field used to store data while tracking the
+touch point, the initial position of the touchpoint, and another
+four fields to used store data while tracking the touch point.
+See `touch-screen-handle-point-update' for the meanings of the
+fourth element.")
+
+(defvar touch-screen-set-point-commands '(mouse-set-point)
+ "List of commands known to set the point.
+This is used to determine whether or not to display the on-screen
+keyboard after a mouse command is executed in response to a
+`touchscreen-end' event.")
+
+(defvar touch-screen-current-timer nil
+ "Timer used to track long-presses.
+This is always cleared upon any significant state change.")
+
+(defcustom touch-screen-display-keyboard nil
+ "If non-nil, always display the on screen keyboard.
+A buffer local value means to always display the on screen
+keyboard when the buffer is selected."
+ :type 'boolean
+ :group 'mouse
+ :version "30.1")
+
+(defcustom touch-screen-delay 0.7
+ "Delay in seconds before Emacs considers a touch to be a long-press."
+ :type 'number
+ :group 'mouse
+ :version "30.1")
+
+(defcustom touch-screen-precision-scroll nil
+ "Whether or not to use precision scrolling for touch screens.
+See `pixel-scroll-precision-mode' for more details."
+ :type 'boolean
+ :group 'mouse
+ :version "30.1")
+
+(defun touch-screen-relative-xy (posn window)
+ "Return the coordinates of POSN, a mouse position list.
+However, return the coordinates relative to WINDOW.
+
+If (posn-window posn) is the same as window, simply return the
+coordinates in POSN. Otherwise, convert them to the frame, and
+then back again.
+
+If WINDOW is the symbol `frame', simply convert the coordinates
+to the frame that they belong in."
+ (if (or (eq (posn-window posn) window)
+ (and (eq window 'frame)
+ (framep (posn-window posn))))
+ (posn-x-y posn)
+ (let ((xy (posn-x-y posn))
+ (edges (and (windowp window)
+ (window-inside-pixel-edges window))))
+ ;; Make the X and Y positions frame relative.
+ (when (windowp (posn-window posn))
+ (let ((edges (window-inside-pixel-edges
+ (posn-window posn))))
+ (setq xy (cons (+ (car xy) (car edges))
+ (+ (cdr xy) (cadr edges))))))
+ (if (eq window 'frame)
+ xy
+ ;; Make the X and Y positions window relative again.
+ (cons (- (car xy) (car edges))
+ (- (cdr xy) (cadr edges)))))))
+
+(defun touch-screen-handle-scroll (dx dy)
+ "Scroll the display assuming that a touch point has moved by DX and DY.
+Perform vertical scrolling by DY, using `pixel-scroll-precision'
+if `touch-screen-precision-scroll' is enabled. Next, perform
+horizontal scrolling according to the movement in DX."
+ ;; Perform vertical scrolling first. Do not ding at buffer limits.
+ ;; Show a message instead.
+ (condition-case nil
+ (if touch-screen-precision-scroll
+ (if (> dy 0)
+ (pixel-scroll-precision-scroll-down-page dy)
+ (pixel-scroll-precision-scroll-up-page (- dy)))
+ ;; Start conventional scrolling. First, determine the
+ ;; direction in which the scrolling is taking place. Load the
+ ;; accumulator value.
+ (let ((accumulator (or (nth 5 touch-screen-current-tool) 0))
+ (window (cadr touch-screen-current-tool))
+ (lines-vscrolled (or (nth 7 touch-screen-current-tool) 0)))
+ (setq accumulator (+ accumulator dy)) ; Add dy.
+ ;; Figure out how much it has scrolled and how much remains
+ ;; on the top or bottom of the window.
+ (while (catch 'again
+ (let* ((line-height (window-default-line-height window)))
+ (if (and (< accumulator 0)
+ (>= (- accumulator) line-height))
+ (progn
+ (setq accumulator (+ accumulator line-height))
+ (scroll-down 1)
+ (setq lines-vscrolled (1+ lines-vscrolled))
+ (when (not (zerop accumulator))
+ ;; If there is still an outstanding
+ ;; amount to scroll, do this again.
+ (throw 'again t)))
+ (when (and (> accumulator 0)
+ (>= accumulator line-height))
+ (setq accumulator (- accumulator line-height))
+ (scroll-up 1)
+ (setq lines-vscrolled (1+ lines-vscrolled))
+ (when (not (zerop accumulator))
+ ;; If there is still an outstanding amount
+ ;; to scroll, do this again.
+ (throw 'again t)))))
+ ;; Scrolling is done. Move the accumulator back to
+ ;; touch-screen-current-tool and break out of the
+ ;; loop.
+ (setcar (nthcdr 5 touch-screen-current-tool) accumulator)
+ (setcar (nthcdr 7 touch-screen-current-tool)
+ lines-vscrolled)
+ nil))))
+ (beginning-of-buffer
+ (message (error-message-string '(beginning-of-buffer))))
+ (end-of-buffer
+ (message (error-message-string '(end-of-buffer)))))
+
+ ;; Perform horizontal scrolling by DX, as this does not signal at
+ ;; the beginning of the buffer.
+ (let ((accumulator (or (nth 6 touch-screen-current-tool) 0))
+ (window (cadr touch-screen-current-tool))
+ (lines-vscrolled (or (nth 7 touch-screen-current-tool) 0))
+ (lines-hscrolled (or (nth 8 touch-screen-current-tool) 0)))
+ (setq accumulator (+ accumulator dx)) ; Add dx;
+ ;; Figure out how much it has scrolled and how much remains on the
+ ;; left or right of the window. If a line has already been
+ ;; vscrolled but no hscrolling has happened, don't hscroll, as
+ ;; otherwise it is too easy to hscroll by accident.
+ (if (or (> lines-hscrolled 0)
+ (< lines-vscrolled 1))
+ (while (catch 'again
+ (let* ((column-width (frame-char-width (window-frame window))))
+ (if (and (< accumulator 0)
+ (>= (- accumulator) column-width))
+ (progn
+ (setq accumulator (+ accumulator column-width))
+ (scroll-right 1)
+ (setq lines-hscrolled (1+ lines-hscrolled))
+ (when (not (zerop accumulator))
+ ;; If there is still an outstanding amount to
+ ;; scroll, do this again.
+ (throw 'again t)))
+ (when (and (> accumulator 0)
+ (>= accumulator column-width))
+ (setq accumulator (- accumulator column-width))
+ (scroll-left 1)
+ (setq lines-hscrolled (1+ lines-hscrolled))
+ (when (not (zerop accumulator))
+ ;; If there is still an outstanding amount to
+ ;; scroll, do this again.
+ (throw 'again t)))))
+ ;; Scrolling is done. Move the accumulator back to
+ ;; touch-screen-current-tool and break out of the loop.
+ (setcar (nthcdr 6 touch-screen-current-tool) accumulator)
+ (setcar (nthcdr 8 touch-screen-current-tool) lines-hscrolled)
+ nil)))))
+
+(defun touch-screen-handle-timeout (arg)
+ "Start the touch screen timeout or handle it depending on ARG.
+When ARG is nil, start the `touch-screen-current-timer' to go off
+in `touch-screen-delay' seconds, and call this function with ARG
+t.
+
+When ARG is t, beep. Then, set the fourth element of
+touch-screen-current-tool to `held', and the mark to the last
+known position of the tool."
+ (if (not arg)
+ ;; Cancel the touch screen long-press timer, if it is still
+ ;; there by any chance.
+ (progn
+ (when touch-screen-current-timer
+ (cancel-timer touch-screen-current-timer))
+ (setq touch-screen-current-timer
+ (run-at-time touch-screen-delay nil
+ #'touch-screen-handle-timeout
+ t)))
+ ;; Beep.
+ (beep)
+ ;; Set touch-screen-current-timer to nil.
+ (setq touch-screen-current-timer nil)
+ (when touch-screen-current-tool
+ ;; Set the state to `held'.
+ (setcar (nthcdr 3 touch-screen-current-tool) 'held)
+ ;; Go to the initial position of the touchpoint and activate the
+ ;; 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.
+POINT must be the touch point currently being tracked as
+`touch-screen-current-tool'.
+
+If the fourth element of `touch-screen-current-tool' is nil, then
+the touch has just begun. Determine how much POINT has moved.
+If POINT has moved upwards or downwards by a significant amount,
+then set the fourth element to `scroll'. Then, call
+`touch-screen-handle-scroll' to scroll the display by that
+amount.
+
+If the fourth element of `touch-screen-current-tool' is `scroll',
+then scroll the display by how much POINT has moved in the Y
+axis.
+
+If the fourth element of `touch-screen-current-tool' is `held',
+then the touch has been held down for some time. If motion
+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."
+ (let ((window (nth 1 touch-screen-current-tool))
+ (what (nth 3 touch-screen-current-tool)))
+ (cond ((null what)
+ (let* ((posn (cdr point))
+ (last-posn (nth 2 touch-screen-current-tool))
+ ;; Now get the position of X and Y relative to
+ ;; WINDOW.
+ (relative-xy
+ (touch-screen-relative-xy posn window))
+ (diff-x (- (car last-posn) (car relative-xy)))
+ (diff-y (- (cdr last-posn) (cdr relative-xy))))
+ ;; Decide whether or not to start scrolling.
+ (when (or (> diff-y 10) (> diff-x 10)
+ (< diff-y -10) (< diff-x -10))
+ (setcar (nthcdr 3 touch-screen-current-tool)
+ 'scroll)
+ (setcar (nthcdr 2 touch-screen-current-tool)
+ relative-xy)
+ (with-selected-window window
+ (touch-screen-handle-scroll diff-x diff-y))
+ ;; Cancel the touch screen long-press timer, if it is
+ ;; still there by any chance.
+ (when touch-screen-current-timer
+ (cancel-timer touch-screen-current-timer)
+ (setq touch-screen-current-timer nil)))))
+ ((eq what 'scroll)
+ ;; Cancel the touch screen long-press timer, if it is still
+ ;; there by any chance.
+ (when touch-screen-current-timer
+ (cancel-timer touch-screen-current-timer)
+ (setq touch-screen-current-timer nil))
+ (let* ((posn (cdr point))
+ (last-posn (nth 2 touch-screen-current-tool))
+ ;; Now get the position of X and Y relative to
+ ;; WINDOW.
+ (relative-xy
+ (touch-screen-relative-xy posn window))
+ (diff-x (- (car last-posn) (car relative-xy)))
+ (diff-y (- (cdr last-posn) (cdr relative-xy))))
+ (setcar (nthcdr 3 touch-screen-current-tool)
+ 'scroll)
+ (setcar (nthcdr 2 touch-screen-current-tool)
+ relative-xy)
+ (unless (and (zerop diff-x) (zerop diff-y))
+ (with-selected-window window
+ (touch-screen-handle-scroll diff-x diff-y)))))
+ ((eq what 'held)
+ (let* ((posn (cdr point))
+ (relative-xy
+ (touch-screen-relative-xy posn window)))
+ (when touch-screen-current-timer
+ (cancel-timer touch-screen-current-timer)
+ (setq touch-screen-current-timer nil))
+ ;; Now start dragging.
+ (setcar (nthcdr 3 touch-screen-current-tool)
+ 'drag)
+ (setcar (nthcdr 2 touch-screen-current-tool)
+ relative-xy)
+ (with-selected-window window
+ ;; Activate the mark. It should have been set by the
+ ;; time `touch-screen-timeout' was called.
+ (activate-mark)
+
+ ;; Figure out what character to go to. If this posn is
+ ;; in the window, go to (posn-point posn). If not,
+ ;; then go to the line before either window start or
+ ;; window end.
+ (if (and (eq (posn-window posn) window)
+ (posn-point posn))
+ (goto-char (posn-point posn))
+ (let ((relative-xy
+ (touch-screen-relative-xy posn window)))
+ (let ((scroll-conservatively 101))
+ (cond
+ ((< (cdr relative-xy) 0)
+ (ignore-errors
+ (goto-char (1- (window-start))))
+ (redisplay))
+ ((> (cdr relative-xy)
+ (let ((edges (window-inside-pixel-edges)))
+ (- (nth 3 edges) (cadr edges))))
+ (ignore-errors
+ (goto-char (1+ (window-end nil t))))
+ (redisplay)))))))))
+ ((eq what 'drag)
+ (let* ((posn (cdr point)))
+ ;; Keep dragging.
+ (with-selected-window window
+ ;; Figure out what character to go to. If this posn is
+ ;; in the window, go to (posn-point posn). If not,
+ ;; then go to the line before either window start or
+ ;; window end.
+ (if (and (eq (posn-window posn) window)
+ (posn-point posn))
+ (goto-char (posn-point posn))
+ (let ((relative-xy
+ (touch-screen-relative-xy posn window)))
+ (let ((scroll-conservatively 101))
+ (cond
+ ((< (cdr relative-xy) 0)
+ (ignore-errors
+ (goto-char (1- (window-start))))
+ (redisplay))
+ ((> (cdr relative-xy)
+ (let ((edges (window-inside-pixel-edges)))
+ (- (nth 3 edges) (cadr edges))))
+ (ignore-errors
+ (goto-char (1+ (window-end nil t))))
+ (redisplay))))))))))))
+
+(defun touch-screen-window-selection-changed (frame)
+ "Notice that FRAME's selected window has changed.
+If point is now on read only text, hide the on screen keyboard.
+Otherwise, cancel any timer that is supposed to hide the keyboard
+in response to the minibuffer being closed."
+ (with-selected-frame frame
+ (if (or buffer-read-only
+ (get-text-property (point) 'read-only))
+ (frame-toggle-on-screen-keyboard (selected-frame) t)
+ ;; Prevent hiding the minibuffer from hiding the on screen
+ ;; keyboard.
+ (when minibuffer-on-screen-keyboard-timer
+ (cancel-timer minibuffer-on-screen-keyboard-timer)
+ (setq minibuffer-on-screen-keyboard-timer nil)))))
+
+(defun touch-screen-handle-point-up (point)
+ "Notice that POINT has been removed from the screen.
+POINT should be the point currently tracked as
+`touch-screen-current-tool'.
+
+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, 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'.
+
+If the command being executed is listed in
+`touch-screen-set-point-commands' also display the on-screen
+keyboard if the current buffer and the character at the new point
+is not read-only."
+ (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)))
+ ;; 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)))
+ ;; Figure out if the on screen keyboard needs to be
+ ;; displayed.
+ (when command
+ (call-interactively command nil
+ (vector event))
+ (when (memq command touch-screen-set-point-commands)
+ (if (and (or (not buffer-read-only)
+ touch-screen-display-keyboard)
+ ;; Detect the splash screen and avoid
+ ;; displaying the on screen keyboard
+ ;; there.
+ (not (equal (buffer-name) "*GNU Emacs*")))
+ ;; Once the on-screen keyboard has been opened,
+ ;; add `touch-screen-window-selection-changed'
+ ;; as a window selection change function This
+ ;; allows the on screen keyboard to be hidden
+ ;; if the selected window's point becomes read
+ ;; only at some point in the future.
+ (progn
+ (add-hook 'window-selection-change-functions
+ #'touch-screen-window-selection-changed)
+ (frame-toggle-on-screen-keyboard (selected-frame) nil))
+ ;; Otherwise, hide the on screen keyboard now.
+ (frame-toggle-on-screen-keyboard (selected-frame) t))))))))))
+
+(defun touch-screen-handle-touch (event)
+ "Handle a single touch EVENT, and perform associated actions.
+EVENT can either be a touchscreen-begin, touchscreen-update or
+touchscreen-end event."
+ (interactive "e")
+ (cond
+ ((eq (car event) 'touchscreen-begin)
+ ;; A tool was just pressed against the screen. Figure out the
+ ;; window where it is and make it the tool being tracked on the
+ ;; window.
+ (let ((touchpoint (caadr event))
+ (position (cdadr event)))
+ ;; Cancel the touch screen timer, if it is still there by any
+ ;; chance.
+ (when touch-screen-current-timer
+ (cancel-timer touch-screen-current-timer)
+ (setq touch-screen-current-timer nil))
+ ;; Replace any previously ongoing gesture. If POSITION has no
+ ;; window or position, make it nil instead.
+ (setq touch-screen-current-tool (and (windowp (posn-window position))
+ (posn-point position)
+ (list touchpoint
+ (posn-window position)
+ (posn-x-y position)
+ nil position nil nil
+ nil nil)))
+ ;; Start the long-press timer.
+ (touch-screen-handle-timeout nil)))
+ ((eq (car event) 'touchscreen-update)
+ ;; The positions of tools currently pressed against the screen
+ ;; have changed. If there is a tool being tracked as part of a
+ ;; gesture, look it up in the list of tools.
+ (let ((new-point (assq (car touch-screen-current-tool)
+ (cadr event))))
+ (when new-point
+ (touch-screen-handle-point-update new-point))))
+ ((eq (car event) 'touchscreen-end)
+ ;; A tool has been removed from the screen. If it is the tool
+ ;; currently being tracked, clear `touch-screen-current-tool'.
+ (when (eq (caadr event) (car touch-screen-current-tool))
+ ;; Cancel the touch screen long-press timer, if it is still there
+ ;; by any chance.
+ (when touch-screen-current-timer
+ (cancel-timer touch-screen-current-timer)
+ (setq touch-screen-current-timer nil))
+ (touch-screen-handle-point-up (cadr event))
+ (setq touch-screen-current-tool nil)))))
+
+(define-key global-map [touchscreen-begin] #'touch-screen-handle-touch)
+(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."
+ (let ((disable-inhibit-text-conversion t))
+ (catch 'finish
+ (while t
+ (let ((new-event (read-event nil)))
+ (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, once the touch point has moved
+significantly by at least 5 pixels from where it was in EVENT.
+
+Return nil immediately if any other kind of event is received;
+otherwise, return either t or `no-drag' once the
+`touchscreen-end' event arrives; return `no-drag' returned if the
+touch point in EVENT did not move significantly, and t otherwise."
+ (let ((return-value 'no-drag)
+ (start-xy (touch-screen-relative-xy (cdadr event)
+ 'frame))
+ (disable-inhibit-text-conversion t))
+ (catch 'finish
+ (while t
+ (let ((new-event (read-event nil)))
+ (cond
+ ((eq (car-safe new-event) 'touchscreen-update)
+ (when-let* ((tool (assq (caadr event) (nth 1 new-event)))
+ (xy (touch-screen-relative-xy (cdr tool) 'frame)))
+ (when (or (> (- (car xy) (car start-xy)) 5)
+ (< (- (car xy) (car start-xy)) -5)
+ (> (- (cdr xy) (cdr start-xy)) 5)
+ (< (- (cdr xy) (cdr start-xy)) -5))
+ (setq return-value t))
+ (when (and update tool (eq return-value t))
+ (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.
+ (and (eq (caadr event) (caadr new-event))
+ return-value)))
+ (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 that no drag really
+happened. EVENT is the same as in `touch-screen-drag-mode-line'."
+ ;; 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.
+ ;; Also, select the window in EVENT.
+ (select-window (posn-window (cdadr event)))
+ (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)))
+ (when (or (keymapp keymap) command)
+ (if (keymapp keymap)
+ (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 (commandp command)
+ (call-interactively command nil
+ (vector (list 'mouse-1 (cdadr event)))))))))
+
+(defun touch-screen-drag-mode-line (event)
+ "Begin dragging the mode line in response to a touch EVENT.
+Change the height of the window based on where the touch point in
+EVENT moves.
+
+If it does not actually move anywhere and the touch point is
+removed, and EVENT lies on top of text with a mouse command
+bound, run that command instead."
+ (interactive "e")
+ ;; 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)
+ 'frame))
+ (last-position (cdr relative-xy)))
+ (when (window-resizable window 0)
+ (when (eq
+ (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) 'frame))
+ (position (cdr new-relative-xy))
+ (window-resize-pixelwise t)
+ 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)))))
+ (when (ignore-errors
+ (adjust-window-trailing-edge window growth nil t) t)
+ (setq last-position position))))))
+ 'no-drag)
+ ;; Dragging did not actually happen, so try to run any command
+ ;; necessary.
+ (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/version.el b/lisp/version.el
index 9cadc59237f..ca61f8cfeee 100644
--- a/lisp/version.el
+++ b/lisp/version.el
@@ -26,6 +26,31 @@
;;; Code:
+
+
+(defun android-read-build-system ()
+ "Obtain the host name of the system on which Emacs was built.
+Use the data stored in the special file `/assets/build_info'.
+Value is the string ``Unknown'' upon failure, else the hostname
+of the build system."
+ (with-temp-buffer
+ (insert-file-contents "/assets/build_info")
+ (let ((string (buffer-substring 1 (line-end-position))))
+ (and (not (equal string "Unknown")) string))))
+
+(defun android-read-build-time ()
+ "Obtain the time at which Emacs was built.
+Use the data stored in the special file `/assets/build_info'.
+Value is nil upon failure, else the time in the same format as
+returned by `current-time'."
+ (with-temp-buffer
+ (insert-file-contents "/assets/build_info")
+ (end-of-line)
+ (let ((number (read (current-buffer))))
+ (time-convert number 'list))))
+
+
+
(defconst emacs-major-version
(progn (string-match "^[0-9]+" emacs-version)
(string-to-number (match-string 0 emacs-version)))
@@ -36,10 +61,15 @@
(string-to-number (match-string 1 emacs-version)))
"Minor version number of this version of Emacs.")
-(defconst emacs-build-system (system-name)
+(defconst emacs-build-system (or (and (eq system-type 'android)
+ (android-read-build-system))
+ (system-name))
"Name of the system on which Emacs was built, or nil if not available.")
-(defconst emacs-build-time (if emacs-build-system (current-time))
+(defconst emacs-build-time (if emacs-build-system
+ (or (and (eq system-type 'android)
+ (android-read-build-time))
+ (current-time)))
"Time at which Emacs was dumped out, or nil if not available.")
(defconst emacs-build-number 1 ; loadup.el may increment this
@@ -130,9 +160,22 @@ or if we could not determine the revision.")
(looking-at "[[:xdigit:]]\\{40\\}"))
(match-string 0)))))
+(defun emacs-repository-version-android ()
+ "Return the Emacs repository revision Emacs was built from.
+Value is nil if Emacs was not built from a repository checkout.
+Use information from the `/assets/version' special file."
+ (with-temp-buffer
+ (insert-file-contents "/assets/version")
+ (let ((string (buffer-substring 1 (line-end-position))))
+ (and (not (equal string "Unknown")) string))))
+
(defun emacs-repository-get-version (&optional dir _external)
"Try to return as a string the repository revision of the Emacs sources.
The format of the returned string is dependent on the VCS in use.
+
+If Emacs is built for Android, use the version information
+embedded in the Emacs installation package.
+
Value is nil if the sources do not seem to be under version
control, or if we could not determine the revision. Note that
this reports on the current state of the sources, which may not
@@ -140,13 +183,27 @@ correspond to the running Emacs.
Optional argument DIR is a directory to use instead of `source-directory'.
Optional argument EXTERNAL is ignored."
- (emacs-repository-version-git (or dir source-directory)))
+ (cond ((eq system-type 'android)
+ (emacs-repository-version-android))
+ (t (emacs-repository-version-git
+ (or dir source-directory)))))
(defvar emacs-repository-branch nil
"String giving the repository branch from which this Emacs was built.
Value is nil if Emacs was not built from a repository checkout,
or if we could not determine the branch.")
+(defun emacs-repository-branch-android ()
+ "Return the Emacs repository branch Emacs was built from.
+Value is nil if Emacs was not built from a repository checkout.
+Use information from the `/assets/version' special file."
+ (with-temp-buffer
+ (insert-file-contents "/assets/version")
+ (end-of-line)
+ (forward-char)
+ (let ((string (buffer-substring (point) (line-end-position))))
+ (and (not (equal string "Unknown")) string))))
+
(defun emacs-repository-branch-git (dir)
"Ask git itself for the branch information for directory DIR."
(message "Waiting for git...")
@@ -162,12 +219,19 @@ or if we could not determine the branch.")
(defun emacs-repository-get-branch (&optional dir)
"Try to return as a string the repository branch of the Emacs sources.
The format of the returned string is dependent on the VCS in use.
+
+If Emacs is built for Android, use the version information
+embedded in the Emacs installation package.
+
Value is nil if the sources do not seem to be under version
control, or if we could not determine the branch. Note that
this reports on the current state of the sources, which may not
correspond to the running Emacs.
Optional argument DIR is a directory to use instead of `source-directory'."
- (emacs-repository-branch-git (or dir source-directory)))
+ (cond ((eq system-type 'android)
+ (emacs-repository-branch-android))
+ (t (emacs-repository-branch-git
+ (or dir source-directory)))))
;;; version.el ends here
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index cafd0ad0a4d..14864b5ac86 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/asm-underscore.m4 b/m4/asm-underscore.m4
new file mode 100644
index 00000000000..65ae55a75fd
--- /dev/null
+++ b/m4/asm-underscore.m4
@@ -0,0 +1,83 @@
+# asm-underscore.m4 serial 5
+dnl Copyright (C) 2010-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible. Based on as-underscore.m4 in GNU clisp.
+
+# gl_ASM_SYMBOL_PREFIX
+# Tests for the prefix of C symbols at the assembly language level and the
+# linker level. This prefix is either an underscore or empty. Defines the
+# C macro USER_LABEL_PREFIX to this prefix, and sets ASM_SYMBOL_PREFIX to
+# a stringified variant of this prefix.
+
+AC_DEFUN([gl_ASM_SYMBOL_PREFIX],
+[
+ AC_REQUIRE([AC_PROG_EGREP])
+ dnl We don't use GCC's __USER_LABEL_PREFIX__ here, because
+ dnl 1. It works only for GCC.
+ dnl 2. It is incorrectly defined on some platforms, in some GCC versions.
+ AC_REQUIRE([gl_C_ASM])
+ AC_CACHE_CHECK(
+ [whether C symbols are prefixed with underscore at the linker level],
+ [gl_cv_prog_as_underscore],
+ [cat > conftest.c <<EOF
+#ifdef __cplusplus
+extern "C" int foo (void);
+#endif
+int foo(void) { return 0; }
+EOF
+ # Look for the assembly language name in the .s file.
+ AC_TRY_COMMAND(${CC-cc} $CFLAGS $CPPFLAGS $gl_c_asm_opt conftest.c) >/dev/null 2>&1
+ if LC_ALL=C $EGREP '(^|[[^a-zA-Z0-9_]])_foo([[^a-zA-Z0-9_]]|$)' conftest.$gl_asmext >/dev/null; then
+ gl_cv_prog_as_underscore=yes
+ else
+ gl_cv_prog_as_underscore=no
+ fi
+ rm -f conftest*
+ ])
+ if test $gl_cv_prog_as_underscore = yes; then
+ USER_LABEL_PREFIX=_
+ else
+ USER_LABEL_PREFIX=
+ fi
+ AC_DEFINE_UNQUOTED([USER_LABEL_PREFIX], [$USER_LABEL_PREFIX],
+ [Define to the prefix of C symbols at the assembler and linker level,
+ either an underscore or empty.])
+ ASM_SYMBOL_PREFIX='"'${USER_LABEL_PREFIX}'"'
+ AC_SUBST([ASM_SYMBOL_PREFIX])
+])
+
+# gl_C_ASM
+# Determines how to produce an assembly language file from C source code.
+# Sets the variables:
+# gl_asmext - the extension of assembly language output,
+# gl_c_asm_opt - the C compiler option that produces assembly language output.
+
+AC_DEFUN([gl_C_ASM],
+[
+ AC_EGREP_CPP([MicrosoftCompiler],
+ [
+#ifdef _MSC_VER
+MicrosoftCompiler
+#endif
+ ],
+ [dnl Microsoft's 'cl' and 'clang-cl' produce an .asm file, whereas 'clang'
+ dnl produces a .s file. Need to distinguish 'clang' and 'clang-cl'.
+ rm -f conftest*
+ echo 'int dummy;' > conftest.c
+ AC_TRY_COMMAND(${CC-cc} $CFLAGS $CPPFLAGS -c conftest.c) >/dev/null 2>&1
+ if test -f conftest.o; then
+ gl_asmext='s'
+ gl_c_asm_opt='-S'
+ else
+ gl_asmext='asm'
+ gl_c_asm_opt='-c -Fa'
+ fi
+ rm -f conftest*
+ ],
+ [gl_asmext='s'
+ gl_c_asm_opt='-S'
+ ])
+])
diff --git a/m4/exponentd.m4 b/m4/exponentd.m4
new file mode 100644
index 00000000000..2ef46437deb
--- /dev/null
+++ b/m4/exponentd.m4
@@ -0,0 +1,116 @@
+# exponentd.m4 serial 3
+dnl Copyright (C) 2007-2008, 2010-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+AC_DEFUN([gl_DOUBLE_EXPONENT_LOCATION],
+[
+ AC_CACHE_CHECK([where to find the exponent in a 'double'],
+ [gl_cv_cc_double_expbit0],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <float.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#define NWORDS \
+ ((sizeof (double) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+typedef union { double value; unsigned int word[NWORDS]; } memory_double;
+static unsigned int ored_words[NWORDS];
+static unsigned int anded_words[NWORDS];
+static void add_to_ored_words (double x)
+{
+ memory_double m;
+ size_t i;
+ /* Clear it first, in case sizeof (double) < sizeof (memory_double). */
+ memset (&m, 0, sizeof (memory_double));
+ m.value = x;
+ for (i = 0; i < NWORDS; i++)
+ {
+ ored_words[i] |= m.word[i];
+ anded_words[i] &= m.word[i];
+ }
+}
+int main ()
+{
+ size_t j;
+ FILE *fp = fopen ("conftest.out", "w");
+ if (fp == NULL)
+ return 1;
+ for (j = 0; j < NWORDS; j++)
+ anded_words[j] = ~ (unsigned int) 0;
+ add_to_ored_words (0.25);
+ add_to_ored_words (0.5);
+ add_to_ored_words (1.0);
+ add_to_ored_words (2.0);
+ add_to_ored_words (4.0);
+ /* Remove bits that are common (e.g. if representation of the first mantissa
+ bit is explicit). */
+ for (j = 0; j < NWORDS; j++)
+ ored_words[j] &= ~anded_words[j];
+ /* Now find the nonzero word. */
+ for (j = 0; j < NWORDS; j++)
+ if (ored_words[j] != 0)
+ break;
+ if (j < NWORDS)
+ {
+ size_t i;
+ for (i = j + 1; i < NWORDS; i++)
+ if (ored_words[i] != 0)
+ {
+ fprintf (fp, "unknown");
+ return (fclose (fp) != 0);
+ }
+ for (i = 0; ; i++)
+ if ((ored_words[j] >> i) & 1)
+ {
+ fprintf (fp, "word %d bit %d", (int) j, (int) i);
+ return (fclose (fp) != 0);
+ }
+ }
+ fprintf (fp, "unknown");
+ return (fclose (fp) != 0);
+}
+ ]])],
+ [gl_cv_cc_double_expbit0=`cat conftest.out`],
+ [gl_cv_cc_double_expbit0="unknown"],
+ [
+ dnl On ARM, there are two 'double' floating-point formats, used by
+ dnl different sets of instructions: The older FPA instructions assume
+ dnl that they are stored in big-endian word order, while the words
+ dnl (like integer types) are stored in little-endian byte order.
+ dnl The newer VFP instructions assume little-endian order
+ dnl consistently.
+ AC_EGREP_CPP([mixed_endianness], [
+#if defined arm || defined __arm || defined __arm__
+ mixed_endianness
+#endif
+ ],
+ [gl_cv_cc_double_expbit0="unknown"],
+ [
+ pushdef([AC_MSG_CHECKING],[:])dnl
+ pushdef([AC_MSG_RESULT],[:])dnl
+ pushdef([AC_MSG_RESULT_UNQUOTED],[:])dnl
+ AC_C_BIGENDIAN(
+ [gl_cv_cc_double_expbit0="word 0 bit 20"],
+ [gl_cv_cc_double_expbit0="word 1 bit 20"],
+ [gl_cv_cc_double_expbit0="unknown"])
+ popdef([AC_MSG_RESULT_UNQUOTED])dnl
+ popdef([AC_MSG_RESULT])dnl
+ popdef([AC_MSG_CHECKING])dnl
+ ])
+ ])
+ rm -f conftest.out
+ ])
+ case "$gl_cv_cc_double_expbit0" in
+ word*bit*)
+ word=`echo "$gl_cv_cc_double_expbit0" | sed -e 's/word //' -e 's/ bit.*//'`
+ bit=`echo "$gl_cv_cc_double_expbit0" | sed -e 's/word.*bit //'`
+ AC_DEFINE_UNQUOTED([DBL_EXPBIT0_WORD], [$word],
+ [Define as the word index where to find the exponent of 'double'.])
+ AC_DEFINE_UNQUOTED([DBL_EXPBIT0_BIT], [$bit],
+ [Define as the bit index in the word where to find bit 0 of the exponent of 'double'.])
+ ;;
+ esac
+])
diff --git a/m4/exponentf.m4 b/m4/exponentf.m4
new file mode 100644
index 00000000000..f6395f23f27
--- /dev/null
+++ b/m4/exponentf.m4
@@ -0,0 +1,92 @@
+# exponentf.m4 serial 2
+dnl Copyright (C) 2007-2008, 2010-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+AC_DEFUN([gl_FLOAT_EXPONENT_LOCATION],
+[
+ AC_CACHE_CHECK([where to find the exponent in a 'float'],
+ [gl_cv_cc_float_expbit0],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <float.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#define NWORDS \
+ ((sizeof (float) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+typedef union { float value; unsigned int word[NWORDS]; } memory_float;
+static unsigned int ored_words[NWORDS];
+static unsigned int anded_words[NWORDS];
+static void add_to_ored_words (float x)
+{
+ memory_float m;
+ size_t i;
+ /* Clear it first, in case
+ sizeof (float) < sizeof (memory_float). */
+ memset (&m, 0, sizeof (memory_float));
+ m.value = x;
+ for (i = 0; i < NWORDS; i++)
+ {
+ ored_words[i] |= m.word[i];
+ anded_words[i] &= m.word[i];
+ }
+}
+int main ()
+{
+ size_t j;
+ FILE *fp = fopen ("conftest.out", "w");
+ if (fp == NULL)
+ return 1;
+ for (j = 0; j < NWORDS; j++)
+ anded_words[j] = ~ (unsigned int) 0;
+ add_to_ored_words (0.25f);
+ add_to_ored_words (0.5f);
+ add_to_ored_words (1.0f);
+ add_to_ored_words (2.0f);
+ add_to_ored_words (4.0f);
+ /* Remove bits that are common (e.g. if representation of the first mantissa
+ bit is explicit). */
+ for (j = 0; j < NWORDS; j++)
+ ored_words[j] &= ~anded_words[j];
+ /* Now find the nonzero word. */
+ for (j = 0; j < NWORDS; j++)
+ if (ored_words[j] != 0)
+ break;
+ if (j < NWORDS)
+ {
+ size_t i;
+ for (i = j + 1; i < NWORDS; i++)
+ if (ored_words[i] != 0)
+ {
+ fprintf (fp, "unknown");
+ return (fclose (fp) != 0);
+ }
+ for (i = 0; ; i++)
+ if ((ored_words[j] >> i) & 1)
+ {
+ fprintf (fp, "word %d bit %d", (int) j, (int) i);
+ return (fclose (fp) != 0);
+ }
+ }
+ fprintf (fp, "unknown");
+ return (fclose (fp) != 0);
+}
+ ]])],
+ [gl_cv_cc_float_expbit0=`cat conftest.out`],
+ [gl_cv_cc_float_expbit0="unknown"],
+ [gl_cv_cc_float_expbit0="word 0 bit 23"])
+ rm -f conftest.out
+ ])
+ case "$gl_cv_cc_float_expbit0" in
+ word*bit*)
+ word=`echo "$gl_cv_cc_float_expbit0" | sed -e 's/word //' -e 's/ bit.*//'`
+ bit=`echo "$gl_cv_cc_float_expbit0" | sed -e 's/word.*bit //'`
+ AC_DEFINE_UNQUOTED([FLT_EXPBIT0_WORD], [$word],
+ [Define as the word index where to find the exponent of 'float'.])
+ AC_DEFINE_UNQUOTED([FLT_EXPBIT0_BIT], [$bit],
+ [Define as the bit index in the word where to find bit 0 of the exponent of 'float'.])
+ ;;
+ esac
+])
diff --git a/m4/exponentl.m4 b/m4/exponentl.m4
new file mode 100644
index 00000000000..e1fa5c48cc7
--- /dev/null
+++ b/m4/exponentl.m4
@@ -0,0 +1,112 @@
+# exponentl.m4 serial 5
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+AC_DEFUN([gl_LONG_DOUBLE_EXPONENT_LOCATION],
+[
+ AC_REQUIRE([gl_BIGENDIAN])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([where to find the exponent in a 'long double'],
+ [gl_cv_cc_long_double_expbit0],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <float.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#define NWORDS \
+ ((sizeof (long double) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+typedef union { long double value; unsigned int word[NWORDS]; }
+ memory_long_double;
+static unsigned int ored_words[NWORDS];
+static unsigned int anded_words[NWORDS];
+static void add_to_ored_words (long double *x)
+{
+ memory_long_double m;
+ size_t i;
+ /* Clear it first, in case
+ sizeof (long double) < sizeof (memory_long_double). */
+ memset (&m, 0, sizeof (memory_long_double));
+ m.value = *x;
+ for (i = 0; i < NWORDS; i++)
+ {
+ ored_words[i] |= m.word[i];
+ anded_words[i] &= m.word[i];
+ }
+}
+int main ()
+{
+ static long double samples[5] = { 0.25L, 0.5L, 1.0L, 2.0L, 4.0L };
+ size_t j;
+ FILE *fp = fopen ("conftest.out", "w");
+ if (fp == NULL)
+ return 1;
+ for (j = 0; j < NWORDS; j++)
+ anded_words[j] = ~ (unsigned int) 0;
+ for (j = 0; j < 5; j++)
+ add_to_ored_words (&samples[j]);
+ /* Remove bits that are common (e.g. if representation of the first mantissa
+ bit is explicit). */
+ for (j = 0; j < NWORDS; j++)
+ ored_words[j] &= ~anded_words[j];
+ /* Now find the nonzero word. */
+ for (j = 0; j < NWORDS; j++)
+ if (ored_words[j] != 0)
+ break;
+ if (j < NWORDS)
+ {
+ size_t i;
+ for (i = j + 1; i < NWORDS; i++)
+ if (ored_words[i] != 0)
+ {
+ fprintf (fp, "unknown");
+ return (fclose (fp) != 0);
+ }
+ for (i = 0; ; i++)
+ if ((ored_words[j] >> i) & 1)
+ {
+ fprintf (fp, "word %d bit %d", (int) j, (int) i);
+ return (fclose (fp) != 0);
+ }
+ }
+ fprintf (fp, "unknown");
+ return (fclose (fp) != 0);
+}
+ ]])],
+ [gl_cv_cc_long_double_expbit0=`cat conftest.out`],
+ [gl_cv_cc_long_double_expbit0="unknown"],
+ [
+ dnl When cross-compiling, in general we don't know. It depends on the
+ dnl ABI and compiler version. There are too many cases.
+ gl_cv_cc_long_double_expbit0="unknown"
+ case "$host_os" in
+ mingw*) # On native Windows (little-endian), we know the result
+ # in two cases: mingw, MSVC.
+ AC_EGREP_CPP([Known], [
+#ifdef __MINGW32__
+ Known
+#endif
+ ], [gl_cv_cc_long_double_expbit0="word 2 bit 0"])
+ AC_EGREP_CPP([Known], [
+#ifdef _MSC_VER
+ Known
+#endif
+ ], [gl_cv_cc_long_double_expbit0="word 1 bit 20"])
+ ;;
+ esac
+ ])
+ rm -f conftest.out
+ ])
+ case "$gl_cv_cc_long_double_expbit0" in
+ word*bit*)
+ word=`echo "$gl_cv_cc_long_double_expbit0" | sed -e 's/word //' -e 's/ bit.*//'`
+ bit=`echo "$gl_cv_cc_long_double_expbit0" | sed -e 's/word.*bit //'`
+ AC_DEFINE_UNQUOTED([LDBL_EXPBIT0_WORD], [$word],
+ [Define as the word index where to find the exponent of 'long double'.])
+ AC_DEFINE_UNQUOTED([LDBL_EXPBIT0_BIT], [$bit],
+ [Define as the bit index in the word where to find bit 0 of the exponent of 'long double'.])
+ ;;
+ esac
+])
diff --git a/m4/float_h.m4 b/m4/float_h.m4
new file mode 100644
index 00000000000..2f0c9c4ee9c
--- /dev/null
+++ b/m4/float_h.m4
@@ -0,0 +1,106 @@
+# float_h.m4 serial 13
+dnl Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FLOAT_H],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ GL_GENERATE_FLOAT_H=false
+ REPLACE_FLOAT_LDBL=0
+ case "$host_os" in
+ aix* | beos* | openbsd* | mirbsd* | irix*)
+ GL_GENERATE_FLOAT_H=true
+ ;;
+ freebsd* | dragonfly*)
+ case "$host_cpu" in
+changequote(,)dnl
+ i[34567]86 )
+changequote([,])dnl
+ GL_GENERATE_FLOAT_H=true
+ ;;
+ x86_64 )
+ # On x86_64 systems, the C compiler may still be generating
+ # 32-bit code.
+ AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE(
+ [[#if defined __LP64__ || defined __x86_64__ || defined __amd64__
+ int ok;
+ #else
+ error fail
+ #endif
+ ]])],
+ [],
+ [GL_GENERATE_FLOAT_H=true])
+ ;;
+ esac
+ ;;
+ linux*)
+ case "$host_cpu" in
+ powerpc*)
+ GL_GENERATE_FLOAT_H=true
+ ;;
+ esac
+ ;;
+ esac
+ case "$host_os" in
+ aix* | freebsd* | dragonfly* | linux*)
+ if $GL_GENERATE_FLOAT_H; then
+ REPLACE_FLOAT_LDBL=1
+ fi
+ ;;
+ esac
+
+ dnl Test against glibc-2.7 Linux/SPARC64 bug.
+ REPLACE_ITOLD=0
+ AC_CACHE_CHECK([whether conversion from 'int' to 'long double' works],
+ [gl_cv_func_itold_works],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+int i = -1;
+volatile long double ld;
+int main ()
+{
+ ld += i * 1.0L;
+ if (ld > 0)
+ return 1;
+ return 0;
+}]])],
+ [gl_cv_func_itold_works=yes],
+ [gl_cv_func_itold_works=no],
+ [case "$host" in
+ sparc*-*-linux*)
+ AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE(
+ [[#if defined __LP64__ || defined __arch64__
+ int ok;
+ #else
+ error fail
+ #endif
+ ]])],
+ [gl_cv_func_itold_works="guessing no"],
+ [gl_cv_func_itold_works="guessing yes"])
+ ;;
+ # Guess yes on native Windows.
+ mingw*) gl_cv_func_itold_works="guessing yes" ;;
+ *) gl_cv_func_itold_works="guessing yes" ;;
+ esac
+ ])
+ ])
+ case "$gl_cv_func_itold_works" in
+ *no)
+ REPLACE_ITOLD=1
+ dnl We add the workaround to <float.h> but also to <math.h>,
+ dnl to increase the chances that the fix function gets pulled in.
+ GL_GENERATE_FLOAT_H=true
+ ;;
+ esac
+
+ if $GL_GENERATE_FLOAT_H; then
+ gl_NEXT_HEADERS([float.h])
+ fi
+ AC_SUBST([REPLACE_ITOLD])
+])
diff --git a/m4/frexp.m4 b/m4/frexp.m4
new file mode 100644
index 00000000000..0480d98f304
--- /dev/null
+++ b/m4/frexp.m4
@@ -0,0 +1,181 @@
+# frexp.m4 serial 16
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_FREXP],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ AC_REQUIRE([gl_CHECK_FREXP_NO_LIBM])
+ FREXP_LIBM=
+ if test $gl_cv_func_frexp_no_libm = no; then
+ AC_CACHE_CHECK([whether frexp() can be used with libm],
+ [gl_cv_func_frexp_in_libm],
+ [
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lm"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ double x;]],
+ [[int e; return frexp (x, &e) > 0;]])],
+ [gl_cv_func_frexp_in_libm=yes],
+ [gl_cv_func_frexp_in_libm=no])
+ LIBS="$save_LIBS"
+ ])
+ if test $gl_cv_func_frexp_in_libm = yes; then
+ FREXP_LIBM=-lm
+ fi
+ fi
+ if test $gl_cv_func_frexp_no_libm = yes \
+ || test $gl_cv_func_frexp_in_libm = yes; then
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $FREXP_LIBM"
+ gl_FUNC_FREXP_WORKS
+ LIBS="$save_LIBS"
+ case "$gl_cv_func_frexp_works" in
+ *yes) gl_func_frexp=yes ;;
+ *) gl_func_frexp=no; REPLACE_FREXP=1; FREXP_LIBM= ;;
+ esac
+ else
+ gl_func_frexp=no
+ fi
+ if test $gl_func_frexp = yes; then
+ AC_DEFINE([HAVE_FREXP], [1],
+ [Define if the frexp() function is available and works.])
+ fi
+ AC_SUBST([FREXP_LIBM])
+])
+
+AC_DEFUN([gl_FUNC_FREXP_NO_LIBM],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ AC_REQUIRE([gl_CHECK_FREXP_NO_LIBM])
+ if test $gl_cv_func_frexp_no_libm = yes; then
+ gl_FUNC_FREXP_WORKS
+ case "$gl_cv_func_frexp_works" in
+ *yes) gl_func_frexp_no_libm=yes ;;
+ *) gl_func_frexp_no_libm=no; REPLACE_FREXP=1 ;;
+ esac
+ else
+ gl_func_frexp_no_libm=no
+ dnl Set REPLACE_FREXP here because the system may have frexp in libm.
+ REPLACE_FREXP=1
+ fi
+ if test $gl_func_frexp_no_libm = yes; then
+ AC_DEFINE([HAVE_FREXP_IN_LIBC], [1],
+ [Define if the frexp() function is available in libc.])
+ fi
+])
+
+dnl Test whether frexp() can be used without linking with libm.
+dnl Set gl_cv_func_frexp_no_libm to 'yes' or 'no' accordingly.
+AC_DEFUN([gl_CHECK_FREXP_NO_LIBM],
+[
+ AC_CACHE_CHECK([whether frexp() can be used without linking with libm],
+ [gl_cv_func_frexp_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ double x;]],
+ [[int e; return frexp (x, &e) > 0;]])],
+ [gl_cv_func_frexp_no_libm=yes],
+ [gl_cv_func_frexp_no_libm=no])
+ ])
+])
+
+dnl Test whether frexp() works also on denormalized numbers (this fails e.g. on
+dnl NetBSD 3.0), on infinite numbers (this fails e.g. on IRIX 6.5 and mingw),
+dnl and on negative zero (this fails e.g. on NetBSD 4.99 and mingw).
+AC_DEFUN([gl_FUNC_FREXP_WORKS],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CHECK_DECLS_ONCE([alarm])
+ AC_CACHE_CHECK([whether frexp works], [gl_cv_func_frexp_works],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <float.h>
+#include <math.h>
+#include <string.h>
+#if HAVE_DECL_ALARM
+# include <signal.h>
+# include <unistd.h>
+#endif
+/* HP cc on HP-UX 10.20 has a bug with the constant expression -0.0.
+ ICC 10.0 has a bug when optimizing the expression -zero.
+ The expression -DBL_MIN * DBL_MIN does not work when cross-compiling
+ to PowerPC on Mac OS X 10.5. */
+#if defined __hpux || defined __sgi || defined __ICC
+static double
+compute_minus_zero (void)
+{
+ return -DBL_MIN * DBL_MIN;
+}
+# define minus_zero compute_minus_zero ()
+#else
+double minus_zero = -0.0;
+#endif
+int main()
+{
+ int result = 0;
+ int i;
+ volatile double x;
+ double zero = 0.0;
+#if HAVE_DECL_ALARM
+ /* NeXTstep 3.3 frexp() runs into an endless loop when called on an infinite
+ number. Let the test fail in this case. */
+ signal (SIGALRM, SIG_DFL);
+ alarm (5);
+#endif
+ /* Test on denormalized numbers. */
+ for (i = 1, x = 1.0; i >= DBL_MIN_EXP; i--, x *= 0.5)
+ ;
+ if (x > 0.0)
+ {
+ int exp;
+ double y = frexp (x, &exp);
+ /* On machines with IEEE754 arithmetic: x = 1.11254e-308, exp = -1022.
+ On NetBSD: y = 0.75. Correct: y = 0.5. */
+ if (y != 0.5)
+ result |= 1;
+ }
+ /* Test on infinite numbers. */
+ x = 1.0 / zero;
+ {
+ int exp;
+ double y = frexp (x, &exp);
+ if (y != x)
+ result |= 2;
+ }
+ /* Test on negative zero. */
+ x = minus_zero;
+ {
+ int exp;
+ double y = frexp (x, &exp);
+ if (memcmp (&y, &x, sizeof x))
+ result |= 4;
+ }
+ return result;
+}]])],
+ [gl_cv_func_frexp_works=yes],
+ [gl_cv_func_frexp_works=no],
+ [case "$host_os" in
+ netbsd* | irix*) gl_cv_func_frexp_works="guessing no" ;;
+ mingw*) # Guess yes with MSVC, no with mingw.
+ AC_EGREP_CPP([Good], [
+#ifdef _MSC_VER
+ Good
+#endif
+ ],
+ [gl_cv_func_frexp_works="guessing yes"],
+ [gl_cv_func_frexp_works="guessing no"])
+ ;;
+ *) gl_cv_func_frexp_works="guessing yes" ;;
+ esac
+ ])
+ ])
+])
diff --git a/m4/frexpl.m4 b/m4/frexpl.m4
new file mode 100644
index 00000000000..b4cf0ca9ea1
--- /dev/null
+++ b/m4/frexpl.m4
@@ -0,0 +1,233 @@
+# frexpl.m4 serial 22
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_FREXPL],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE])
+
+ dnl Persuade glibc <math.h> to declare frexpl().
+ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+ dnl Check whether it's declared.
+ dnl Mac OS X 10.3 has frexpl() in libc but doesn't declare it in <math.h>.
+ AC_CHECK_DECL([frexpl], , [HAVE_DECL_FREXPL=0], [[#include <math.h>]])
+
+ FREXPL_LIBM=
+ if test $HAVE_DECL_FREXPL = 1; then
+ gl_CHECK_FREXPL_NO_LIBM
+ if test $gl_cv_func_frexpl_no_libm = no; then
+ AC_CACHE_CHECK([whether frexpl() can be used with libm],
+ [gl_cv_func_frexpl_in_libm],
+ [
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lm"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ long double x;]],
+ [[int e; return frexpl (x, &e) > 0;]])],
+ [gl_cv_func_frexpl_in_libm=yes],
+ [gl_cv_func_frexpl_in_libm=no])
+ LIBS="$save_LIBS"
+ ])
+ if test $gl_cv_func_frexpl_in_libm = yes; then
+ FREXPL_LIBM=-lm
+ fi
+ fi
+ if test $gl_cv_func_frexpl_no_libm = yes \
+ || test $gl_cv_func_frexpl_in_libm = yes; then
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $FREXPL_LIBM"
+ gl_FUNC_FREXPL_WORKS
+ LIBS="$save_LIBS"
+ case "$gl_cv_func_frexpl_works" in
+ *yes) gl_func_frexpl=yes ;;
+ *) gl_func_frexpl=no; REPLACE_FREXPL=1 ;;
+ esac
+ else
+ gl_func_frexpl=no
+ fi
+ if test $gl_func_frexpl = yes; then
+ AC_DEFINE([HAVE_FREXPL], [1],
+ [Define if the frexpl() function is available.])
+ fi
+ fi
+ if test $HAVE_DECL_FREXPL = 0 || test $gl_func_frexpl = no; then
+ dnl Find libraries needed to link lib/frexpl.c.
+ if test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 1; then
+ AC_REQUIRE([gl_FUNC_FREXP])
+ FREXPL_LIBM="$FREXP_LIBM"
+ else
+ FREXPL_LIBM=
+ fi
+ fi
+ AC_SUBST([FREXPL_LIBM])
+])
+
+AC_DEFUN([gl_FUNC_FREXPL_NO_LIBM],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE])
+ dnl Check whether it's declared.
+ dnl Mac OS X 10.3 has frexpl() in libc but doesn't declare it in <math.h>.
+ AC_CHECK_DECL([frexpl], , [HAVE_DECL_FREXPL=0], [[#include <math.h>]])
+ if test $HAVE_DECL_FREXPL = 1; then
+ gl_CHECK_FREXPL_NO_LIBM
+ if test $gl_cv_func_frexpl_no_libm = yes; then
+ gl_FUNC_FREXPL_WORKS
+ case "$gl_cv_func_frexpl_works" in
+ *yes) gl_func_frexpl_no_libm=yes ;;
+ *) gl_func_frexpl_no_libm=no; REPLACE_FREXPL=1 ;;
+ esac
+ else
+ gl_func_frexpl_no_libm=no
+ dnl Set REPLACE_FREXPL here because the system may have frexpl in libm.
+ REPLACE_FREXPL=1
+ fi
+ if test $gl_func_frexpl_no_libm = yes; then
+ AC_DEFINE([HAVE_FREXPL_IN_LIBC], [1],
+ [Define if the frexpl() function is available in libc.])
+ fi
+ fi
+])
+
+dnl Test whether frexpl() can be used without linking with libm.
+dnl Set gl_cv_func_frexpl_no_libm to 'yes' or 'no' accordingly.
+AC_DEFUN([gl_CHECK_FREXPL_NO_LIBM],
+[
+ AC_CACHE_CHECK([whether frexpl() can be used without linking with libm],
+ [gl_cv_func_frexpl_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ long double x;]],
+ [[int e; return frexpl (x, &e) > 0;]])],
+ [gl_cv_func_frexpl_no_libm=yes],
+ [gl_cv_func_frexpl_no_libm=no])
+ ])
+])
+
+dnl Test whether frexpl() works on finite numbers (this fails on
+dnl Mac OS X 10.4/PowerPC, on AIX 5.1, and on BeOS), on denormalized numbers
+dnl (this fails on Mac OS X 10.5/i386), and also on infinite numbers (this
+dnl fails e.g. on IRIX 6.5 and mingw).
+AC_DEFUN([gl_FUNC_FREXPL_WORKS],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether frexpl works], [gl_cv_func_frexpl_works],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <float.h>
+#include <math.h>
+/* Override the values of <float.h>, like done in float.in.h. */
+#if defined __i386__ && (defined __BEOS__ || defined __OpenBSD__)
+# undef LDBL_MIN_EXP
+# define LDBL_MIN_EXP (-16381)
+#endif
+#if defined __i386__ && (defined __FreeBSD__ || defined __DragonFly__)
+# undef LDBL_MIN_EXP
+# define LDBL_MIN_EXP (-16381)
+#endif
+#if (defined _ARCH_PPC || defined _POWER) && defined _AIX && (LDBL_MANT_DIG == 106) && defined __GNUC__
+# undef LDBL_MIN_EXP
+# define LDBL_MIN_EXP DBL_MIN_EXP
+#endif
+#if defined __sgi && (LDBL_MANT_DIG >= 106)
+# if defined __GNUC__
+# undef LDBL_MIN_EXP
+# define LDBL_MIN_EXP DBL_MIN_EXP
+# endif
+#endif
+extern
+#ifdef __cplusplus
+"C"
+#endif
+long double frexpl (long double, int *);
+long double zero = 0.0L;
+int main()
+{
+ int result = 0;
+ volatile long double x;
+ /* Test on finite numbers that fails on AIX 5.1. */
+ x = 16.0L;
+ {
+ int exp = -9999;
+ frexpl (x, &exp);
+ if (exp != 5)
+ result |= 1;
+ }
+ /* Test on finite numbers that fails on Mac OS X 10.4, because its frexpl
+ function returns an invalid (incorrectly normalized) value: it returns
+ y = { 0x3fe028f5, 0xc28f5c28, 0x3c9eb851, 0xeb851eb8 }
+ but the correct result is
+ 0.505L = { 0x3fe028f5, 0xc28f5c29, 0xbc547ae1, 0x47ae1480 } */
+ x = 1.01L;
+ {
+ int exp = -9999;
+ long double y = frexpl (x, &exp);
+ if (!(exp == 1 && y == 0.505L))
+ result |= 2;
+ }
+ /* Test on large finite numbers. This fails on BeOS at i = 16322, while
+ LDBL_MAX_EXP = 16384.
+ In the loop end test, we test x against Infinity, rather than comparing
+ i with LDBL_MAX_EXP, because BeOS <float.h> has a wrong LDBL_MAX_EXP. */
+ {
+ int i;
+ for (i = 1, x = 1.0L; x != x + x; i++, x *= 2.0L)
+ {
+ int exp = -9999;
+ frexpl (x, &exp);
+ if (exp != i)
+ {
+ result |= 4;
+ break;
+ }
+ }
+ }
+ /* Test on denormalized numbers. */
+ {
+ int i;
+ for (i = 1, x = 1.0L; i >= LDBL_MIN_EXP; i--, x *= 0.5L)
+ ;
+ if (x > 0.0L)
+ {
+ int exp;
+ long double y = frexpl (x, &exp);
+ /* On machines with IEEE854 arithmetic: x = 1.68105e-4932,
+ exp = -16382, y = 0.5. On Mac OS X 10.5: exp = -16384, y = 0.5. */
+ if (exp != LDBL_MIN_EXP - 1)
+ result |= 8;
+ }
+ }
+ /* Test on infinite numbers. */
+ /* The Microsoft MSVC 14 compiler chokes on the expression 1.0 / 0.0. */
+ x = 1.0L / zero;
+ {
+ int exp;
+ long double y = frexpl (x, &exp);
+ if (y != x)
+ result |= 16;
+ }
+ return result;
+}]])],
+ [gl_cv_func_frexpl_works=yes],
+ [gl_cv_func_frexpl_works=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ aix | aix[3-6]* | beos* | darwin* | irix* | mingw* | pw*)
+ gl_cv_func_frexpl_works="guessing no";;
+ *) gl_cv_func_frexpl_works="guessing yes";;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
diff --git a/m4/fseterr.m4 b/m4/fseterr.m4
new file mode 100644
index 00000000000..61ac03d49d1
--- /dev/null
+++ b/m4/fseterr.m4
@@ -0,0 +1,13 @@
+# fseterr.m4 serial 2
+dnl Copyright (C) 2012-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_FSETERR],
+[
+ gl_CHECK_FUNCS_ANDROID([__fseterr],
+ [[#include <stdio.h>
+ #include <stdio_ext.h>
+ ]])
+])
diff --git a/m4/getdelim.m4 b/m4/getdelim.m4
new file mode 100644
index 00000000000..60555b9718b
--- /dev/null
+++ b/m4/getdelim.m4
@@ -0,0 +1,114 @@
+# getdelim.m4 serial 19
+
+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])
+
+ gl_CHECK_FUNCS_ANDROID([getdelim], [[#include <stdio.h>]])
+ 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* | midipix*) 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
+ case "$gl_cv_onwards_func_getdelim" in
+ future*) REPLACE_GETDELIM=1 ;;
+ esac
+ 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 00000000000..83e7e9315f6
--- /dev/null
+++ b/m4/getline.m4
@@ -0,0 +1,111 @@
+# getline.m4 serial 33
+
+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_CHECK_FUNCS_ANDROID([getline], [[#include <stdio.h>]])
+ if test $ac_cv_func_getline = yes; then
+ dnl Found it in some library. Verify that it works.
+ 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* | midipix*) am_cv_func_working_getline="guessing yes" ;;
+ *) am_cv_func_working_getline="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ ])
+ ])
+ else
+ am_cv_func_working_getline=no
+ case "$gl_cv_onwards_func_getline" in
+ future*) REPLACE_GETLINE=1 ;;
+ 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 882b5869755..9f701e95211 100644
--- a/m4/gnulib-comp.m4
+++ b/m4/gnulib-comp.m4
@@ -95,17 +95,24 @@ AC_DEFUN([gl_EARLY],
# Code from module filename:
# Code from module filevercmp:
# Code from module flexmember:
+ # Code from module float:
# Code from module fpending:
# Code from module fpieee:
AC_REQUIRE([gl_FP_IEEE])
+ # Code from module fpucw:
# Code from module free-posix:
+ # Code from module frexp-nolibm:
+ # Code from module frexpl-nolibm:
+ # Code from module fseterr:
# Code from module fstatat:
# Code from module fsusage:
# 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:
@@ -123,6 +130,9 @@ AC_DEFUN([gl_EARLY],
# Code from module include_next:
# Code from module intprops:
# Code from module inttypes-incomplete:
+ # Code from module isnand-nolibm:
+ # Code from module isnanf-nolibm:
+ # Code from module isnanl-nolibm:
# Code from module largefile:
AC_REQUIRE([AC_SYS_LARGEFILE])
# Code from module lchmod:
@@ -133,6 +143,7 @@ AC_DEFUN([gl_EARLY],
# Code from module malloc-gnu:
# Code from module malloc-posix:
# Code from module manywarnings:
+ # Code from module math:
# Code from module memmem-simple:
# Code from module mempcpy:
# Code from module memrchr:
@@ -150,6 +161,10 @@ AC_DEFUN([gl_EARLY],
# Code from module openat-h:
# Code from module pathmax:
# Code from module pipe2:
+ # Code from module printf-frexp:
+ # Code from module printf-frexpl:
+ # Code from module printf-posix:
+ # Code from module printf-safe:
# Code from module pselect:
# Code from module pthread_sigmask:
# Code from module qcopy-acl:
@@ -163,6 +178,8 @@ AC_DEFUN([gl_EARLY],
# Code from module sig2str:
# Code from module sigdescr_np:
# Code from module signal-h:
+ # Code from module signbit:
+ # Code from module size_max:
# Code from module snippet/_Noreturn:
# Code from module snippet/arg-nonnull:
# Code from module snippet/c++defs:
@@ -179,6 +196,7 @@ AC_DEFUN([gl_EARLY],
gl_STDIO_H_EARLY
# Code from module stdlib:
# Code from module stpcpy:
+ # Code from module stpncpy:
# Code from module string:
# Code from module strnlen:
# Code from module strtoimax:
@@ -206,12 +224,20 @@ AC_DEFUN([gl_EARLY],
# Code from module utimens:
# Code from module utimensat:
# Code from module vararrays:
+ # Code from module vasnprintf:
+ # Code from module vasprintf:
+ # Code from module vasprintf-posix:
# Code from module verify:
+ # Code from module vfprintf-posix:
# Code from module vla:
# Code from module warnings:
# Code from module xalloc-oversized:
+<<<<<<< HEAD
+ # Code from module xsize:
+=======
# Code from module year2038:
AC_REQUIRE([AC_SYS_YEAR2038])
+>>>>>>> origin/master
])
# This macro should be invoked from ./configure.ac, in the section
@@ -319,6 +345,11 @@ AC_DEFUN([gl_INIT],
gl_FILE_HAS_ACL
gl_FILEMODE
AC_C_FLEXIBLE_ARRAY_MEMBER
+ gl_FLOAT_H
+ gl_CONDITIONAL_HEADER([float.h])
+ AC_PROG_MKDIR_P
+ gl_CONDITIONAL([GL_COND_OBJ_FLOAT], [test $REPLACE_FLOAT_LDBL = 1])
+ gl_CONDITIONAL([GL_COND_OBJ_ITOLD], [test $REPLACE_ITOLD = 1])
gl_FUNC_FPENDING
gl_CONDITIONAL([GL_COND_OBJ_FPENDING], [test $gl_cv_func___fpending = no])
gl_FUNC_FREE
@@ -327,6 +358,16 @@ AC_DEFUN([gl_INIT],
gl_PREREQ_FREE
])
gl_STDLIB_MODULE_INDICATOR([free-posix])
+ gl_FUNC_FREXP_NO_LIBM
+ if test $gl_func_frexp_no_libm != yes; then
+ AC_LIBOBJ([frexp])
+ fi
+ gl_MATH_MODULE_INDICATOR([frexp])
+ gl_FUNC_FREXPL_NO_LIBM
+ if test $HAVE_DECL_FREXPL = 0 || test $gl_func_frexpl_no_libm = no; then
+ AC_LIBOBJ([frexpl])
+ fi
+ gl_MATH_MODULE_INDICATOR([frexpl])
gl_FUNC_FSTATAT
gl_CONDITIONAL([GL_COND_OBJ_FSTATAT],
[test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1])
@@ -346,6 +387,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],
@@ -388,6 +435,16 @@ AC_DEFUN([gl_INIT],
gl_INTTYPES_INCOMPLETE
gl_INTTYPES_H_REQUIRE_DEFAULTS
AC_PROG_MKDIR_P
+ gl_FUNC_ISNAND_NO_LIBM
+ if test $gl_func_isnand_no_libm != yes; then
+ AC_LIBOBJ([isnand])
+ gl_PREREQ_ISNAND
+ fi
+ gl_FUNC_ISNANL_NO_LIBM
+ if test $gl_func_isnanl_no_libm != yes; then
+ AC_LIBOBJ([isnanl])
+ gl_PREREQ_ISNANL
+ fi
AC_REQUIRE([gl_LARGEFILE])
gl___INLINE
gl_LIBGMP
@@ -403,6 +460,9 @@ AC_DEFUN([gl_INIT],
gl_PREREQ_LSTAT
])
gl_SYS_STAT_MODULE_INDICATOR([lstat])
+ gl_MATH_H
+ gl_MATH_H_REQUIRE_DEFAULTS
+ AC_PROG_MKDIR_P
gl_FUNC_MEMMEM_SIMPLE
if test $HAVE_MEMMEM = 0 || test $REPLACE_MEMMEM = 1; then
AC_LIBOBJ([memmem])
@@ -452,6 +512,11 @@ AC_DEFUN([gl_INIT],
gl_PATHMAX
gl_FUNC_PIPE2
gl_UNISTD_MODULE_INDICATOR([pipe2])
+ gl_FUNC_PRINTF_FREXP
+ gl_FUNC_PRINTF_FREXPL
+ gl_FUNC_PRINTF_POSIX
+ gl_STDIO_MODULE_INDICATOR([printf-posix])
+ m4_divert_text([INIT_PREPARE], [gl_printf_safe=yes])
gl_FUNC_PSELECT
gl_CONDITIONAL([GL_COND_OBJ_PSELECT],
[test $HAVE_PSELECT = 0 || test $REPLACE_PSELECT = 1])
@@ -498,6 +563,9 @@ AC_DEFUN([gl_INIT],
gl_SIGNAL_H
gl_SIGNAL_H_REQUIRE_DEFAULTS
AC_PROG_MKDIR_P
+ gl_SIGNBIT
+ gl_CONDITIONAL([GL_COND_OBJ_SIGNBIT3], [test $REPLACE_SIGNBIT = 1])
+ gl_MATH_MODULE_INDICATOR([signbit])
gl_TYPE_SOCKLEN_T
gt_TYPE_SSIZE_T
gl_STAT_TIME
@@ -558,6 +626,13 @@ AC_DEFUN([gl_INIT],
gl_PREREQ_STPCPY
])
gl_STRING_MODULE_INDICATOR([stpcpy])
+ gl_FUNC_STPNCPY
+ gl_CONDITIONAL([GL_COND_OBJ_STPNCPY],
+ [test $HAVE_STPNCPY = 0 || test $REPLACE_STPNCPY = 1])
+ AM_COND_IF([GL_COND_OBJ_STPNCPY], [
+ gl_PREREQ_STPNCPY
+ ])
+ gl_STRING_MODULE_INDICATOR([stpncpy])
gl_STRING_H
gl_STRING_H_REQUIRE_DEFAULTS
AC_PROG_MKDIR_P
@@ -637,17 +712,26 @@ AC_DEFUN([gl_INIT],
[test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1])
gl_SYS_STAT_MODULE_INDICATOR([utimensat])
AC_C_VARARRAYS
+ gl_FUNC_VASPRINTF
+ gl_STDIO_MODULE_INDICATOR([vasprintf])
+ m4_ifdef([AM_XGETTEXT_OPTION],
+ [AM_][XGETTEXT_OPTION([--flag=asprintf:2:c-format])
+ AM_][XGETTEXT_OPTION([--flag=vasprintf:2:c-format])])
+ gl_FUNC_VASPRINTF_POSIX
gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false
gl_gnulib_enabled_cloexec=false
gl_gnulib_enabled_dirfd=false
gl_gnulib_enabled_925677f0343de64b89a9f0c790b4104c=false
gl_gnulib_enabled_euidaccess=false
+ gl_gnulib_enabled_fseterr=false
+ gl_gnulib_enabled_getdelim=false
gl_gnulib_enabled_getdtablesize=false
gl_gnulib_enabled_getgroups=false
gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false
gl_gnulib_enabled_fd38c7e463b54744b77b98aeafb4fa7c=false
gl_gnulib_enabled_8444034ea779b88768865bb60b4fb8c9=false
gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false
+ gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66=false
gl_gnulib_enabled_lchmod=false
gl_gnulib_enabled_e80bf6f757095d2e5fc94dafb8f8fc8b=false
gl_gnulib_enabled_ef455225c00f5049c808c2eda3e76866=false
@@ -658,9 +742,13 @@ AC_DEFUN([gl_INIT],
gl_gnulib_enabled_d3b2383720ee0e541357aa2aac598e2b=false
gl_gnulib_enabled_61bcaca76b3e6f9ae55d57a1c3193bc4=false
gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false
+ gl_gnulib_enabled_size_max=false
gl_gnulib_enabled_strtoll=false
gl_gnulib_enabled_utimens=false
+ gl_gnulib_enabled_vasnprintf=false
+ gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b=false
gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false
+ gl_gnulib_enabled_xsize=false
func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b ()
{
if $gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b; then :; else
@@ -713,6 +801,27 @@ AC_DEFUN([gl_INIT],
func_gl_gnulib_m4code_6099e9737f757db36c47fa9d9f02e88c
fi
}
+ func_gl_gnulib_m4code_fseterr ()
+ {
+ if ! $gl_gnulib_enabled_fseterr; then
+ gl_FUNC_FSETERR
+ gl_CONDITIONAL([GL_COND_OBJ_FSETERR], [test $ac_cv_func___fseterr = no])
+ gl_gnulib_enabled_fseterr=true
+ 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 :; else
@@ -781,6 +890,17 @@ AC_DEFUN([gl_INIT],
fi
fi
}
+ func_gl_gnulib_m4code_3f0e593033d1fc2c127581960f641b66 ()
+ {
+ if ! $gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66; then
+ gl_FUNC_ISNANF_NO_LIBM
+ if test $gl_func_isnanf_no_libm != yes; then
+ AC_LIBOBJ([isnanf])
+ gl_PREREQ_ISNANF
+ fi
+ gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66=true
+ fi
+ }
func_gl_gnulib_m4code_lchmod ()
{
if $gl_gnulib_enabled_lchmod; then :; else
@@ -907,6 +1027,13 @@ AC_DEFUN([gl_INIT],
gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=true
fi
}
+ func_gl_gnulib_m4code_size_max ()
+ {
+ if ! $gl_gnulib_enabled_size_max; then
+ gl_SIZE_MAX
+ gl_gnulib_enabled_size_max=true
+ fi
+ }
func_gl_gnulib_m4code_strtoll ()
{
if $gl_gnulib_enabled_strtoll; then :; else
@@ -927,12 +1054,44 @@ AC_DEFUN([gl_INIT],
gl_gnulib_enabled_utimens=true
fi
}
+ func_gl_gnulib_m4code_vasnprintf ()
+ {
+ if ! $gl_gnulib_enabled_vasnprintf; then
+ AC_REQUIRE([AC_C_RESTRICT])
+ gl_FUNC_VASNPRINTF
+ gl_gnulib_enabled_vasnprintf=true
+ func_gl_gnulib_m4code_xsize
+ fi
+ }
+ func_gl_gnulib_m4code_ed5616be3593d355b981ffab56b9f37b ()
+ {
+ if ! $gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b; then
+ gl_FUNC_VFPRINTF_POSIX
+ gl_STDIO_MODULE_INDICATOR([vfprintf-posix])
+ gl_MODULE_INDICATOR([vfprintf-posix])
+ gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b=true
+ if test $REPLACE_VFPRINTF = 1; then
+ func_gl_gnulib_m4code_fseterr
+ fi
+ if test $REPLACE_VFPRINTF = 1; then
+ func_gl_gnulib_m4code_vasnprintf
+ fi
+ fi
+ }
func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec ()
{
if $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then :; else
gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=true
fi
}
+ func_gl_gnulib_m4code_xsize ()
+ {
+ if ! $gl_gnulib_enabled_xsize; then
+ gl_XSIZE
+ gl_gnulib_enabled_xsize=true
+ func_gl_gnulib_m4code_size_max
+ fi
+ }
if test $HAVE_CANONICALIZE_FILE_NAME = 0 || test $REPLACE_CANONICALIZE_FILE_NAME = 1; then
func_gl_gnulib_m4code_925677f0343de64b89a9f0c790b4104c
fi
@@ -978,6 +1137,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 || test $REPLACE_GETLOADAVG = 1;; esac; then
func_gl_gnulib_m4code_open
fi
@@ -987,6 +1149,9 @@ AC_DEFUN([gl_INIT],
if case $host_os in mingw*) false;; *) test $HAVE_GETRANDOM = 0 || test $REPLACE_GETRANDOM = 1;; esac; then
func_gl_gnulib_m4code_open
fi
+ if test $REPLACE_PRINTF = 1; then
+ func_gl_gnulib_m4code_ed5616be3593d355b981ffab56b9f37b
+ fi
if test $HAVE_READLINKAT = 0 || test $REPLACE_READLINKAT = 1; then
func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b
fi
@@ -996,6 +1161,9 @@ AC_DEFUN([gl_INIT],
if test $ac_use_included_regex = yes; then
func_gl_gnulib_m4code_fd38c7e463b54744b77b98aeafb4fa7c
fi
+ if test $REPLACE_SIGNBIT = 1; then
+ func_gl_gnulib_m4code_3f0e593033d1fc2c127581960f641b66
+ fi
if { test $HAVE_DECL_STRTOIMAX = 0 || test $REPLACE_STRTOIMAX = 1; } && test $ac_cv_type_long_long_int = yes; then
func_gl_gnulib_m4code_strtoll
fi
@@ -1011,18 +1179,27 @@ AC_DEFUN([gl_INIT],
if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then
func_gl_gnulib_m4code_utimens
fi
+ if test $HAVE_VASPRINTF = 0 || test $REPLACE_VASPRINTF = 1; then
+ func_gl_gnulib_m4code_vasnprintf
+ fi
+ if test $HAVE_VASPRINTF = 0 || test $REPLACE_VASPRINTF = 1; then
+ func_gl_gnulib_m4code_vasnprintf
+ fi
m4_pattern_allow([^gl_GNULIB_ENABLED_])
AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b])
AM_CONDITIONAL([gl_GNULIB_ENABLED_cloexec], [$gl_gnulib_enabled_cloexec])
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_fseterr], [$gl_gnulib_enabled_fseterr])
+ 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])
AM_CONDITIONAL([gl_GNULIB_ENABLED_fd38c7e463b54744b77b98aeafb4fa7c], [$gl_gnulib_enabled_fd38c7e463b54744b77b98aeafb4fa7c])
AM_CONDITIONAL([gl_GNULIB_ENABLED_8444034ea779b88768865bb60b4fb8c9], [$gl_gnulib_enabled_8444034ea779b88768865bb60b4fb8c9])
AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1])
+ AM_CONDITIONAL([gl_GNULIB_ENABLED_3f0e593033d1fc2c127581960f641b66], [$gl_gnulib_enabled_3f0e593033d1fc2c127581960f641b66])
AM_CONDITIONAL([gl_GNULIB_ENABLED_lchmod], [$gl_gnulib_enabled_lchmod])
AM_CONDITIONAL([gl_GNULIB_ENABLED_e80bf6f757095d2e5fc94dafb8f8fc8b], [$gl_gnulib_enabled_e80bf6f757095d2e5fc94dafb8f8fc8b])
AM_CONDITIONAL([gl_GNULIB_ENABLED_ef455225c00f5049c808c2eda3e76866], [$gl_gnulib_enabled_ef455225c00f5049c808c2eda3e76866])
@@ -1033,9 +1210,13 @@ AC_DEFUN([gl_INIT],
AM_CONDITIONAL([gl_GNULIB_ENABLED_d3b2383720ee0e541357aa2aac598e2b], [$gl_gnulib_enabled_d3b2383720ee0e541357aa2aac598e2b])
AM_CONDITIONAL([gl_GNULIB_ENABLED_61bcaca76b3e6f9ae55d57a1c3193bc4], [$gl_gnulib_enabled_61bcaca76b3e6f9ae55d57a1c3193bc4])
AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c])
+ AM_CONDITIONAL([gl_GNULIB_ENABLED_size_max], [$gl_gnulib_enabled_size_max])
AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll])
AM_CONDITIONAL([gl_GNULIB_ENABLED_utimens], [$gl_gnulib_enabled_utimens])
+ AM_CONDITIONAL([gl_GNULIB_ENABLED_vasnprintf], [$gl_gnulib_enabled_vasnprintf])
+ AM_CONDITIONAL([gl_GNULIB_ENABLED_ed5616be3593d355b981ffab56b9f37b], [$gl_gnulib_enabled_ed5616be3593d355b981ffab56b9f37b])
AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec])
+ AM_CONDITIONAL([gl_GNULIB_ENABLED_xsize], [$gl_gnulib_enabled_xsize])
# End of code from modules
m4_ifval(gl_LIBSOURCES_LIST, [
m4_syscmd([test ! -d ]m4_defn([gl_LIBSOURCES_DIR])[ ||
@@ -1221,6 +1402,8 @@ AC_DEFUN([gl_FILE_LIST], [
lib/allocator.c
lib/allocator.h
lib/arg-nonnull.h
+ lib/asnprintf.c
+ lib/asprintf.c
lib/assert.in.h
lib/at-func.c
lib/attribute.h
@@ -1273,9 +1456,17 @@ AC_DEFUN([gl_FILE_LIST], [
lib/filevercmp.c
lib/filevercmp.h
lib/flexmember.h
+ lib/float+.h
+ lib/float.c
+ lib/float.in.h
lib/fpending.c
lib/fpending.h
+ lib/fpucw.h
lib/free.c
+ lib/frexp.c
+ lib/frexpl.c
+ lib/fseterr.c
+ lib/fseterr.h
lib/fstatat.c
lib/fsusage.c
lib/fsusage.h
@@ -1284,8 +1475,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
@@ -1308,6 +1501,14 @@ AC_DEFUN([gl_FILE_LIST], [
lib/intprops-internal.h
lib/intprops.h
lib/inttypes.in.h
+ lib/isnan.c
+ lib/isnand-nolibm.h
+ lib/isnand.c
+ lib/isnanf-nolibm.h
+ lib/isnanf.c
+ lib/isnanl-nolibm.h
+ lib/isnanl.c
+ lib/itold.c
lib/lchmod.c
lib/libc-config.h
lib/limits.in.h
@@ -1324,6 +1525,8 @@ AC_DEFUN([gl_FILE_LIST], [
lib/malloc/scratch_buffer_grow.c
lib/malloc/scratch_buffer_grow_preserve.c
lib/malloc/scratch_buffer_set_array_size.c
+ lib/math.c
+ lib/math.in.h
lib/md5-stream.c
lib/md5.c
lib/md5.h
@@ -1348,6 +1551,15 @@ AC_DEFUN([gl_FILE_LIST], [
lib/openat.h
lib/pathmax.h
lib/pipe2.c
+ lib/printf-args.c
+ lib/printf-args.h
+ lib/printf-frexp.c
+ lib/printf-frexp.h
+ lib/printf-frexpl.c
+ lib/printf-frexpl.h
+ lib/printf-parse.c
+ lib/printf-parse.h
+ lib/printf.c
lib/pselect.c
lib/pthread_sigmask.c
lib/qcopy-acl.c
@@ -1375,6 +1587,10 @@ AC_DEFUN([gl_FILE_LIST], [
lib/sig2str.h
lib/sigdescr_np.c
lib/signal.in.h
+ lib/signbitd.c
+ lib/signbitf.c
+ lib/signbitl.c
+ lib/size_max.h
lib/stat-time.c
lib/stat-time.h
lib/stdckdint.in.h
@@ -1386,6 +1602,7 @@ AC_DEFUN([gl_FILE_LIST], [
lib/stdio.in.h
lib/stdlib.in.h
lib/stpcpy.c
+ lib/stpncpy.c
lib/str-two-way.h
lib/strftime.h
lib/string.in.h
@@ -1418,15 +1635,22 @@ AC_DEFUN([gl_FILE_LIST], [
lib/utimens.c
lib/utimens.h
lib/utimensat.c
+ lib/vasnprintf.c
+ lib/vasnprintf.h
+ lib/vasprintf.c
lib/verify.h
+ lib/vfprintf.c
lib/vla.h
lib/warn-on-use.h
lib/xalloc-oversized.h
+ lib/xsize.c
+ lib/xsize.h
m4/00gnulib.m4
m4/__inline.m4
m4/absolute-header.m4
m4/acl.m4
m4/alloca.m4
+ m4/asm-underscore.m4
m4/assert_h.m4
m4/builtin-expect.m4
m4/byteswap.m4
@@ -1445,6 +1669,9 @@ AC_DEFUN([gl_FILE_LIST], [
m4/errno_h.m4
m4/euidaccess.m4
m4/execinfo.m4
+ m4/exponentd.m4
+ m4/exponentf.m4
+ m4/exponentl.m4
m4/extensions.m4
m4/extern-inline.m4
m4/faccessat.m4
@@ -1455,15 +1682,21 @@ AC_DEFUN([gl_FILE_LIST], [
m4/fdopendir.m4
m4/filemode.m4
m4/flexmember.m4
+ m4/float_h.m4
m4/fpending.m4
m4/fpieee.m4
m4/free.m4
+ m4/frexp.m4
+ m4/frexpl.m4
+ m4/fseterr.m4
m4/fstatat.m4
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
@@ -1474,9 +1707,15 @@ AC_DEFUN([gl_FILE_LIST], [
m4/group-member.m4
m4/ieee754-h.m4
m4/include_next.m4
+ m4/intmax_t.m4
m4/inttypes.m4
+ m4/inttypes_h.m4
+ m4/isnand.m4
+ m4/isnanf.m4
+ m4/isnanl.m4
m4/largefile.m4
m4/lchmod.m4
+ m4/ldexpl.m4
m4/libgmp.m4
m4/limits-h.m4
m4/locale-fr.m4
@@ -1484,6 +1723,7 @@ AC_DEFUN([gl_FILE_LIST], [
m4/malloc.m4
m4/manywarnings-c++.m4
m4/manywarnings.m4
+ m4/math_h.m4
m4/mbstate_t.m4
m4/md5.m4
m4/memmem.m4
@@ -1507,6 +1747,10 @@ AC_DEFUN([gl_FILE_LIST], [
m4/pathmax.m4
m4/pid_t.m4
m4/pipe2.m4
+ m4/printf-frexp.m4
+ m4/printf-frexpl.m4
+ m4/printf-posix-rpl.m4
+ m4/printf.m4
m4/pselect.m4
m4/pthread_sigmask.m4
m4/rawmemchr.m4
@@ -1520,6 +1764,8 @@ AC_DEFUN([gl_FILE_LIST], [
m4/sig2str.m4
m4/sigdescr_np.m4
m4/signal_h.m4
+ m4/signbit.m4
+ m4/size_max.m4
m4/socklen.m4
m4/ssize_t.m4
m4/stat-time.m4
@@ -1527,9 +1773,11 @@ AC_DEFUN([gl_FILE_LIST], [
m4/stdalign.m4
m4/stddef_h.m4
m4/stdint.m4
+ m4/stdint_h.m4
m4/stdio_h.m4
m4/stdlib_h.m4
m4/stpcpy.m4
+ m4/stpncpy.m4
m4/string_h.m4
m4/strnlen.m4
m4/strtoimax.m4
@@ -1555,10 +1803,15 @@ AC_DEFUN([gl_FILE_LIST], [
m4/utimensat.m4
m4/utimes.m4
m4/vararrays.m4
+ m4/vasnprintf.m4
+ m4/vasprintf-posix.m4
+ m4/vasprintf.m4
+ m4/vfprintf-posix.m4
m4/warn-on-use.m4
m4/warnings.m4
m4/wchar_t.m4
m4/wint_t.m4
m4/xattr.m4
+ m4/xsize.m4
m4/zzgnulib.m4
])
diff --git a/m4/intmax_t.m4 b/m4/intmax_t.m4
new file mode 100644
index 00000000000..ef32e1b9ca9
--- /dev/null
+++ b/m4/intmax_t.m4
@@ -0,0 +1,59 @@
+# intmax_t.m4 serial 9
+dnl Copyright (C) 1997-2004, 2006-2007, 2009-2023 Free Software Foundation,
+dnl Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+AC_PREREQ([2.53])
+
+# Define intmax_t to 'long' or 'long long'
+# if it is not already defined in <stdint.h> or <inttypes.h>.
+
+AC_DEFUN([gl_AC_TYPE_INTMAX_T],
+[
+ dnl For simplicity, we assume that a header file defines 'intmax_t' if and
+ dnl only if it defines 'uintmax_t'.
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ if test $gl_cv_header_inttypes_h = no && test $gl_cv_header_stdint_h = no; then
+ AC_DEFINE_UNQUOTED([intmax_t], [long long],
+ [Define to long or long long if <inttypes.h> and <stdint.h> don't define.])
+ else
+ AC_DEFINE([HAVE_INTMAX_T], [1],
+ [Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>.])
+ fi
+])
+
+dnl An alternative would be to explicitly test for 'intmax_t'.
+
+AC_DEFUN([gt_AC_TYPE_INTMAX_T],
+[
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ AC_CACHE_CHECK([for intmax_t], [gt_cv_c_intmax_t],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+#include <stddef.h>
+#include <stdlib.h>
+#if HAVE_STDINT_H_WITH_UINTMAX
+#include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H_WITH_UINTMAX
+#include <inttypes.h>
+#endif
+ ]],
+ [[intmax_t x = -1; return !x;]])],
+ [gt_cv_c_intmax_t=yes],
+ [gt_cv_c_intmax_t=no])])
+ if test $gt_cv_c_intmax_t = yes; then
+ AC_DEFINE([HAVE_INTMAX_T], [1],
+ [Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>.])
+ else
+ AC_DEFINE_UNQUOTED([intmax_t], [long long],
+ [Define to long or long long if <stdint.h> and <inttypes.h> don't define.])
+ fi
+])
diff --git a/m4/inttypes_h.m4 b/m4/inttypes_h.m4
new file mode 100644
index 00000000000..68c60e9dbb6
--- /dev/null
+++ b/m4/inttypes_h.m4
@@ -0,0 +1,29 @@
+# inttypes_h.m4 serial 10
+dnl Copyright (C) 1997-2004, 2006, 2008-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+# Define HAVE_INTTYPES_H_WITH_UINTMAX if <inttypes.h> exists,
+# doesn't clash with <sys/types.h>, and declares uintmax_t.
+
+AC_DEFUN([gl_AC_HEADER_INTTYPES_H],
+[
+ AC_CACHE_CHECK([for inttypes.h], [gl_cv_header_inttypes_h],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[
+#include <sys/types.h>
+#include <inttypes.h>
+ ]],
+ [[uintmax_t i = (uintmax_t) -1; return !i;]])],
+ [gl_cv_header_inttypes_h=yes],
+ [gl_cv_header_inttypes_h=no])])
+ if test $gl_cv_header_inttypes_h = yes; then
+ AC_DEFINE_UNQUOTED([HAVE_INTTYPES_H_WITH_UINTMAX], [1],
+ [Define if <inttypes.h> exists, doesn't clash with <sys/types.h>,
+ and declares uintmax_t. ])
+ fi
+])
diff --git a/m4/isnand.m4 b/m4/isnand.m4
new file mode 100644
index 00000000000..95346f420b7
--- /dev/null
+++ b/m4/isnand.m4
@@ -0,0 +1,96 @@
+# isnand.m4 serial 12
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Check how to get or define isnand().
+
+AC_DEFUN([gl_FUNC_ISNAND],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ ISNAND_LIBM=
+ gl_HAVE_ISNAND_NO_LIBM
+ if test $gl_cv_func_isnand_no_libm = no; then
+ gl_HAVE_ISNAND_IN_LIBM
+ if test $gl_cv_func_isnand_in_libm = yes; then
+ ISNAND_LIBM=-lm
+ fi
+ fi
+ dnl The variable gl_func_isnand set here is used by isnan.m4.
+ if test $gl_cv_func_isnand_no_libm = yes \
+ || test $gl_cv_func_isnand_in_libm = yes; then
+ gl_func_isnand=yes
+ else
+ gl_func_isnand=no
+ HAVE_ISNAND=0
+ fi
+ AC_SUBST([ISNAND_LIBM])
+])
+
+dnl Check how to get or define isnand() without linking with libm.
+
+AC_DEFUN([gl_FUNC_ISNAND_NO_LIBM],
+[
+ gl_HAVE_ISNAND_NO_LIBM
+ gl_func_isnand_no_libm=$gl_cv_func_isnand_no_libm
+ if test $gl_cv_func_isnand_no_libm = yes; then
+ AC_DEFINE([HAVE_ISNAND_IN_LIBC], [1],
+ [Define if the isnan(double) function is available in libc.])
+ fi
+])
+
+dnl Prerequisites of replacement isnand definition. It does not need -lm.
+AC_DEFUN([gl_PREREQ_ISNAND],
+[
+ AC_REQUIRE([gl_DOUBLE_EXPONENT_LOCATION])
+])
+
+dnl Test whether isnand() can be used with libm.
+
+AC_DEFUN([gl_HAVE_ISNAND_IN_LIBM],
+[
+ AC_CACHE_CHECK([whether isnan(double) can be used with libm],
+ [gl_cv_func_isnand_in_libm],
+ [
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lm"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ #if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ # undef isnand
+ # define isnand(x) __builtin_isnan ((double)(x))
+ #elif defined isnan
+ # undef isnand
+ # define isnand(x) isnan ((double)(x))
+ #endif
+ double x;]],
+ [[return isnand (x);]])],
+ [gl_cv_func_isnand_in_libm=yes],
+ [gl_cv_func_isnand_in_libm=no])
+ LIBS="$save_LIBS"
+ ])
+])
+
+AC_DEFUN([gl_HAVE_ISNAND_NO_LIBM],
+[
+ AC_CACHE_CHECK([whether isnan(double) can be used without linking with libm],
+ [gl_cv_func_isnand_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ #if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ # undef isnand
+ # define isnand(x) __builtin_isnan ((double)(x))
+ #else
+ # undef isnand
+ # define isnand(x) isnan ((double)(x))
+ #endif
+ double x;]],
+ [[return isnand (x);]])],
+ [gl_cv_func_isnand_no_libm=yes],
+ [gl_cv_func_isnand_no_libm=no])
+ ])
+])
diff --git a/m4/isnanf.m4 b/m4/isnanf.m4
new file mode 100644
index 00000000000..01f7bbd20d8
--- /dev/null
+++ b/m4/isnanf.m4
@@ -0,0 +1,197 @@
+# isnanf.m4 serial 18
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Check how to get or define isnanf().
+
+AC_DEFUN([gl_FUNC_ISNANF],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ ISNANF_LIBM=
+ gl_HAVE_ISNANF_NO_LIBM
+ if test $gl_cv_func_isnanf_no_libm = no; then
+ gl_HAVE_ISNANF_IN_LIBM
+ if test $gl_cv_func_isnanf_in_libm = yes; then
+ ISNANF_LIBM=-lm
+ fi
+ fi
+ dnl The variable gl_func_isnanf set here is used by isnan.m4.
+ if test $gl_cv_func_isnanf_no_libm = yes \
+ || test $gl_cv_func_isnanf_in_libm = yes; then
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $ISNANF_LIBM"
+ gl_ISNANF_WORKS
+ LIBS="$save_LIBS"
+ case "$gl_cv_func_isnanf_works" in
+ *yes) gl_func_isnanf=yes ;;
+ *) gl_func_isnanf=no; ISNANF_LIBM= ;;
+ esac
+ else
+ gl_func_isnanf=no
+ fi
+ if test $gl_func_isnanf != yes; then
+ HAVE_ISNANF=0
+ fi
+ AC_SUBST([ISNANF_LIBM])
+])
+
+dnl Check how to get or define isnanf() without linking with libm.
+
+AC_DEFUN([gl_FUNC_ISNANF_NO_LIBM],
+[
+ gl_HAVE_ISNANF_NO_LIBM
+ if test $gl_cv_func_isnanf_no_libm = yes; then
+ gl_ISNANF_WORKS
+ fi
+ if test $gl_cv_func_isnanf_no_libm = yes \
+ && { case "$gl_cv_func_isnanf_works" in
+ *yes) true;;
+ *) false;;
+ esac
+ }; then
+ gl_func_isnanf_no_libm=yes
+ AC_DEFINE([HAVE_ISNANF_IN_LIBC], [1],
+ [Define if the isnan(float) function is available in libc.])
+ else
+ gl_func_isnanf_no_libm=no
+ fi
+])
+
+dnl Prerequisites of replacement isnanf definition. It does not need -lm.
+AC_DEFUN([gl_PREREQ_ISNANF],
+[
+ gl_FLOAT_EXPONENT_LOCATION
+])
+
+dnl Test whether isnanf() can be used without libm.
+AC_DEFUN([gl_HAVE_ISNANF_NO_LIBM],
+[
+ AC_CACHE_CHECK([whether isnan(float) can be used without linking with libm],
+ [gl_cv_func_isnanf_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ #if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ # undef isnanf
+ # define isnanf(x) __builtin_isnan ((float)(x))
+ #elif defined isnan
+ # undef isnanf
+ # define isnanf(x) isnan ((float)(x))
+ #endif
+ float x;]],
+ [[return isnanf (x);]])],
+ [gl_cv_func_isnanf_no_libm=yes],
+ [gl_cv_func_isnanf_no_libm=no])
+ ])
+])
+
+dnl Test whether isnanf() can be used with libm.
+AC_DEFUN([gl_HAVE_ISNANF_IN_LIBM],
+[
+ AC_CACHE_CHECK([whether isnan(float) can be used with libm],
+ [gl_cv_func_isnanf_in_libm],
+ [
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lm"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ #if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ # undef isnanf
+ # define isnanf(x) __builtin_isnan ((float)(x))
+ #elif defined isnan
+ # undef isnanf
+ # define isnanf(x) isnan ((float)(x))
+ #endif
+ float x;]],
+ [[return isnanf (x);]])],
+ [gl_cv_func_isnanf_in_libm=yes],
+ [gl_cv_func_isnanf_in_libm=no])
+ LIBS="$save_LIBS"
+ ])
+])
+
+dnl Test whether isnanf() rejects Infinity (this fails on Solaris 2.5.1),
+dnl recognizes a NaN (this fails on IRIX 6.5 with cc), and recognizes a NaN
+dnl with in-memory representation 0x7fbfffff (this fails on IRIX 6.5).
+AC_DEFUN([gl_ISNANF_WORKS],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_REQUIRE([gl_FLOAT_EXPONENT_LOCATION])
+ AC_CACHE_CHECK([whether isnan(float) works], [gl_cv_func_isnanf_works],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <math.h>
+#if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+# undef isnanf
+# define isnanf(x) __builtin_isnan ((float)(x))
+#elif defined isnan
+# undef isnanf
+# define isnanf(x) isnan ((float)(x))
+#endif
+/* The Compaq (ex-DEC) C 6.4 compiler chokes on the expression 0.0 / 0.0. */
+#ifdef __DECC
+static float
+NaN ()
+{
+ static float zero = 0.0f;
+ return zero / zero;
+}
+#else
+# define NaN() (0.0f / 0.0f)
+#endif
+#define NWORDS \
+ ((sizeof (float) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+typedef union { unsigned int word[NWORDS]; float value; } memory_float;
+int main()
+{
+ int result = 0;
+
+ if (isnanf (1.0f / 0.0f))
+ result |= 1;
+
+ if (!isnanf (NaN ()))
+ result |= 2;
+
+#if defined FLT_EXPBIT0_WORD && defined FLT_EXPBIT0_BIT
+ /* The isnanf function should be immune against changes in the sign bit and
+ in the mantissa bits. The xor operation twiddles a bit that can only be
+ a sign bit or a mantissa bit. */
+ if (FLT_EXPBIT0_WORD == 0 && FLT_EXPBIT0_BIT > 0)
+ {
+ memory_float m;
+
+ m.value = NaN ();
+ /* Set the bits below the exponent to 01111...111. */
+ m.word[0] &= -1U << FLT_EXPBIT0_BIT;
+ m.word[0] |= (1U << (FLT_EXPBIT0_BIT - 1)) - 1;
+ if (!isnanf (m.value))
+ result |= 4;
+ }
+#endif
+
+ return result;
+}]])],
+ [gl_cv_func_isnanf_works=yes],
+ [gl_cv_func_isnanf_works=no],
+ [case "$host_os" in
+ irix* | solaris*) gl_cv_func_isnanf_works="guessing no" ;;
+ mingw*) # Guess yes on mingw, no on MSVC.
+ AC_EGREP_CPP([Known], [
+#ifdef __MINGW32__
+ Known
+#endif
+ ],
+ [gl_cv_func_isnanf_works="guessing yes"],
+ [gl_cv_func_isnanf_works="guessing no"])
+ ;;
+ *) gl_cv_func_isnanf_works="guessing yes" ;;
+ esac
+ ])
+ ])
+])
diff --git a/m4/isnanl.m4 b/m4/isnanl.m4
new file mode 100644
index 00000000000..bb39d02558f
--- /dev/null
+++ b/m4/isnanl.m4
@@ -0,0 +1,248 @@
+# isnanl.m4 serial 22
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_ISNANL],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ ISNANL_LIBM=
+ gl_HAVE_ISNANL_NO_LIBM
+ if test $gl_cv_func_isnanl_no_libm = no; then
+ gl_HAVE_ISNANL_IN_LIBM
+ if test $gl_cv_func_isnanl_in_libm = yes; then
+ ISNANL_LIBM=-lm
+ fi
+ fi
+ dnl The variable gl_func_isnanl set here is used by isnan.m4.
+ if test $gl_cv_func_isnanl_no_libm = yes \
+ || test $gl_cv_func_isnanl_in_libm = yes; then
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $ISNANL_LIBM"
+ gl_FUNC_ISNANL_WORKS
+ LIBS="$save_LIBS"
+ case "$gl_cv_func_isnanl_works" in
+ *yes) gl_func_isnanl=yes ;;
+ *) gl_func_isnanl=no; ISNANL_LIBM= ;;
+ esac
+ else
+ gl_func_isnanl=no
+ fi
+ if test $gl_func_isnanl != yes; then
+ HAVE_ISNANL=0
+ fi
+ AC_SUBST([ISNANL_LIBM])
+])
+
+AC_DEFUN([gl_FUNC_ISNANL_NO_LIBM],
+[
+ gl_HAVE_ISNANL_NO_LIBM
+ gl_func_isnanl_no_libm=$gl_cv_func_isnanl_no_libm
+ if test $gl_func_isnanl_no_libm = yes; then
+ gl_FUNC_ISNANL_WORKS
+ case "$gl_cv_func_isnanl_works" in
+ *yes) ;;
+ *) gl_func_isnanl_no_libm=no ;;
+ esac
+ fi
+ if test $gl_func_isnanl_no_libm = yes; then
+ AC_DEFINE([HAVE_ISNANL_IN_LIBC], [1],
+ [Define if the isnan(long double) function is available in libc.])
+ fi
+])
+
+dnl Prerequisites of replacement isnanl definition. It does not need -lm.
+AC_DEFUN([gl_PREREQ_ISNANL],
+[
+ gl_LONG_DOUBLE_EXPONENT_LOCATION
+ AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE])
+])
+
+dnl Test whether isnanl() can be used without libm.
+AC_DEFUN([gl_HAVE_ISNANL_NO_LIBM],
+[
+ AC_CACHE_CHECK([whether isnan(long double) can be used without linking with libm],
+ [gl_cv_func_isnanl_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ #if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ # undef isnanl
+ # define isnanl(x) __builtin_isnan ((long double)(x))
+ #elif defined isnan
+ # undef isnanl
+ # define isnanl(x) isnan ((long double)(x))
+ #endif
+ long double x;]],
+ [[return isnanl (x);]])],
+ [gl_cv_func_isnanl_no_libm=yes],
+ [gl_cv_func_isnanl_no_libm=no])
+ ])
+])
+
+dnl Test whether isnanl() can be used with libm.
+AC_DEFUN([gl_HAVE_ISNANL_IN_LIBM],
+[
+ AC_CACHE_CHECK([whether isnan(long double) can be used with libm],
+ [gl_cv_func_isnanl_in_libm],
+ [
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lm"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ #if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+ # undef isnanl
+ # define isnanl(x) __builtin_isnan ((long double)(x))
+ #elif defined isnan
+ # undef isnanl
+ # define isnanl(x) isnan ((long double)(x))
+ #endif
+ long double x;]],
+ [[return isnanl (x);]])],
+ [gl_cv_func_isnanl_in_libm=yes],
+ [gl_cv_func_isnanl_in_libm=no])
+ LIBS="$save_LIBS"
+ ])
+])
+
+dnl Test whether isnanl() recognizes all canonical numbers which are neither
+dnl finite nor infinite.
+AC_DEFUN([gl_FUNC_ISNANL_WORKS],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([gl_BIGENDIAN])
+ AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether isnanl works], [gl_cv_func_isnanl_works],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <float.h>
+#include <limits.h>
+#include <math.h>
+#if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+# undef isnanl
+# define isnanl(x) __builtin_isnan ((long double)(x))
+#elif defined isnan
+# undef isnanl
+# define isnanl(x) isnan ((long double)(x))
+#endif
+#define NWORDS \
+ ((sizeof (long double) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+typedef union { unsigned int word[NWORDS]; long double value; }
+ memory_long_double;
+/* On Irix 6.5, gcc 3.4.3 can't compute compile-time NaN, and needs the
+ runtime type conversion. */
+#ifdef __sgi
+static long double NaNl ()
+{
+ double zero = 0.0;
+ return zero / zero;
+}
+#else
+# define NaNl() (0.0L / 0.0L)
+#endif
+int main ()
+{
+ int result = 0;
+
+ if (!isnanl (NaNl ()))
+ result |= 1;
+
+ {
+ memory_long_double m;
+ unsigned int i;
+
+ /* The isnanl function should be immune against changes in the sign bit and
+ in the mantissa bits. The xor operation twiddles a bit that can only be
+ a sign bit or a mantissa bit (since the exponent never extends to
+ bit 31). */
+ m.value = NaNl ();
+ m.word[NWORDS / 2] ^= (unsigned int) 1 << (sizeof (unsigned int) * CHAR_BIT - 1);
+ for (i = 0; i < NWORDS; i++)
+ m.word[i] |= 1;
+ if (!isnanl (m.value))
+ result |= 1;
+ }
+
+#if ((defined __ia64 && LDBL_MANT_DIG == 64) || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_)) && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE
+/* Representation of an 80-bit 'long double' as an initializer for a sequence
+ of 'unsigned int' words. */
+# ifdef WORDS_BIGENDIAN
+# define LDBL80_WORDS(exponent,manthi,mantlo) \
+ { ((unsigned int) (exponent) << 16) | ((unsigned int) (manthi) >> 16), \
+ ((unsigned int) (manthi) << 16) | ((unsigned int) (mantlo) >> 16), \
+ (unsigned int) (mantlo) << 16 \
+ }
+# else
+# define LDBL80_WORDS(exponent,manthi,mantlo) \
+ { mantlo, manthi, exponent }
+# endif
+ { /* Quiet NaN. */
+ static memory_long_double x =
+ { LDBL80_WORDS (0xFFFF, 0xC3333333, 0x00000000) };
+ if (!isnanl (x.value))
+ result |= 2;
+ }
+ {
+ /* Signalling NaN. */
+ static memory_long_double x =
+ { LDBL80_WORDS (0xFFFF, 0x83333333, 0x00000000) };
+ if (!isnanl (x.value))
+ result |= 2;
+ }
+ /* isnanl should return something even for noncanonical values. */
+ { /* Pseudo-NaN. */
+ static memory_long_double x =
+ { LDBL80_WORDS (0xFFFF, 0x40000001, 0x00000000) };
+ if (isnanl (x.value) && !isnanl (x.value))
+ result |= 4;
+ }
+ { /* Pseudo-Infinity. */
+ static memory_long_double x =
+ { LDBL80_WORDS (0xFFFF, 0x00000000, 0x00000000) };
+ if (isnanl (x.value) && !isnanl (x.value))
+ result |= 8;
+ }
+ { /* Pseudo-Zero. */
+ static memory_long_double x =
+ { LDBL80_WORDS (0x4004, 0x00000000, 0x00000000) };
+ if (isnanl (x.value) && !isnanl (x.value))
+ result |= 16;
+ }
+ { /* Unnormalized number. */
+ static memory_long_double x =
+ { LDBL80_WORDS (0x4000, 0x63333333, 0x00000000) };
+ if (isnanl (x.value) && !isnanl (x.value))
+ result |= 32;
+ }
+ { /* Pseudo-Denormal. */
+ static memory_long_double x =
+ { LDBL80_WORDS (0x0000, 0x83333333, 0x00000000) };
+ if (isnanl (x.value) && !isnanl (x.value))
+ result |= 64;
+ }
+#endif
+
+ return result;
+}]])],
+ [gl_cv_func_isnanl_works=yes],
+ [gl_cv_func_isnanl_works=no],
+ [case "$host_os" in
+ mingw*) # Guess yes on mingw, no on MSVC.
+ AC_EGREP_CPP([Known], [
+#ifdef __MINGW32__
+ Known
+#endif
+ ],
+ [gl_cv_func_isnanl_works="guessing yes"],
+ [gl_cv_func_isnanl_works="guessing no"])
+ ;;
+ *) gl_cv_func_isnanl_works="guessing yes" ;;
+ esac
+ ])
+ ])
+])
diff --git a/m4/ldexpl.m4 b/m4/ldexpl.m4
new file mode 100644
index 00000000000..f2785d67c5b
--- /dev/null
+++ b/m4/ldexpl.m4
@@ -0,0 +1,135 @@
+# ldexpl.m4 serial 17
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_LDEXPL],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE])
+ AC_REQUIRE([gl_FUNC_ISNANL]) dnl for ISNANL_LIBM
+
+ dnl Persuade glibc <math.h> to declare ldexpl().
+ AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS])
+
+ dnl Check whether it's declared.
+ dnl Mac OS X 10.3 has ldexpl() in libc but doesn't declare it in <math.h>.
+ AC_CHECK_DECL([ldexpl], , [HAVE_DECL_LDEXPL=0], [[#include <math.h>]])
+
+ LDEXPL_LIBM=
+ if test $HAVE_DECL_LDEXPL = 1; then
+ gl_CHECK_LDEXPL_NO_LIBM
+ if test $gl_cv_func_ldexpl_no_libm = no; then
+ AC_CACHE_CHECK([whether ldexpl() can be used with libm],
+ [gl_cv_func_ldexpl_in_libm],
+ [
+ save_LIBS="$LIBS"
+ LIBS="$LIBS -lm"
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ long double x;]],
+ [[return ldexpl (x, -1) > 0;]])],
+ [gl_cv_func_ldexpl_in_libm=yes],
+ [gl_cv_func_ldexpl_in_libm=no])
+ LIBS="$save_LIBS"
+ ])
+ if test $gl_cv_func_ldexpl_in_libm = yes; then
+ LDEXPL_LIBM=-lm
+ fi
+ fi
+ if test $gl_cv_func_ldexpl_no_libm = yes \
+ || test $gl_cv_func_ldexpl_in_libm = yes; then
+ save_LIBS="$LIBS"
+ LIBS="$LIBS $LDEXPL_LIBM"
+ gl_FUNC_LDEXPL_WORKS
+ LIBS="$save_LIBS"
+ case "$gl_cv_func_ldexpl_works" in
+ *yes) gl_func_ldexpl=yes ;;
+ *) gl_func_ldexpl=no; REPLACE_LDEXPL=1 ;;
+ esac
+ else
+ gl_func_ldexpl=no
+ fi
+ if test $gl_func_ldexpl = yes; then
+ AC_DEFINE([HAVE_LDEXPL], [1],
+ [Define if the ldexpl() function is available.])
+ fi
+ fi
+ if test $HAVE_DECL_LDEXPL = 0 || test $gl_func_ldexpl = no; then
+ dnl Find libraries needed to link lib/ldexpl.c.
+ if test $HAVE_SAME_LONG_DOUBLE_AS_DOUBLE = 1; then
+ AC_REQUIRE([gl_FUNC_LDEXP])
+ LDEXPL_LIBM="$LDEXP_LIBM"
+ else
+ LDEXPL_LIBM="$ISNANL_LIBM"
+ fi
+ fi
+ AC_SUBST([LDEXPL_LIBM])
+])
+
+dnl Test whether ldexpl() can be used without linking with libm.
+dnl Set gl_cv_func_ldexpl_no_libm to 'yes' or 'no' accordingly.
+AC_DEFUN([gl_CHECK_LDEXPL_NO_LIBM],
+[
+ AC_CACHE_CHECK([whether ldexpl() can be used without linking with libm],
+ [gl_cv_func_ldexpl_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ long double x;]],
+ [[return ldexpl (x, -1) > 0;]])],
+ [gl_cv_func_ldexpl_no_libm=yes],
+ [gl_cv_func_ldexpl_no_libm=no])
+ ])
+])
+
+dnl Test whether ldexpl() works on finite numbers (this fails on AIX 5.1
+dnl and Mac OS X 10.4/PowerPC).
+AC_DEFUN([gl_FUNC_LDEXPL_WORKS],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether ldexpl works], [gl_cv_func_ldexpl_works],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <math.h>
+extern
+#ifdef __cplusplus
+"C"
+#endif
+long double ldexpl (long double, int);
+int main()
+{
+ int result = 0;
+ {
+ volatile long double x = 1.0;
+ volatile long double y = ldexpl (x, -1);
+ if (y != 0.5L)
+ result |= 1;
+ }
+ {
+ volatile long double x = 1.73205L;
+ volatile long double y = ldexpl (x, 0);
+ if (y != x)
+ result |= 2;
+ }
+ return result;
+}]])],
+ [gl_cv_func_ldexpl_works=yes],
+ [gl_cv_func_ldexpl_works=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ aix | aix[3-6]*) gl_cv_func_ldexpl_works="guessing no" ;;
+ # Guess yes on native Windows.
+ mingw*) gl_cv_func_ldexpl_works="guessing yes" ;;
+ *) gl_cv_func_ldexpl_works="guessing yes" ;;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
diff --git a/m4/math_h.m4 b/m4/math_h.m4
new file mode 100644
index 00000000000..d2e90ff1eb6
--- /dev/null
+++ b/m4/math_h.m4
@@ -0,0 +1,391 @@
+# math_h.m4 serial 125
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN_ONCE([gl_MATH_H],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ gl_CHECK_NEXT_HEADERS([math.h])
+
+ AC_CACHE_CHECK([whether NAN macro works], [gl_cv_header_math_nan_works],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <math.h>]],
+ [[/* Solaris 10 has a broken definition of NAN. Other platforms
+ fail to provide NAN, or provide it only in C99 mode; this
+ test only needs to fail when NAN is provided but wrong. */
+ float f = 1.0f;
+#ifdef NAN
+ f = NAN;
+#endif
+ return f == 0;]])],
+ [gl_cv_header_math_nan_works=yes],
+ [gl_cv_header_math_nan_works=no])])
+ if test $gl_cv_header_math_nan_works = no; then
+ REPLACE_NAN=1
+ fi
+ AC_CACHE_CHECK([whether HUGE_VAL works], [gl_cv_header_math_huge_val_works],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <math.h>]],
+ [[/* Solaris 10 has a broken definition of HUGE_VAL. */
+ double d = HUGE_VAL;
+ return d == 0;]])],
+ [gl_cv_header_math_huge_val_works=yes],
+ [gl_cv_header_math_huge_val_works=no])])
+ if test $gl_cv_header_math_huge_val_works = no; then
+ REPLACE_HUGE_VAL=1
+ fi
+
+ dnl Check for declarations of anything we want to poison if the
+ dnl corresponding gnulib module is not in use.
+ gl_WARN_ON_USE_PREPARE([[#include <math.h>]],
+ [acosf acosl asinf asinl atanf atanl
+ cbrt cbrtf cbrtl ceilf ceill copysign copysignf copysignl cosf cosl coshf
+ expf expl exp2 exp2f exp2l expm1 expm1f expm1l
+ fabsf fabsl floorf floorl fma fmaf fmal
+ fmod fmodf fmodl frexpf frexpl hypotf hypotl
+ ilogb ilogbf ilogbl
+ ldexpf ldexpl
+ log logf logl log10 log10f log10l log1p log1pf log1pl log2 log2f log2l
+ logb logbf logbl
+ modf modff modfl powf
+ remainder remainderf remainderl
+ rint rintf rintl round roundf roundl sinf sinl sinhf sqrtf sqrtl
+ tanf tanl tanhf trunc truncf truncl])
+])
+
+# gl_MATH_MODULE_INDICATOR([modulename])
+# sets the shell variable that indicates the presence of the given module
+# to a C preprocessor expression that will evaluate to 1.
+# This macro invocation must not occur in macros that are AC_REQUIREd.
+AC_DEFUN([gl_MATH_MODULE_INDICATOR],
+[
+ dnl Ensure to expand the default settings once only.
+ gl_MATH_H_REQUIRE_DEFAULTS
+ gl_MODULE_INDICATOR_SET_VARIABLE([$1])
+ dnl Define it also as a C macro, for the benefit of the unit tests.
+ gl_MODULE_INDICATOR_FOR_TESTS([$1])
+])
+
+# Initializes the default values for AC_SUBSTed shell variables.
+# This macro must not be AC_REQUIREd. It must only be invoked, and only
+# outside of macros or in macros that are not AC_REQUIREd.
+AC_DEFUN([gl_MATH_H_REQUIRE_DEFAULTS],
+[
+ m4_defun(GL_MODULE_INDICATOR_PREFIX[_MATH_H_MODULE_INDICATOR_DEFAULTS], [
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ACOSF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ACOSL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ASINF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ASINL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ATANF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ATANL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ATAN2F])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CBRT])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CBRTF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CBRTL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CEIL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CEILF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_CEILL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COPYSIGN])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COPYSIGNF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COPYSIGNL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COSF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COSL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_COSHF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXP2])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXP2F])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXP2L])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPM1])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPM1F])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_EXPM1L])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FABSF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FABSL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FLOOR])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FLOORF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FLOORL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMA])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMAF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMAL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMOD])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMODF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FMODL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FREXPF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FREXP])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_FREXPL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_HYPOT])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_HYPOTF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_HYPOTL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ILOGB])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ILOGBF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ILOGBL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISFINITE])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISINF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNAN])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNANF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNAND])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ISNANL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LDEXPF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LDEXPL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG10])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG10F])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG10L])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG1P])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG1PF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG1PL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG2])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG2F])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOG2L])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGB])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGBF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_LOGBL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MODF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MODFF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MODFL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_POWF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_REMAINDER])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_REMAINDERF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_REMAINDERL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_RINT])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_RINTF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_RINTL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ROUND])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ROUNDF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_ROUNDL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SIGNBIT])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SINF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SINL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SINHF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SQRTF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_SQRTL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TANF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TANL])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TANHF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TRUNC])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TRUNCF])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_TRUNCL])
+ dnl Support Microsoft deprecated alias function names by default.
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_J0], [1])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_J1], [1])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_JN], [1])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_Y0], [1])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_Y1], [1])
+ gl_MODULE_INDICATOR_INIT_VARIABLE([GNULIB_MDA_YN], [1])
+ ])
+ m4_require(GL_MODULE_INDICATOR_PREFIX[_MATH_H_MODULE_INDICATOR_DEFAULTS])
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+])
+
+AC_DEFUN([gl_MATH_H_DEFAULTS],
+[
+ dnl Assume proper GNU behavior unless another module says otherwise.
+ HAVE_ACOSF=1; AC_SUBST([HAVE_ACOSF])
+ HAVE_ACOSL=1; AC_SUBST([HAVE_ACOSL])
+ HAVE_ASINF=1; AC_SUBST([HAVE_ASINF])
+ HAVE_ASINL=1; AC_SUBST([HAVE_ASINL])
+ HAVE_ATANF=1; AC_SUBST([HAVE_ATANF])
+ HAVE_ATANL=1; AC_SUBST([HAVE_ATANL])
+ HAVE_ATAN2F=1; AC_SUBST([HAVE_ATAN2F])
+ HAVE_CBRT=1; AC_SUBST([HAVE_CBRT])
+ HAVE_CBRTF=1; AC_SUBST([HAVE_CBRTF])
+ HAVE_CBRTL=1; AC_SUBST([HAVE_CBRTL])
+ HAVE_COPYSIGN=1; AC_SUBST([HAVE_COPYSIGN])
+ HAVE_COPYSIGNL=1; AC_SUBST([HAVE_COPYSIGNL])
+ HAVE_COSF=1; AC_SUBST([HAVE_COSF])
+ HAVE_COSL=1; AC_SUBST([HAVE_COSL])
+ HAVE_COSHF=1; AC_SUBST([HAVE_COSHF])
+ HAVE_EXPF=1; AC_SUBST([HAVE_EXPF])
+ HAVE_EXPL=1; AC_SUBST([HAVE_EXPL])
+ HAVE_EXPM1=1; AC_SUBST([HAVE_EXPM1])
+ HAVE_EXPM1F=1; AC_SUBST([HAVE_EXPM1F])
+ HAVE_FABSF=1; AC_SUBST([HAVE_FABSF])
+ HAVE_FABSL=1; AC_SUBST([HAVE_FABSL])
+ HAVE_FMA=1; AC_SUBST([HAVE_FMA])
+ HAVE_FMAF=1; AC_SUBST([HAVE_FMAF])
+ HAVE_FMAL=1; AC_SUBST([HAVE_FMAL])
+ HAVE_FMODF=1; AC_SUBST([HAVE_FMODF])
+ HAVE_FMODL=1; AC_SUBST([HAVE_FMODL])
+ HAVE_FREXPF=1; AC_SUBST([HAVE_FREXPF])
+ HAVE_HYPOTF=1; AC_SUBST([HAVE_HYPOTF])
+ HAVE_HYPOTL=1; AC_SUBST([HAVE_HYPOTL])
+ HAVE_ILOGB=1; AC_SUBST([HAVE_ILOGB])
+ HAVE_ILOGBF=1; AC_SUBST([HAVE_ILOGBF])
+ HAVE_ILOGBL=1; AC_SUBST([HAVE_ILOGBL])
+ HAVE_ISNANF=1; AC_SUBST([HAVE_ISNANF])
+ HAVE_ISNAND=1; AC_SUBST([HAVE_ISNAND])
+ HAVE_ISNANL=1; AC_SUBST([HAVE_ISNANL])
+ HAVE_LDEXPF=1; AC_SUBST([HAVE_LDEXPF])
+ HAVE_LOGF=1; AC_SUBST([HAVE_LOGF])
+ HAVE_LOGL=1; AC_SUBST([HAVE_LOGL])
+ HAVE_LOG10F=1; AC_SUBST([HAVE_LOG10F])
+ HAVE_LOG10L=1; AC_SUBST([HAVE_LOG10L])
+ HAVE_LOG1P=1; AC_SUBST([HAVE_LOG1P])
+ HAVE_LOG1PF=1; AC_SUBST([HAVE_LOG1PF])
+ HAVE_LOG1PL=1; AC_SUBST([HAVE_LOG1PL])
+ HAVE_LOGBF=1; AC_SUBST([HAVE_LOGBF])
+ HAVE_LOGBL=1; AC_SUBST([HAVE_LOGBL])
+ HAVE_MODFF=1; AC_SUBST([HAVE_MODFF])
+ HAVE_MODFL=1; AC_SUBST([HAVE_MODFL])
+ HAVE_POWF=1; AC_SUBST([HAVE_POWF])
+ HAVE_REMAINDER=1; AC_SUBST([HAVE_REMAINDER])
+ HAVE_REMAINDERF=1; AC_SUBST([HAVE_REMAINDERF])
+ HAVE_RINT=1; AC_SUBST([HAVE_RINT])
+ HAVE_RINTL=1; AC_SUBST([HAVE_RINTL])
+ HAVE_SINF=1; AC_SUBST([HAVE_SINF])
+ HAVE_SINL=1; AC_SUBST([HAVE_SINL])
+ HAVE_SINHF=1; AC_SUBST([HAVE_SINHF])
+ HAVE_SQRTF=1; AC_SUBST([HAVE_SQRTF])
+ HAVE_SQRTL=1; AC_SUBST([HAVE_SQRTL])
+ HAVE_TANF=1; AC_SUBST([HAVE_TANF])
+ HAVE_TANL=1; AC_SUBST([HAVE_TANL])
+ HAVE_TANHF=1; AC_SUBST([HAVE_TANHF])
+ HAVE_DECL_ACOSL=1; AC_SUBST([HAVE_DECL_ACOSL])
+ HAVE_DECL_ASINL=1; AC_SUBST([HAVE_DECL_ASINL])
+ HAVE_DECL_ATANL=1; AC_SUBST([HAVE_DECL_ATANL])
+ HAVE_DECL_CBRTF=1; AC_SUBST([HAVE_DECL_CBRTF])
+ HAVE_DECL_CBRTL=1; AC_SUBST([HAVE_DECL_CBRTL])
+ HAVE_DECL_CEILF=1; AC_SUBST([HAVE_DECL_CEILF])
+ HAVE_DECL_CEILL=1; AC_SUBST([HAVE_DECL_CEILL])
+ HAVE_DECL_COPYSIGNF=1; AC_SUBST([HAVE_DECL_COPYSIGNF])
+ HAVE_DECL_COSL=1; AC_SUBST([HAVE_DECL_COSL])
+ HAVE_DECL_EXPL=1; AC_SUBST([HAVE_DECL_EXPL])
+ HAVE_DECL_EXP2=1; AC_SUBST([HAVE_DECL_EXP2])
+ HAVE_DECL_EXP2F=1; AC_SUBST([HAVE_DECL_EXP2F])
+ HAVE_DECL_EXP2L=1; AC_SUBST([HAVE_DECL_EXP2L])
+ HAVE_DECL_EXPM1L=1; AC_SUBST([HAVE_DECL_EXPM1L])
+ HAVE_DECL_FLOORF=1; AC_SUBST([HAVE_DECL_FLOORF])
+ HAVE_DECL_FLOORL=1; AC_SUBST([HAVE_DECL_FLOORL])
+ HAVE_DECL_FREXPL=1; AC_SUBST([HAVE_DECL_FREXPL])
+ HAVE_DECL_LDEXPL=1; AC_SUBST([HAVE_DECL_LDEXPL])
+ HAVE_DECL_LOGL=1; AC_SUBST([HAVE_DECL_LOGL])
+ HAVE_DECL_LOG10L=1; AC_SUBST([HAVE_DECL_LOG10L])
+ HAVE_DECL_LOG2=1; AC_SUBST([HAVE_DECL_LOG2])
+ HAVE_DECL_LOG2F=1; AC_SUBST([HAVE_DECL_LOG2F])
+ HAVE_DECL_LOG2L=1; AC_SUBST([HAVE_DECL_LOG2L])
+ HAVE_DECL_LOGB=1; AC_SUBST([HAVE_DECL_LOGB])
+ HAVE_DECL_REMAINDER=1; AC_SUBST([HAVE_DECL_REMAINDER])
+ HAVE_DECL_REMAINDERL=1; AC_SUBST([HAVE_DECL_REMAINDERL])
+ HAVE_DECL_RINTF=1; AC_SUBST([HAVE_DECL_RINTF])
+ HAVE_DECL_ROUND=1; AC_SUBST([HAVE_DECL_ROUND])
+ HAVE_DECL_ROUNDF=1; AC_SUBST([HAVE_DECL_ROUNDF])
+ HAVE_DECL_ROUNDL=1; AC_SUBST([HAVE_DECL_ROUNDL])
+ HAVE_DECL_SINL=1; AC_SUBST([HAVE_DECL_SINL])
+ HAVE_DECL_SQRTL=1; AC_SUBST([HAVE_DECL_SQRTL])
+ HAVE_DECL_TANL=1; AC_SUBST([HAVE_DECL_TANL])
+ HAVE_DECL_TRUNC=1; AC_SUBST([HAVE_DECL_TRUNC])
+ HAVE_DECL_TRUNCF=1; AC_SUBST([HAVE_DECL_TRUNCF])
+ HAVE_DECL_TRUNCL=1; AC_SUBST([HAVE_DECL_TRUNCL])
+ REPLACE_ACOSF=0; AC_SUBST([REPLACE_ACOSF])
+ REPLACE_ASINF=0; AC_SUBST([REPLACE_ASINF])
+ REPLACE_ATANF=0; AC_SUBST([REPLACE_ATANF])
+ REPLACE_ATAN2F=0; AC_SUBST([REPLACE_ATAN2F])
+ REPLACE_CBRTF=0; AC_SUBST([REPLACE_CBRTF])
+ REPLACE_CBRTL=0; AC_SUBST([REPLACE_CBRTL])
+ REPLACE_CEIL=0; AC_SUBST([REPLACE_CEIL])
+ REPLACE_CEILF=0; AC_SUBST([REPLACE_CEILF])
+ REPLACE_CEILL=0; AC_SUBST([REPLACE_CEILL])
+ REPLACE_COSF=0; AC_SUBST([REPLACE_COSF])
+ REPLACE_COSHF=0; AC_SUBST([REPLACE_COSHF])
+ REPLACE_EXPF=0; AC_SUBST([REPLACE_EXPF])
+ REPLACE_EXPL=0; AC_SUBST([REPLACE_EXPL])
+ REPLACE_EXPM1=0; AC_SUBST([REPLACE_EXPM1])
+ REPLACE_EXPM1F=0; AC_SUBST([REPLACE_EXPM1F])
+ REPLACE_EXPM1L=0; AC_SUBST([REPLACE_EXPM1L])
+ REPLACE_EXP2=0; AC_SUBST([REPLACE_EXP2])
+ REPLACE_EXP2L=0; AC_SUBST([REPLACE_EXP2L])
+ REPLACE_FABSL=0; AC_SUBST([REPLACE_FABSL])
+ REPLACE_FLOOR=0; AC_SUBST([REPLACE_FLOOR])
+ REPLACE_FLOORF=0; AC_SUBST([REPLACE_FLOORF])
+ REPLACE_FLOORL=0; AC_SUBST([REPLACE_FLOORL])
+ REPLACE_FMA=0; AC_SUBST([REPLACE_FMA])
+ REPLACE_FMAF=0; AC_SUBST([REPLACE_FMAF])
+ REPLACE_FMAL=0; AC_SUBST([REPLACE_FMAL])
+ REPLACE_FMOD=0; AC_SUBST([REPLACE_FMOD])
+ REPLACE_FMODF=0; AC_SUBST([REPLACE_FMODF])
+ REPLACE_FMODL=0; AC_SUBST([REPLACE_FMODL])
+ REPLACE_FREXPF=0; AC_SUBST([REPLACE_FREXPF])
+ REPLACE_FREXP=0; AC_SUBST([REPLACE_FREXP])
+ REPLACE_FREXPL=0; AC_SUBST([REPLACE_FREXPL])
+ REPLACE_HUGE_VAL=0; AC_SUBST([REPLACE_HUGE_VAL])
+ REPLACE_HYPOT=0; AC_SUBST([REPLACE_HYPOT])
+ REPLACE_HYPOTF=0; AC_SUBST([REPLACE_HYPOTF])
+ REPLACE_HYPOTL=0; AC_SUBST([REPLACE_HYPOTL])
+ REPLACE_ILOGB=0; AC_SUBST([REPLACE_ILOGB])
+ REPLACE_ILOGBF=0; AC_SUBST([REPLACE_ILOGBF])
+ REPLACE_ILOGBL=0; AC_SUBST([REPLACE_ILOGBL])
+ REPLACE_ISFINITE=0; AC_SUBST([REPLACE_ISFINITE])
+ REPLACE_ISINF=0; AC_SUBST([REPLACE_ISINF])
+ REPLACE_ISNAN=0; AC_SUBST([REPLACE_ISNAN])
+ REPLACE_LDEXPL=0; AC_SUBST([REPLACE_LDEXPL])
+ REPLACE_LOG=0; AC_SUBST([REPLACE_LOG])
+ REPLACE_LOGF=0; AC_SUBST([REPLACE_LOGF])
+ REPLACE_LOGL=0; AC_SUBST([REPLACE_LOGL])
+ REPLACE_LOG10=0; AC_SUBST([REPLACE_LOG10])
+ REPLACE_LOG10F=0; AC_SUBST([REPLACE_LOG10F])
+ REPLACE_LOG10L=0; AC_SUBST([REPLACE_LOG10L])
+ REPLACE_LOG1P=0; AC_SUBST([REPLACE_LOG1P])
+ REPLACE_LOG1PF=0; AC_SUBST([REPLACE_LOG1PF])
+ REPLACE_LOG1PL=0; AC_SUBST([REPLACE_LOG1PL])
+ REPLACE_LOG2=0; AC_SUBST([REPLACE_LOG2])
+ REPLACE_LOG2F=0; AC_SUBST([REPLACE_LOG2F])
+ REPLACE_LOG2L=0; AC_SUBST([REPLACE_LOG2L])
+ REPLACE_LOGB=0; AC_SUBST([REPLACE_LOGB])
+ REPLACE_LOGBF=0; AC_SUBST([REPLACE_LOGBF])
+ REPLACE_LOGBL=0; AC_SUBST([REPLACE_LOGBL])
+ REPLACE_MODF=0; AC_SUBST([REPLACE_MODF])
+ REPLACE_MODFF=0; AC_SUBST([REPLACE_MODFF])
+ REPLACE_MODFL=0; AC_SUBST([REPLACE_MODFL])
+ REPLACE_NAN=0; AC_SUBST([REPLACE_NAN])
+ REPLACE_REMAINDER=0; AC_SUBST([REPLACE_REMAINDER])
+ REPLACE_REMAINDERF=0; AC_SUBST([REPLACE_REMAINDERF])
+ REPLACE_REMAINDERL=0; AC_SUBST([REPLACE_REMAINDERL])
+ REPLACE_RINTL=0; AC_SUBST([REPLACE_RINTL])
+ REPLACE_ROUND=0; AC_SUBST([REPLACE_ROUND])
+ REPLACE_ROUNDF=0; AC_SUBST([REPLACE_ROUNDF])
+ REPLACE_ROUNDL=0; AC_SUBST([REPLACE_ROUNDL])
+ REPLACE_SIGNBIT=0; AC_SUBST([REPLACE_SIGNBIT])
+ REPLACE_SIGNBIT_USING_BUILTINS=0; AC_SUBST([REPLACE_SIGNBIT_USING_BUILTINS])
+ REPLACE_SINF=0; AC_SUBST([REPLACE_SINF])
+ REPLACE_SINHF=0; AC_SUBST([REPLACE_SINHF])
+ REPLACE_SQRTF=0; AC_SUBST([REPLACE_SQRTF])
+ REPLACE_SQRTL=0; AC_SUBST([REPLACE_SQRTL])
+ REPLACE_TANF=0; AC_SUBST([REPLACE_TANF])
+ REPLACE_TANHF=0; AC_SUBST([REPLACE_TANHF])
+ REPLACE_TRUNC=0; AC_SUBST([REPLACE_TRUNC])
+ REPLACE_TRUNCF=0; AC_SUBST([REPLACE_TRUNCF])
+ REPLACE_TRUNCL=0; AC_SUBST([REPLACE_TRUNCL])
+])
+
+# gl_LONG_DOUBLE_VS_DOUBLE
+# determines whether 'long double' and 'double' have the same representation.
+# Sets variable HAVE_SAME_LONG_DOUBLE_AS_DOUBLE to 0 or 1, and defines
+# HAVE_SAME_LONG_DOUBLE_AS_DOUBLE accordingly.
+# The currently known platforms where this is the case are:
+# Linux/HPPA, Minix 3.1.8, AIX 5, AIX 6 and 7 with xlc, MSVC 9.
+AC_DEFUN([gl_LONG_DOUBLE_VS_DOUBLE],
+[
+ AC_CACHE_CHECK([whether long double and double are the same],
+ [gl_cv_long_double_equals_double],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM([[#include <float.h>]],
+ [[typedef int check[sizeof (long double) == sizeof (double)
+ && LDBL_MANT_DIG == DBL_MANT_DIG
+ && LDBL_MAX_EXP == DBL_MAX_EXP
+ && LDBL_MIN_EXP == DBL_MIN_EXP
+ ? 1 : -1];
+ ]])],
+ [gl_cv_long_double_equals_double=yes],
+ [gl_cv_long_double_equals_double=no])
+ ])
+ if test $gl_cv_long_double_equals_double = yes; then
+ AC_DEFINE([HAVE_SAME_LONG_DOUBLE_AS_DOUBLE], [1],
+ [Define to 1 if 'long double' and 'double' have the same representation.])
+ HAVE_SAME_LONG_DOUBLE_AS_DOUBLE=1
+ else
+ HAVE_SAME_LONG_DOUBLE_AS_DOUBLE=0
+ fi
+ AC_SUBST([HAVE_SAME_LONG_DOUBLE_AS_DOUBLE])
+])
diff --git a/m4/ndk-build.m4 b/m4/ndk-build.m4
new file mode 100644
index 00000000000..8769e294452
--- /dev/null
+++ b/m4/ndk-build.m4
@@ -0,0 +1,480 @@
+dnl Copyright (C) 2023 Free Software Foundation, Inc.
+dnl This file is part of GNU Emacs.
+
+dnl GNU Emacs is free software: you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation, either version 3 of the License, or
+dnl (at your option) any later version.
+
+dnl GNU Emacs is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+
+dnl You should have received a copy of the GNU General Public License
+dnl along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
+
+# Support for building Emacs with dependencies using the Android NDK
+# build system.
+
+AC_ARG_WITH([ndk_path],
+ [AS_HELP_STRING([--with-ndk-path],
+ [find Android libraries in these directories])])
+
+AC_ARG_WITH([ndk_cxx_shared],
+ [AS_HELP_STRING([--with-ndk-cxx-shared],
+ [name of the C++ standard library included with the NDK])])
+
+AC_ARG_WITH([ndk_cxx],
+ [AS_HELP_STRING([--with-ndk-cxx],
+ [name of the C++ compiler included with the NDK])])
+
+# ndk_INIT(ABI, API, DIR, CFLAGS)
+# -------------------------------
+# Initialize the Android NDK. ABI is the ABI being built for.
+# API is the API version being built for.
+# CFLAGS is a list of compiler flags.
+# As a side effect, set the variable ndk_INITIALIZED to true.
+# DIR should be a directory containing the Makefile.in actually
+# implementing the Android NDK build system.
+
+AC_DEFUN([ndk_INIT],
+[
+# Look for Android.mk files.
+ndk_module_files=
+for file in $with_ndk_path; do
+ if test -f $file/Android.mk; then
+ ndk_module_files="$ndk_module_files$file/Android.mk "
+ fi
+done
+
+AC_REQUIRE_AUX_FILE([ndk-build-helper.mk])
+ndk_AUX_DIR=$ac_aux_dir
+ndk_ABI=$1
+ndk_MODULES=
+ndk_MAKEFILES=
+ndk_INITIALIZED=yes
+ndk_API=$2
+ndk_DIR=$3
+ndk_ANY_CXX=
+ndk_BUILD_CFLAGS="$4"
+ndk_working_cxx=no
+
+AS_CASE(["$ndk_ABI"],
+ [*arm64*], [ndk_ARCH=arm64],
+ [*arm*], [ndk_ARCH=arm],
+ [*x86_64*], [ndk_ARCH=x86_64],
+ [*x86*], [ndk_ARCH=x86],
+ [*mips64*], [ndk_ARCH=mips64],
+ [*mips*], [ndk_ARCH=mips],
+ [AC_MSG_ERROR([Failed to determine Android device architecture])])
+
+# This is a map between pkg-config style package names and Android
+# ones.
+
+ndk_package_map="libwebpdemux:webpdemux libxml-2.0:libxml2 jansson:libjansson"
+ndk_package_map="$ndk_package_map sqlite3:libsqlite_static_minimal"
+ndk_package_map="$ndk_package_map MagickWand:libmagickwand-7 lcms2:liblcms2"
+
+# Replace ndk_module with the appropriate Android module name if it is
+# found in ndk_package_map.
+
+ndk_replace_pkg_config_package () {
+ for ndk_stuff in $ndk_package_map; do
+ ndk_key=`AS_ECHO([$ndk_stuff]) | cut -d: -f1`
+ ndk_value=`AS_ECHO([$ndk_stuff]) | cut -d: -f2`
+
+ if test "$ndk_key" = "$ndk_module"; then
+ ndk_module="$ndk_value"
+ break
+ fi
+ done
+}
+
+# Run the Makefile helper script for the Android.mk file.
+
+ndk_run_test () {
+ # Figure out where the helper Makefile is.
+ ndk_build_helper_file="${ndk_AUX_DIR}ndk-build-helper.mk"
+ ndk_module_extract_awk="${ndk_AUX_DIR}ndk-module-extract.awk"
+ ndk_dir=`AS_DIRNAME([$ndk_android_mk])`
+
+ # Now call Make with the right arguments.
+ "$MAKE" -s -f "$ndk_build_helper_file" EMACS_SRCDIR=`pwd` \
+ EMACS_ABI="$ndk_ABI" ANDROID_MAKEFILE="$ndk_android_mk" \
+ NDK_BUILD_DIR="$ndk_DIR" NDK_ROOT="/tmp" \
+ ANDROID_MODULE_DIRECTORY="$ndk_dir" BUILD_AUXDIR=$ndk_AUX_DIR \
+ NDK_BUILD_ARCH="$ndk_ARCH" 2>&AS_MESSAGE_LOG_FD >conftest.ndk
+
+ # Read the output.
+ cat conftest.ndk | awk -f "$ndk_module_extract_awk" MODULE="$ndk_module"
+
+ # Remove the temporary file.
+ rm -f conftest.ndk
+}
+
+# ndk_parse_pkg_config_string PKG_CONFIG_STRING
+# ---------------------------------------------
+# Parse a pkg-config style list of modules. Place the resulting list
+# in ndk_modules.
+
+ndk_parse_pkg_config_string () {
+ ndk_input=[$]1
+ ndk_modules=
+ while test -n "$ndk_input"; do
+ ndk_str=`AS_ECHO_N(["$ndk_input"]) | cut -f1 -d' '`
+ ndk_input=`AS_ECHO_N(["$ndk_input"]) | cut -s -f2- -d' '`
+
+ if test "$ndk_str" = ">=" || test "$ndk_str" = "<=" \
+ || test "$ndk_str" = ">" || test "$ndk_str" = "<" \
+ || test "$ndk_str" = "!="; then
+ ndk_input=`AS_ECHO_N(["$ndk_input"]) | cut -s -f2- -d' '`
+ else
+ ndk_modules="$ndk_modules$ndk_str "
+ fi
+ done
+}
+
+# ndk_resolve_import_module MODULE
+# --------------------------------
+# Resolve MODULE, a single import. Prepend its makefile to
+# ndk_MAKEFILES if found. Also, prepend all includes to the variable
+# ndk_import_includes.
+
+ndk_resolve_import_module () {
+ module_name=
+ ndk_module=[$]1
+
+ AC_MSG_CHECKING([for imported $ndk_module])
+
+ for ndk_android_mk in $ndk_module_files; do
+ # Read this Android.mk file. Set NDK_ROOT to /tmp: the Android in
+ # tree build system sets it to a meaning value, but build files
+ # just use it to test whether or not the NDK is being used.
+ ndk_commands=`ndk_run_test`
+
+ AS_IF([test -n "${ndk_commands//\n }"], [eval "$ndk_commands"])
+
+ if test -n "$module_name"; then
+ break;
+ fi
+ done
+
+ AS_IF([test -z "$module_name"],
+ [AC_MSG_RESULT([no])
+ AC_MSG_ERROR([The module currently being built depends on [$]1, but \
+that could not be found in the list of directories specified in \
+`--with-ndk-path'.])])
+
+ if test -n "$module_cxx_deps"; then
+ ndk_ANY_CXX=yes
+ fi
+
+ AS_IF([test "$ndk_ANY_CXX" = "yes" && test -z "$with_ndk_cxx_shared"],
+ [AC_MSG_ERROR([The module [$]1 requires the C++ standard library \
+(libc++_shared.so), but it was not found.])])
+
+ AS_IF([test "$ndk_ANY_CXX" = "yes" && test "$ndk_working_cxx" != "yes"],
+ [AC_MSG_ERROR([The module [$]1 requires the C++ standard library \
+(libc++_shared.so), but a working C++ compiler was not found.])])
+
+ AC_MSG_RESULT([yes])
+
+ # Make sure the module is prepended.
+ ndk_MAKEFILES="$ndk_android_mk $ndk_MAKEFILES"
+ ndk_import_includes="$module_includes $ndk_import_includes"
+
+ # Now recursively resolve this module's imports.
+ for ndk_module in $module_imports; do
+ ndk_resolve_import_module $ndk_module
+ done
+}
+
+# ndk_filter_cc_for_cxx
+# ---------------------
+# Run through $CC, removing any options that are not suitable for
+# use in a C++ compiler.
+
+ndk_filter_cc_for_cxx () {
+ for ndk_word in $CC; do
+ AS_CASE([$ndk_word], [*-std=*], [],
+ [AS_ECHO_N(["$ndk_word "])])
+ done
+}
+
+# ndk_subst_cc_onto_cxx
+# ---------------------
+# Print the value of $CXX, followed by any innocent looking options
+# in $CC.
+
+ndk_subst_cc_onto_cxx () {
+ AS_ECHO_N(["$CXX "])
+ ndk_flag=
+ for ndk_word in `AS_ECHO_N(["$CC"]) | cut -s -f2- -d' '`; do
+ AS_IF([test "$ndk_flag" = "yes"],
+ [AS_ECHO_N(["$ndk_word "])
+ ndk_flag=no],
+ [AS_CASE([$ndk_word],
+ [*-sysroot=*],
+ [AS_ECHO_N(["$ndk_word "])],
+ [*-isystem*],
+ [AS_ECHO_N(["$ndk_word "])
+ ndk_flag=yes],
+ [*-sysroot*],
+ [AS_ECHO_N(["$ndk_word "])
+ ndk_flag=yes],
+ [-D__ANDROID_API__*],
+ [AS_ECHO_N(["$ndk_word "])])])
+ done
+}
+
+# Look for a suitable ar and ranlib in the same directory as the C
+# compiler.
+ndk_cc_firstword=`AS_ECHO(["$CC"]) | cut -d' ' -f1`
+ndk_where_cc=`which $ndk_cc_firstword`
+ndk_ar_search_path=$PATH
+ndk_ranlib_search_path=$RANLIB
+
+# First, try to find $host_alias-ar in PATH.
+AC_PATH_PROGS([AR], [$host_alias-ar], [], [$ndk_ar_search_path])
+
+AS_IF([test -z "$AR"],[
+ # Next, try finding either that or llvm-ar in the directory holding
+ # CC.
+ ndk_ar_search_path="`AS_DIRNAME([$ndk_where_cc])`:$ndk_ar_search_path"
+ AC_PATH_PROGS([AR], [$host_alias-ar llvm-ar], [], [$ndk_ar_search_path])])
+
+# First, try to find $host_alias-ranlib in PATH.
+AC_PATH_PROGS([RANLIB], [$host_alias-ranlib], [], [$ndk_ranlib_search_path])
+
+AS_IF([test -z "$RANLIB"],[
+ # Next, try finding either that or llvm-ranlib in the directory
+ # holding CC.
+ ndk_ranlib_search_path="`AS_DIRNAME([$ndk_where_cc])`:$ndk_ranlib_search_path"
+ AC_PATH_PROGS([RANLIB], [$host_alias-ranlib llvm-ranlib], [],
+ [$ndk_ranlib_search_path])])
+
+NDK_BUILD_NASM=
+
+# Next, try to find nasm on x86. This doesn't ship with the NDK.
+AS_IF([test "$ndk_ARCH" = "x86" || test "$ndk_ARCH" = "x86_64"],
+ [AC_CHECK_PROGS([NDK_BUILD_NASM], [nasm])])
+
+# Look for a file named ``libc++_shared.so'' in a subdirectory of
+# $ndk_where_cc if it was not specified.
+AC_MSG_CHECKING([for libc++_shared.so])
+
+ndk_where_toolchain=
+AS_IF([test -z "$with_ndk_cxx_shared" && test -n "$ndk_where_cc"],[
+ # Find the NDK root directory. Go to $ndk_where_cc.
+ SAVE_PWD=`pwd`
+ cd `AS_DIRNAME(["$ndk_where_cc"])`
+
+ # Now, keep moving backwards until pwd ends with ``toolchains''.
+ while :; do
+ if test "`pwd`" = "/"; then
+ cd "$SAVE_PWD"
+ break
+ fi
+
+ ndk_pwd=`pwd`
+ if test "`AS_BASENAME([$ndk_pwd])`" = "toolchains"; then
+ ndk_where_toolchain=$ndk_pwd
+ cd "$SAVE_PWD"
+ break
+ fi
+
+ cd ..
+ done
+
+ ndk_matching_libcxx_shared_so=
+
+ # The toolchain directory should be in "$ndk_where_toolchain".
+ AS_IF([test -n "$ndk_where_toolchain"],[
+ # Now, look in the directory behind it.
+ ndk_cxx_shared_so=`find "$ndk_where_toolchain" -name libc++_shared.so`
+
+ # Look for one with the correct architecture.
+ for ndk_candidate in $ndk_cxx_shared_so; do
+ AS_CASE([$ndk_candidate],
+ [*arm-linux-android*],
+ [AS_IF([test "$ndk_ARCH" = "arm"],
+ [ndk_matching_libcxx_shared_so=$ndk_candidate])],
+ [*aarch64-linux-android*],
+ [AS_IF([test "$ndk_ARCH" = "arm64"],
+ [ndk_matching_libcxx_shared_so=$ndk_candidate])],
+ [*i[[3-6]]86-linux-android*],
+ [AS_IF([test "$ndk_ARCH" = "x86"],
+ [ndk_matching_libcxx_shared_so=$ndk_candidate])],
+ [*x86_64-linux-android*],
+ [AS_IF([test "$ndk_ARCH" = "x86_64"],
+ [ndk_matching_libcxx_shared_so=$ndk_candidate])])
+
+ AS_IF([test -n "$ndk_matching_libcxx_shared_so"],
+ [with_ndk_cxx_shared=$ndk_matching_libcxx_shared_so])
+ done])])
+
+AS_IF([test -z "$with_ndk_cxx_shared"],[AC_MSG_RESULT([no])
+ AC_MSG_WARN([The C++ standard library could not be found. \
+If you try to build Emacs with a dependency that requires the C++ standard \
+library, Emacs will not build correctly, unless you manually specify the \
+name of an appropriate ``libc++_shared.so'' binary.])],
+ [AC_MSG_RESULT([$with_ndk_cxx_shared])])
+
+ndk_CXX_SHARED=$with_ndk_cxx_shared
+
+# These variables have now been found. Now look for a C++ compiler.
+# Upon failure, pretend the C compiler is a C++ compiler and use that
+# instead.
+
+ndk_cc_name=`AS_BASENAME(["${ndk_cc_firstword}"])`
+ndk_cxx_name=
+
+AS_CASE([$ndk_cc_name], [*-gcc],
+ [ndk_cxx_name=`AS_ECHO([$ndk_cc_name]) | sed 's/gcc/g++/'`],
+ [ndk_cxx_name="${ndk_cc_name}++"])
+
+AS_IF([test -n "$with_ndk_cxx"], [CXX=$with_ndk_cxx],
+ [AC_PATH_PROGS([CXX], [$ndk_cxx_name],
+ [], [`AS_DIRNAME(["$ndk_where_cc"])`:$PATH])
+ AS_IF([test -z "$CXX"], [CXX=`ndk_filter_cc_for_cxx`],
+ [CXX=`ndk_subst_cc_onto_cxx`])])
+])
+
+# ndk_LATE
+# --------
+# Perform late initialization of the ndk-build system by checking for
+# required C and C++ headers.
+
+AC_DEFUN([ndk_LATE],
+[dnl
+dnl This calls AC_REQUIRE([AC_PROG_CXX]), leading to configure looking
+dnl for a C++ compiler. However, the language is not restored
+dnl afterwards if not `$ndk_INITIALIZED'.
+AS_IF([test "$ndk_INITIALIZED" = "yes"],[
+ AS_IF([test -n "$CXX"], [AC_LANG_PUSH([C++])
+ AC_CHECK_HEADER([string], [ndk_working_cxx=yes],
+ [AC_MSG_WARN([Your C++ compiler is not properly set up, and\
+ the standard library headers could not be found.])])
+ AC_LANG_POP([C++])])])
+dnl Thus, manually switch back to C here.
+AC_LANG([C])
+])
+
+# ndk_SEARCH_MODULE(MODULE, NAME, ACTION-IF-FOUND, [ACTION-IF-NOT-FOUND])
+# -----------------------------------------------------------------------
+# Search for a module named MODULE in `with_ndk_path'. Add the file
+# name of the module's Android.mk file to the variable ndk_MAKEFILES.
+# Set NAME_CFLAGS and NAME_LIBS to the appropriate values. Then, call
+# ACTION-IF-FOUND, or ACTION-IF-NOT-FOUND upon failure.
+#
+# Resolve any imports specified by MODULE, and expand AC_MSG_ERROR
+# with a suitable error message if imports were not found.
+AC_DEFUN([ndk_SEARCH_MODULE],
+[
+module_name=
+ndk_module=$1
+ndk_replace_pkg_config_package
+AC_MSG_CHECKING([for Android.mk that builds $ndk_module])
+
+for ndk_android_mk in $ndk_module_files; do
+ # Read this Android.mk file. Set NDK_ROOT to /tmp: the Android in
+ # tree build system sets it to a meaning value, but build files just
+ # use it to test whether or not the NDK is being used.
+ ndk_commands=`ndk_run_test`
+
+ eval "$ndk_commands"
+ if test -n "$module_name"; then
+ break;
+ fi
+done
+
+if test -z "$module_name"; then
+ AC_MSG_RESULT([no])
+ $4
+else
+ if test -n "$module_cxx_deps"; then
+ ndk_ANY_CXX=yes
+ fi
+
+ AS_IF([test "$ndk_ANY_CXX" = "yes" && test -z "$with_ndk_cxx_shared"],
+ [AC_MSG_ERROR([The module $1 requires the C++ standard library \
+(libc++_shared.so), but it was not found.])])
+
+ AS_IF([test "$ndk_ANY_CXX" = "yes" && test "$ndk_working_cxx" != "yes"],
+ [AC_MSG_ERROR([The module [$]1 requires the C++ standard library \
+(libc++_shared.so), but a working C++ compiler was not found.])])
+
+ $2[]_CFLAGS="[$]$2[]_CFLAGS $module_cflags $module_includes"
+ $2[]_LIBS="[$]$2[]_LIBS $module_ldflags"
+ ndk_MAKEFILES="$ndk_MAKEFILES $ndk_android_mk"
+ ndk_MODULES="$ndk_MODULES $module_target"
+ AC_MSG_RESULT([yes])
+ $3
+
+ # Now, resolve imports. Make sure the imports' Makefiles comes
+ # before ndk_MAKEFILES; likewise for its includes.
+ ndk_import_includes=
+ for ndk_module in $module_imports; do
+ ndk_resolve_import_module $ndk_module
+ $2[]_CFLAGS="$ndk_import_includes [$]$2[]_CFLAGS"
+ done
+fi
+])
+
+# ndk_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
+# [ACTION-IF-NOT-FOUND])
+# --------------------------------------------------------------
+# Just like `PKG_CHECK_MODULES'. However, it uses the ndk-build
+# system instead.
+
+AC_DEFUN([ndk_CHECK_MODULES],
+[
+ ndk_modules=
+ ndk_parse_pkg_config_string "$2"
+ ndk_found=no
+
+ for module in $ndk_modules; do
+ ndk_SEARCH_MODULE([$module], [$1], [ndk_found=yes], [ndk_found=no])
+ done
+
+ AS_IF([test "$ndk_found" = "yes"],[$3],[$4])
+])
+
+# ndk_CONFIG_FILES
+# -------------------------------------------------------------
+# Write out the NDK build Makefile with the appropriate variables
+# set if the NDK has been initialized.
+
+AC_DEFUN_ONCE([ndk_CONFIG_FILES],
+[
+ if test "$ndk_INITIALIZED" = "yes"; then
+ NDK_BUILD_ANDROID_MK="$ndk_MAKEFILES"
+ NDK_BUILD_ARCH=$ndk_ARCH
+ NDK_BUILD_ABI=$ndk_ABI
+ NDK_BUILD_SDK=$ndk_API
+ NDK_BUILD_CC=$CC
+ NDK_BUILD_CXX=$CXX
+ NDK_BUILD_AR=$AR
+ NDK_BUILD_MODULES="$ndk_MODULES"
+ NDK_BUILD_CXX_SHARED="$ndk_CXX_SHARED"
+ NDK_BUILD_ANY_CXX_MODULE=$ndk_ANY_CXX
+ NDK_BUILD_CFLAGS="$ndk_BUILD_CFLAGS"
+
+ AC_SUBST([NDK_BUILD_ANDROID_MK])
+ AC_SUBST([NDK_BUILD_ARCH])
+ AC_SUBST([NDK_BUILD_ABI])
+ AC_SUBST([NDK_BUILD_SDK])
+ AC_SUBST([NDK_BUILD_CC])
+ AC_SUBST([NDK_BUILD_CXX])
+ AC_SUBST([NDK_BUILD_AR])
+ AC_SUBST([NDK_BUILD_NASM])
+ AC_SUBST([NDK_BUILD_MODULES])
+ AC_SUBST([NDK_BUILD_CXX_SHARED])
+ AC_SUBST([NDK_BUILD_ANY_CXX_MODULE])
+ AC_SUBST([NDK_BUILD_CFLAGS])
+
+ AC_CONFIG_FILES([$ndk_DIR/Makefile])
+ AC_CONFIG_FILES([$ndk_DIR/ndk-build.mk])
+ fi
+])
diff --git a/m4/printf-frexp.m4 b/m4/printf-frexp.m4
new file mode 100644
index 00000000000..8f5844a2c1c
--- /dev/null
+++ b/m4/printf-frexp.m4
@@ -0,0 +1,38 @@
+# printf-frexp.m4 serial 5
+dnl Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Check how to define printf_frexp() without linking with libm.
+
+AC_DEFUN([gl_FUNC_PRINTF_FREXP],
+[
+ AC_REQUIRE([gl_CHECK_FREXP_NO_LIBM])
+ if test $gl_cv_func_frexp_no_libm = yes; then
+ gl_FUNC_FREXP_WORKS
+ case "$gl_cv_func_frexp_works" in
+ *yes)
+ AC_DEFINE([HAVE_FREXP_IN_LIBC], [1],
+ [Define if the frexp function is available in libc.])
+ ;;
+ esac
+ fi
+
+ AC_CACHE_CHECK([whether ldexp can be used without linking with libm],
+ [gl_cv_func_ldexp_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ double x;
+ int y;]],
+ [[return ldexp (x, y) < 1;]])],
+ [gl_cv_func_ldexp_no_libm=yes],
+ [gl_cv_func_ldexp_no_libm=no])
+ ])
+ if test $gl_cv_func_ldexp_no_libm = yes; then
+ AC_DEFINE([HAVE_LDEXP_IN_LIBC], [1],
+ [Define if the ldexp function is available in libc.])
+ fi
+])
diff --git a/m4/printf-frexpl.m4 b/m4/printf-frexpl.m4
new file mode 100644
index 00000000000..aee170ffd39
--- /dev/null
+++ b/m4/printf-frexpl.m4
@@ -0,0 +1,48 @@
+# printf-frexpl.m4 serial 10
+dnl Copyright (C) 2007, 2009-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Check how to define printf_frexpl() without linking with libm.
+
+AC_DEFUN([gl_FUNC_PRINTF_FREXPL],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE])
+
+ dnl Subset of gl_FUNC_FREXPL_NO_LIBM.
+ gl_CHECK_FREXPL_NO_LIBM
+ if test $gl_cv_func_frexpl_no_libm = yes; then
+ gl_FUNC_FREXPL_WORKS
+ case "$gl_cv_func_frexpl_works" in
+ *yes) gl_func_frexpl_no_libm=yes ;;
+ *) gl_func_frexpl_no_libm=no; REPLACE_FREXPL=1 ;;
+ esac
+ else
+ gl_func_frexpl_no_libm=no
+ dnl Set REPLACE_FREXPL here because the system may have frexpl in libm.
+ REPLACE_FREXPL=1
+ fi
+ if test $gl_func_frexpl_no_libm = yes; then
+ AC_DEFINE([HAVE_FREXPL_IN_LIBC], [1],
+ [Define if the frexpl function is available in libc.])
+ dnl Also check whether it's declared.
+ dnl Mac OS X 10.3 has frexpl() in libc but doesn't declare it in <math.h>.
+ AC_CHECK_DECL([frexpl], , [HAVE_DECL_FREXPL=0], [[#include <math.h>]])
+ fi
+
+ gl_CHECK_LDEXPL_NO_LIBM
+ if test $gl_cv_func_ldexpl_no_libm = yes; then
+ gl_FUNC_LDEXPL_WORKS
+ case "$gl_cv_func_ldexpl_works" in
+ *yes)
+ AC_DEFINE([HAVE_LDEXPL_IN_LIBC], [1],
+ [Define if the ldexpl function is available in libc.])
+ dnl Also check whether it's declared.
+ dnl Mac OS X 10.3 has ldexpl() in libc but doesn't declare it in <math.h>.
+ AC_CHECK_DECL([ldexpl], , [HAVE_DECL_LDEXPL=0], [[#include <math.h>]])
+ ;;
+ esac
+ fi
+])
diff --git a/m4/printf-posix-rpl.m4 b/m4/printf-posix-rpl.m4
new file mode 100644
index 00000000000..36156d43502
--- /dev/null
+++ b/m4/printf-posix-rpl.m4
@@ -0,0 +1,26 @@
+# printf-posix-rpl.m4 serial 4
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_PRINTF_POSIX],
+[
+ AC_REQUIRE([gl_FUNC_VFPRINTF_POSIX])
+ if test $gl_cv_func_vfprintf_posix = no; then
+ gl_REPLACE_PRINTF
+ fi
+])
+
+AC_DEFUN([gl_REPLACE_PRINTF],
+[
+ AC_REQUIRE([gl_STDIO_H_DEFAULTS])
+ AC_REQUIRE([gl_ASM_SYMBOL_PREFIX])
+ AC_LIBOBJ([printf])
+ REPLACE_PRINTF=1
+ AC_DEFINE([REPLACE_PRINTF_POSIX], [1],
+ [Define if printf is overridden by a POSIX compliant gnulib implementation.])
+ gl_PREREQ_PRINTF
+])
+
+AC_DEFUN([gl_PREREQ_PRINTF], [:])
diff --git a/m4/printf.m4 b/m4/printf.m4
new file mode 100644
index 00000000000..de98a870e98
--- /dev/null
+++ b/m4/printf.m4
@@ -0,0 +1,1728 @@
+# printf.m4 serial 74
+dnl Copyright (C) 2003, 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl Test whether the *printf family of functions supports the 'j', 'z', 't',
+dnl 'L' size specifiers. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_sizes_c99.
+
+AC_DEFUN([gl_PRINTF_SIZES_C99],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([gl_AC_HEADER_STDINT_H])
+ AC_REQUIRE([gl_AC_HEADER_INTTYPES_H])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports size specifiers as in C99],
+ [gl_cv_func_printf_sizes_c99],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#if HAVE_STDINT_H_WITH_UINTMAX
+# include <stdint.h>
+#endif
+#if HAVE_INTTYPES_H_WITH_UINTMAX
+# include <inttypes.h>
+#endif
+static char buf[100];
+int main ()
+{
+ int result = 0;
+#if HAVE_STDINT_H_WITH_UINTMAX || HAVE_INTTYPES_H_WITH_UINTMAX
+ buf[0] = '\0';
+ if (sprintf (buf, "%ju %d", (uintmax_t) 12345671, 33, 44, 55) < 0
+ || strcmp (buf, "12345671 33") != 0)
+ result |= 1;
+#else
+ result |= 1;
+#endif
+ buf[0] = '\0';
+ if (sprintf (buf, "%zu %d", (size_t) 12345672, 33, 44, 55) < 0
+ || strcmp (buf, "12345672 33") != 0)
+ result |= 2;
+ buf[0] = '\0';
+ if (sprintf (buf, "%tu %d", (ptrdiff_t) 12345673, 33, 44, 55) < 0
+ || strcmp (buf, "12345673 33") != 0)
+ result |= 4;
+ buf[0] = '\0';
+ if (sprintf (buf, "%Lg %d", (long double) 1.5, 33, 44, 55) < 0
+ || strcmp (buf, "1.5 33") != 0)
+ result |= 8;
+ return result;
+}]])],
+ [gl_cv_func_printf_sizes_c99=yes],
+ [gl_cv_func_printf_sizes_c99=no],
+ [
+ case "$host_os" in
+changequote(,)dnl
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_printf_sizes_c99="guessing yes";;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_printf_sizes_c99="guessing yes";;
+ # Guess yes on FreeBSD >= 5.
+ freebsd[1-4].*) gl_cv_func_printf_sizes_c99="guessing no";;
+ freebsd* | kfreebsd*) gl_cv_func_printf_sizes_c99="guessing yes";;
+ midnightbsd*) gl_cv_func_printf_sizes_c99="guessing yes";;
+ # Guess yes on Mac OS X >= 10.3.
+ darwin[1-6].*) gl_cv_func_printf_sizes_c99="guessing no";;
+ darwin*) gl_cv_func_printf_sizes_c99="guessing yes";;
+ # Guess yes on OpenBSD >= 3.9.
+ openbsd[1-2].* | openbsd3.[0-8] | openbsd3.[0-8].*)
+ gl_cv_func_printf_sizes_c99="guessing no";;
+ openbsd*) gl_cv_func_printf_sizes_c99="guessing yes";;
+ # Guess yes on Solaris >= 2.10.
+ solaris2.[1-9][0-9]*) gl_cv_func_printf_sizes_c99="guessing yes";;
+ solaris*) gl_cv_func_printf_sizes_c99="guessing no";;
+ # Guess yes on NetBSD >= 3.
+ netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*)
+ gl_cv_func_printf_sizes_c99="guessing no";;
+ netbsd*) gl_cv_func_printf_sizes_c99="guessing yes";;
+ # Guess yes on Android.
+ linux*-android*) gl_cv_func_printf_sizes_c99="guessing yes";;
+changequote([,])dnl
+ # Guess yes on MSVC, no on mingw.
+ mingw*) AC_EGREP_CPP([Known], [
+#ifdef _MSC_VER
+ Known
+#endif
+ ],
+ [gl_cv_func_printf_sizes_c99="guessing yes"],
+ [gl_cv_func_printf_sizes_c99="guessing no"])
+ ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_printf_sizes_c99="$gl_cross_guess_normal";;
+ esac
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports 'long double'
+dnl arguments together with the 'L' size specifier. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_long_double.
+
+AC_DEFUN([gl_PRINTF_LONG_DOUBLE],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports 'long double' arguments],
+ [gl_cv_func_printf_long_double],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+static char buf[10000];
+int main ()
+{
+ int result = 0;
+ buf[0] = '\0';
+ if (sprintf (buf, "%Lf %d", 1.75L, 33, 44, 55) < 0
+ || strcmp (buf, "1.750000 33") != 0)
+ result |= 1;
+ buf[0] = '\0';
+ if (sprintf (buf, "%Le %d", 1.75L, 33, 44, 55) < 0
+ || strcmp (buf, "1.750000e+00 33") != 0)
+ result |= 2;
+ buf[0] = '\0';
+ if (sprintf (buf, "%Lg %d", 1.75L, 33, 44, 55) < 0
+ || strcmp (buf, "1.75 33") != 0)
+ result |= 4;
+ return result;
+}]])],
+ [gl_cv_func_printf_long_double=yes],
+ [gl_cv_func_printf_long_double=no],
+ [case "$host_os" in
+ # Guess no on BeOS.
+ beos*) gl_cv_func_printf_long_double="guessing no";;
+ # Guess yes on Android.
+ linux*-android*) gl_cv_func_printf_long_double="guessing yes";;
+ # Guess yes on MSVC, no on mingw.
+ mingw*) AC_EGREP_CPP([Known], [
+#ifdef _MSC_VER
+ Known
+#endif
+ ],
+ [gl_cv_func_printf_long_double="guessing yes"],
+ [gl_cv_func_printf_long_double="guessing no"])
+ ;;
+ *) gl_cv_func_printf_long_double="guessing yes";;
+ esac
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports infinite and NaN
+dnl 'double' arguments and negative zero arguments in the %f, %e, %g
+dnl directives. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_infinite.
+
+AC_DEFUN([gl_PRINTF_INFINITE],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports infinite 'double' arguments],
+ [gl_cv_func_printf_infinite],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+static int
+strisnan (const char *string, size_t start_index, size_t end_index)
+{
+ if (start_index < end_index)
+ {
+ if (string[start_index] == '-')
+ start_index++;
+ if (start_index + 3 <= end_index
+ && memcmp (string + start_index, "nan", 3) == 0)
+ {
+ start_index += 3;
+ if (start_index == end_index
+ || (string[start_index] == '(' && string[end_index - 1] == ')'))
+ return 1;
+ }
+ }
+ return 0;
+}
+static int
+have_minus_zero ()
+{
+ static double plus_zero = 0.0;
+ double minus_zero = - plus_zero;
+ return memcmp (&plus_zero, &minus_zero, sizeof (double)) != 0;
+}
+static char buf[10000];
+static double zero = 0.0;
+int main ()
+{
+ int result = 0;
+ if (sprintf (buf, "%f", 1.0 / zero) < 0
+ || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0))
+ result |= 1;
+ if (sprintf (buf, "%f", -1.0 / zero) < 0
+ || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0))
+ result |= 1;
+ if (sprintf (buf, "%f", zero / zero) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 2;
+ if (sprintf (buf, "%e", 1.0 / zero) < 0
+ || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0))
+ result |= 4;
+ if (sprintf (buf, "%e", -1.0 / zero) < 0
+ || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0))
+ result |= 4;
+ if (sprintf (buf, "%e", zero / zero) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 8;
+ if (sprintf (buf, "%g", 1.0 / zero) < 0
+ || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0))
+ result |= 16;
+ if (sprintf (buf, "%g", -1.0 / zero) < 0
+ || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0))
+ result |= 16;
+ if (sprintf (buf, "%g", zero / zero) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 32;
+ /* This test fails on HP-UX 10.20. */
+ if (have_minus_zero ())
+ if (sprintf (buf, "%g", - zero) < 0
+ || strcmp (buf, "-0") != 0)
+ result |= 64;
+ return result;
+}]])],
+ [gl_cv_func_printf_infinite=yes],
+ [gl_cv_func_printf_infinite=no],
+ [
+ case "$host_os" in
+changequote(,)dnl
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_printf_infinite="guessing yes";;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_printf_infinite="guessing yes";;
+ # Guess yes on FreeBSD >= 6.
+ freebsd[1-5].*) gl_cv_func_printf_infinite="guessing no";;
+ freebsd* | kfreebsd*) gl_cv_func_printf_infinite="guessing yes";;
+ midnightbsd*) gl_cv_func_printf_infinite="guessing yes";;
+ # Guess yes on Mac OS X >= 10.3.
+ darwin[1-6].*) gl_cv_func_printf_infinite="guessing no";;
+ darwin*) gl_cv_func_printf_infinite="guessing yes";;
+ # Guess yes on HP-UX >= 11.
+ hpux[7-9]* | hpux10*) gl_cv_func_printf_infinite="guessing no";;
+ hpux*) gl_cv_func_printf_infinite="guessing yes";;
+ # Guess yes on NetBSD >= 3.
+ netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*)
+ gl_cv_func_printf_infinite="guessing no";;
+ netbsd*) gl_cv_func_printf_infinite="guessing yes";;
+ # Guess yes on OpenBSD >= 6.0.
+ openbsd[1-5].*) gl_cv_func_printf_infinite="guessing no";;
+ openbsd*) gl_cv_func_printf_infinite="guessing yes";;
+ # Guess yes on BeOS.
+ beos*) gl_cv_func_printf_infinite="guessing yes";;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_printf_infinite="guessing no";;
+changequote([,])dnl
+ # Guess yes on MSVC, no on mingw.
+ mingw*) AC_EGREP_CPP([Known], [
+#ifdef _MSC_VER
+ Known
+#endif
+ ],
+ [gl_cv_func_printf_infinite="guessing yes"],
+ [gl_cv_func_printf_infinite="guessing no"])
+ ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_printf_infinite="$gl_cross_guess_normal";;
+ esac
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports infinite and NaN
+dnl 'long double' arguments in the %f, %e, %g directives. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_infinite_long_double.
+
+AC_DEFUN([gl_PRINTF_INFINITE_LONG_DOUBLE],
+[
+ AC_REQUIRE([gl_PRINTF_LONG_DOUBLE])
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([gl_BIGENDIAN])
+ AC_REQUIRE([gl_LONG_DOUBLE_VS_DOUBLE])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ dnl The user can set or unset the variable gl_printf_safe to indicate
+ dnl that he wishes a safe handling of non-IEEE-754 'long double' values.
+ if test -n "$gl_printf_safe"; then
+ AC_DEFINE([CHECK_PRINTF_SAFE], [1],
+ [Define if you wish *printf() functions that have a safe handling of
+ non-IEEE-754 'long double' values.])
+ fi
+ case "$gl_cv_func_printf_long_double" in
+ *yes)
+ AC_CACHE_CHECK([whether printf supports infinite 'long double' arguments],
+ [gl_cv_func_printf_infinite_long_double],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+]GL_NOCRASH[
+#include <float.h>
+#include <stdio.h>
+#include <string.h>
+static int
+strisnan (const char *string, size_t start_index, size_t end_index)
+{
+ if (start_index < end_index)
+ {
+ if (string[start_index] == '-')
+ start_index++;
+ if (start_index + 3 <= end_index
+ && memcmp (string + start_index, "nan", 3) == 0)
+ {
+ start_index += 3;
+ if (start_index == end_index
+ || (string[start_index] == '(' && string[end_index - 1] == ')'))
+ return 1;
+ }
+ }
+ return 0;
+}
+static char buf[10000];
+static long double zeroL = 0.0L;
+int main ()
+{
+ int result = 0;
+ nocrash_init();
+ if (sprintf (buf, "%Lf", 1.0L / zeroL) < 0
+ || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0))
+ result |= 1;
+ if (sprintf (buf, "%Lf", -1.0L / zeroL) < 0
+ || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0))
+ result |= 1;
+ if (sprintf (buf, "%Lf", zeroL / zeroL) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 1;
+ if (sprintf (buf, "%Le", 1.0L / zeroL) < 0
+ || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0))
+ result |= 1;
+ if (sprintf (buf, "%Le", -1.0L / zeroL) < 0
+ || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0))
+ result |= 1;
+ if (sprintf (buf, "%Le", zeroL / zeroL) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 1;
+ if (sprintf (buf, "%Lg", 1.0L / zeroL) < 0
+ || (strcmp (buf, "inf") != 0 && strcmp (buf, "infinity") != 0))
+ result |= 1;
+ if (sprintf (buf, "%Lg", -1.0L / zeroL) < 0
+ || (strcmp (buf, "-inf") != 0 && strcmp (buf, "-infinity") != 0))
+ result |= 1;
+ if (sprintf (buf, "%Lg", zeroL / zeroL) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 1;
+#if CHECK_PRINTF_SAFE && ((defined __ia64 && LDBL_MANT_DIG == 64) || (defined __x86_64__ || defined __amd64__) || (defined __i386 || defined __i386__ || defined _I386 || defined _M_IX86 || defined _X86_)) && !HAVE_SAME_LONG_DOUBLE_AS_DOUBLE
+/* Representation of an 80-bit 'long double' as an initializer for a sequence
+ of 'unsigned int' words. */
+# ifdef WORDS_BIGENDIAN
+# define LDBL80_WORDS(exponent,manthi,mantlo) \
+ { ((unsigned int) (exponent) << 16) | ((unsigned int) (manthi) >> 16), \
+ ((unsigned int) (manthi) << 16) | ((unsigned int) (mantlo) >> 16), \
+ (unsigned int) (mantlo) << 16 \
+ }
+# else
+# define LDBL80_WORDS(exponent,manthi,mantlo) \
+ { mantlo, manthi, exponent }
+# endif
+ { /* Quiet NaN. */
+ static union { unsigned int word[4]; long double value; } x =
+ { LDBL80_WORDS (0xFFFF, 0xC3333333, 0x00000000) };
+ if (sprintf (buf, "%Lf", x.value) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 2;
+ if (sprintf (buf, "%Le", x.value) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 2;
+ if (sprintf (buf, "%Lg", x.value) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 2;
+ }
+ {
+ /* Signalling NaN. */
+ static union { unsigned int word[4]; long double value; } x =
+ { LDBL80_WORDS (0xFFFF, 0x83333333, 0x00000000) };
+ if (sprintf (buf, "%Lf", x.value) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 2;
+ if (sprintf (buf, "%Le", x.value) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 2;
+ if (sprintf (buf, "%Lg", x.value) < 0
+ || !strisnan (buf, 0, strlen (buf)))
+ result |= 2;
+ }
+ { /* Pseudo-NaN. */
+ static union { unsigned int word[4]; long double value; } x =
+ { LDBL80_WORDS (0xFFFF, 0x40000001, 0x00000000) };
+ if (sprintf (buf, "%Lf", x.value) <= 0)
+ result |= 4;
+ if (sprintf (buf, "%Le", x.value) <= 0)
+ result |= 4;
+ if (sprintf (buf, "%Lg", x.value) <= 0)
+ result |= 4;
+ }
+ { /* Pseudo-Infinity. */
+ static union { unsigned int word[4]; long double value; } x =
+ { LDBL80_WORDS (0xFFFF, 0x00000000, 0x00000000) };
+ if (sprintf (buf, "%Lf", x.value) <= 0)
+ result |= 8;
+ if (sprintf (buf, "%Le", x.value) <= 0)
+ result |= 8;
+ if (sprintf (buf, "%Lg", x.value) <= 0)
+ result |= 8;
+ }
+ { /* Pseudo-Zero. */
+ static union { unsigned int word[4]; long double value; } x =
+ { LDBL80_WORDS (0x4004, 0x00000000, 0x00000000) };
+ if (sprintf (buf, "%Lf", x.value) <= 0)
+ result |= 16;
+ if (sprintf (buf, "%Le", x.value) <= 0)
+ result |= 16;
+ if (sprintf (buf, "%Lg", x.value) <= 0)
+ result |= 16;
+ }
+ { /* Unnormalized number. */
+ static union { unsigned int word[4]; long double value; } x =
+ { LDBL80_WORDS (0x4000, 0x63333333, 0x00000000) };
+ if (sprintf (buf, "%Lf", x.value) <= 0)
+ result |= 32;
+ if (sprintf (buf, "%Le", x.value) <= 0)
+ result |= 32;
+ if (sprintf (buf, "%Lg", x.value) <= 0)
+ result |= 32;
+ }
+ { /* Pseudo-Denormal. */
+ static union { unsigned int word[4]; long double value; } x =
+ { LDBL80_WORDS (0x0000, 0x83333333, 0x00000000) };
+ if (sprintf (buf, "%Lf", x.value) <= 0)
+ result |= 64;
+ if (sprintf (buf, "%Le", x.value) <= 0)
+ result |= 64;
+ if (sprintf (buf, "%Lg", x.value) <= 0)
+ result |= 64;
+ }
+#endif
+ return result;
+}]])],
+ [gl_cv_func_printf_infinite_long_double=yes],
+ [gl_cv_func_printf_infinite_long_double=no],
+ [case "$host_cpu" in
+ # Guess no on ia64, x86_64, i386.
+ ia64 | x86_64 | i*86) gl_cv_func_printf_infinite_long_double="guessing no";;
+ *)
+ case "$host_os" in
+changequote(,)dnl
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_printf_infinite_long_double="guessing yes";;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_printf_infinite_long_double="guessing yes";;
+ # Guess yes on FreeBSD >= 6.
+ freebsd[1-5].*) gl_cv_func_printf_infinite_long_double="guessing no";;
+ freebsd* | kfreebsd*) gl_cv_func_printf_infinite_long_double="guessing yes";;
+ midnightbsd*) gl_cv_func_printf_infinite_long_double="guessing yes";;
+ # Guess yes on HP-UX >= 11.
+ hpux[7-9]* | hpux10*) gl_cv_func_printf_infinite_long_double="guessing no";;
+ hpux*) gl_cv_func_printf_infinite_long_double="guessing yes";;
+ # Guess yes on OpenBSD >= 6.0.
+ openbsd[1-5].*) gl_cv_func_printf_infinite_long_double="guessing no";;
+ openbsd*) gl_cv_func_printf_infinite_long_double="guessing yes";;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_printf_infinite_long_double="guessing no";;
+changequote([,])dnl
+ # Guess yes on MSVC, no on mingw.
+ mingw*) AC_EGREP_CPP([Known], [
+#ifdef _MSC_VER
+ Known
+#endif
+ ],
+ [gl_cv_func_printf_infinite_long_double="guessing yes"],
+ [gl_cv_func_printf_infinite_long_double="guessing no"])
+ ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_printf_infinite_long_double="$gl_cross_guess_normal";;
+ esac
+ ;;
+ esac
+ ])
+ ])
+ ;;
+ *)
+ gl_cv_func_printf_infinite_long_double="irrelevant"
+ ;;
+ esac
+])
+
+dnl Test whether the *printf family of functions supports the 'a' and 'A'
+dnl conversion specifier for hexadecimal output of floating-point numbers.
+dnl (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_directive_a.
+
+AC_DEFUN([gl_PRINTF_DIRECTIVE_A],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports the 'a' and 'A' directives],
+ [gl_cv_func_printf_directive_a],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+static char buf[100];
+static double zero = 0.0;
+int main ()
+{
+ int result = 0;
+ if (sprintf (buf, "%a %d", 3.1416015625, 33, 44, 55) < 0
+ || (strcmp (buf, "0x1.922p+1 33") != 0
+ && strcmp (buf, "0x3.244p+0 33") != 0
+ && strcmp (buf, "0x6.488p-1 33") != 0
+ && strcmp (buf, "0xc.91p-2 33") != 0))
+ result |= 1;
+ if (sprintf (buf, "%A %d", -3.1416015625, 33, 44, 55) < 0
+ || (strcmp (buf, "-0X1.922P+1 33") != 0
+ && strcmp (buf, "-0X3.244P+0 33") != 0
+ && strcmp (buf, "-0X6.488P-1 33") != 0
+ && strcmp (buf, "-0XC.91P-2 33") != 0))
+ result |= 2;
+ /* This catches a FreeBSD 13.0 bug: it doesn't round. */
+ if (sprintf (buf, "%.2a %d", 1.51, 33, 44, 55) < 0
+ || (strcmp (buf, "0x1.83p+0 33") != 0
+ && strcmp (buf, "0x3.05p-1 33") != 0
+ && strcmp (buf, "0x6.0ap-2 33") != 0
+ && strcmp (buf, "0xc.14p-3 33") != 0))
+ result |= 4;
+ /* This catches a Mac OS X 10.12.4 (Darwin 16.5) bug: it doesn't round. */
+ if (sprintf (buf, "%.0a %d", 1.51, 33, 44, 55) < 0
+ || (strcmp (buf, "0x2p+0 33") != 0
+ && strcmp (buf, "0x3p-1 33") != 0
+ && strcmp (buf, "0x6p-2 33") != 0
+ && strcmp (buf, "0xcp-3 33") != 0))
+ result |= 4;
+ /* This catches a FreeBSD 6.1 bug. See
+ <https://lists.gnu.org/r/bug-gnulib/2007-04/msg00107.html> */
+ if (sprintf (buf, "%010a %d", 1.0 / zero, 33, 44, 55) < 0
+ || buf[0] == '0')
+ result |= 8;
+ /* This catches a Mac OS X 10.3.9 (Darwin 7.9) bug. */
+ if (sprintf (buf, "%.1a", 1.999) < 0
+ || (strcmp (buf, "0x1.0p+1") != 0
+ && strcmp (buf, "0x2.0p+0") != 0
+ && strcmp (buf, "0x4.0p-1") != 0
+ && strcmp (buf, "0x8.0p-2") != 0))
+ result |= 16;
+ /* This catches the same Mac OS X 10.3.9 (Darwin 7.9) bug and also a
+ glibc 2.4 bug <https://sourceware.org/bugzilla/show_bug.cgi?id=2908>. */
+ if (sprintf (buf, "%.1La", 1.999L) < 0
+ || (strcmp (buf, "0x1.0p+1") != 0
+ && strcmp (buf, "0x2.0p+0") != 0
+ && strcmp (buf, "0x4.0p-1") != 0
+ && strcmp (buf, "0x8.0p-2") != 0))
+ result |= 32;
+ return result;
+}]])],
+ [gl_cv_func_printf_directive_a=yes],
+ [gl_cv_func_printf_directive_a=no],
+ [
+ case "$host_os" in
+ # Guess yes on glibc >= 2.5 systems.
+ *-gnu* | gnu*)
+ AC_EGREP_CPP([BZ2908], [
+ #include <features.h>
+ #ifdef __GNU_LIBRARY__
+ #if ((__GLIBC__ == 2 && __GLIBC_MINOR__ >= 5) || (__GLIBC__ > 2)) && !defined __UCLIBC__
+ BZ2908
+ #endif
+ #endif
+ ],
+ [gl_cv_func_printf_directive_a="guessing yes"],
+ [gl_cv_func_printf_directive_a="guessing no"])
+ ;;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_printf_directive_a="guessing yes";;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_printf_directive_a="guessing no";;
+ # Guess no on native Windows.
+ mingw*) gl_cv_func_printf_directive_a="guessing no";;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_printf_directive_a="$gl_cross_guess_normal";;
+ esac
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports the %F format
+dnl directive. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_directive_f.
+
+AC_DEFUN([gl_PRINTF_DIRECTIVE_F],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports the 'F' directive],
+ [gl_cv_func_printf_directive_f],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+static char buf[100];
+static double zero = 0.0;
+int main ()
+{
+ int result = 0;
+ if (sprintf (buf, "%F %d", 1234567.0, 33, 44, 55) < 0
+ || strcmp (buf, "1234567.000000 33") != 0)
+ result |= 1;
+ if (sprintf (buf, "%F", 1.0 / zero) < 0
+ || (strcmp (buf, "INF") != 0 && strcmp (buf, "INFINITY") != 0))
+ result |= 2;
+ /* This catches a Cygwin 1.5.x bug. */
+ if (sprintf (buf, "%.F", 1234.0) < 0
+ || strcmp (buf, "1234") != 0)
+ result |= 4;
+ return result;
+}]])],
+ [gl_cv_func_printf_directive_f=yes],
+ [gl_cv_func_printf_directive_f=no],
+ [
+ case "$host_os" in
+changequote(,)dnl
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_printf_directive_f="guessing yes";;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_printf_directive_f="guessing yes";;
+ # Guess yes on FreeBSD >= 6.
+ freebsd[1-5].*) gl_cv_func_printf_directive_f="guessing no";;
+ freebsd* | kfreebsd*) gl_cv_func_printf_directive_f="guessing yes";;
+ midnightbsd*) gl_cv_func_printf_directive_f="guessing yes";;
+ # Guess yes on Mac OS X >= 10.3.
+ darwin[1-6].*) gl_cv_func_printf_directive_f="guessing no";;
+ darwin*) gl_cv_func_printf_directive_f="guessing yes";;
+ # Guess yes on OpenBSD >= 6.0.
+ openbsd[1-5].*) gl_cv_func_printf_directive_f="guessing no";;
+ openbsd*) gl_cv_func_printf_directive_f="guessing yes";;
+ # Guess yes on Solaris >= 2.10.
+ solaris2.[1-9][0-9]*) gl_cv_func_printf_directive_f="guessing yes";;
+ solaris*) gl_cv_func_printf_directive_f="guessing no";;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_printf_directive_f="guessing no";;
+changequote([,])dnl
+ # Guess yes on MSVC, no on mingw.
+ mingw*) AC_EGREP_CPP([Known], [
+#ifdef _MSC_VER
+ Known
+#endif
+ ],
+ [gl_cv_func_printf_directive_f="guessing yes"],
+ [gl_cv_func_printf_directive_f="guessing no"])
+ ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_printf_directive_f="$gl_cross_guess_normal";;
+ esac
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports the %n format
+dnl directive. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_printf_directive_n.
+
+AC_DEFUN([gl_PRINTF_DIRECTIVE_N],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports the 'n' directive],
+ [gl_cv_func_printf_directive_n],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+#include <inttypes.h>
+/* See page about "Parameter Validation" on msdn.microsoft.com.
+ <https://docs.microsoft.com/en-us/cpp/c-runtime-library/parameter-validation>
+ <https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/set-invalid-parameter-handler-set-thread-local-invalid-parameter-handler> */
+static void cdecl
+invalid_parameter_handler (const wchar_t *expression,
+ const wchar_t *function,
+ const wchar_t *file, unsigned int line,
+ uintptr_t dummy)
+{
+ exit (1);
+}
+#endif
+static char fmtstring[10];
+static char buf[100];
+int main ()
+{
+ int count = -1;
+#ifdef _MSC_VER
+ _set_invalid_parameter_handler (invalid_parameter_handler);
+#endif
+ /* Copy the format string. Some systems (glibc with _FORTIFY_SOURCE=2)
+ support %n in format strings in read-only memory but not in writable
+ memory. */
+ strcpy (fmtstring, "%d %n");
+ if (sprintf (buf, fmtstring, 123, &count, 33, 44, 55) < 0
+ || strcmp (buf, "123 ") != 0
+ || count != 4)
+ return 1;
+ return 0;
+}]])],
+ [gl_cv_func_printf_directive_n=yes],
+ [gl_cv_func_printf_directive_n=no],
+ [case "$host_os" in
+ # Guess no on glibc when _FORTIFY_SOURCE >= 2.
+ *-gnu* | gnu*) AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE(
+ [[#if _FORTIFY_SOURCE >= 2
+ error fail
+ #endif
+ ]])],
+ [gl_cv_func_printf_directive_n="guessing yes"],
+ [gl_cv_func_printf_directive_n="guessing no"])
+ ;;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_printf_directive_n="guessing no";;
+ # Guess no on native Windows.
+ mingw*) gl_cv_func_printf_directive_n="guessing no";;
+ *) gl_cv_func_printf_directive_n="guessing yes";;
+ esac
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports the %ls format
+dnl directive and in particular, when a precision is specified, whether
+dnl the functions stop converting the wide string argument when the number
+dnl of bytes that have been produced by this conversion equals or exceeds
+dnl the precision.
+dnl Result is gl_cv_func_printf_directive_ls.
+
+AC_DEFUN([gl_PRINTF_DIRECTIVE_LS],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports the 'ls' directive],
+ [gl_cv_func_printf_directive_ls],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <wchar.h>
+#include <string.h>
+int main ()
+{
+ int result = 0;
+ char buf[100];
+ /* Test whether %ls works at all.
+ This test fails on OpenBSD 4.0, IRIX 6.5, Solaris 2.6, Haiku, but not on
+ Cygwin 1.5. */
+ {
+ static const wchar_t wstring[] = { 'a', 'b', 'c', 0 };
+ buf[0] = '\0';
+ if (sprintf (buf, "%ls", wstring) < 0
+ || strcmp (buf, "abc") != 0)
+ result |= 1;
+ }
+ /* This test fails on IRIX 6.5, Solaris 2.6, Cygwin 1.5, Haiku (with an
+ assertion failure inside libc), but not on OpenBSD 4.0. */
+ {
+ static const wchar_t wstring[] = { 'a', 0 };
+ buf[0] = '\0';
+ if (sprintf (buf, "%ls", wstring) < 0
+ || strcmp (buf, "a") != 0)
+ result |= 2;
+ }
+ /* Test whether precisions in %ls are supported as specified in ISO C 99
+ section 7.19.6.1:
+ "If a precision is specified, no more than that many bytes are written
+ (including shift sequences, if any), and the array shall contain a
+ null wide character if, to equal the multibyte character sequence
+ length given by the precision, the function would need to access a
+ wide character one past the end of the array."
+ This test fails on Solaris 10. */
+ {
+ static const wchar_t wstring[] = { 'a', 'b', (wchar_t) 0xfdfdfdfd, 0 };
+ buf[0] = '\0';
+ if (sprintf (buf, "%.2ls", wstring) < 0
+ || strcmp (buf, "ab") != 0)
+ result |= 8;
+ }
+ return result;
+}]])],
+ [gl_cv_func_printf_directive_ls=yes],
+ [gl_cv_func_printf_directive_ls=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ # Guess yes on OpenBSD >= 6.0.
+ openbsd[1-5].*) gl_cv_func_printf_directive_ls="guessing no";;
+ openbsd*) gl_cv_func_printf_directive_ls="guessing yes";;
+ irix*) gl_cv_func_printf_directive_ls="guessing no";;
+ solaris*) gl_cv_func_printf_directive_ls="guessing no";;
+ cygwin*) gl_cv_func_printf_directive_ls="guessing no";;
+ beos* | haiku*) gl_cv_func_printf_directive_ls="guessing no";;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_printf_directive_ls="guessing no";;
+ # Guess yes on native Windows.
+ mingw*) gl_cv_func_printf_directive_ls="guessing yes";;
+ *) gl_cv_func_printf_directive_ls="guessing yes";;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports POSIX/XSI format
+dnl strings with positions. (POSIX:2001)
+dnl Result is gl_cv_func_printf_positions.
+
+AC_DEFUN([gl_PRINTF_POSITIONS],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports POSIX/XSI format strings with positions],
+ [gl_cv_func_printf_positions],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+/* The string "%2$d %1$d", with dollar characters protected from the shell's
+ dollar expansion (possibly an autoconf bug). */
+static char format[] = { '%', '2', '$', 'd', ' ', '%', '1', '$', 'd', '\0' };
+static char buf[100];
+int main ()
+{
+ sprintf (buf, format, 33, 55);
+ return (strcmp (buf, "55 33") != 0);
+}]])],
+ [gl_cv_func_printf_positions=yes],
+ [gl_cv_func_printf_positions=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ netbsd[1-3]* | netbsdelf[1-3]* | netbsdaout[1-3]* | netbsdcoff[1-3]*)
+ gl_cv_func_printf_positions="guessing no";;
+ beos*) gl_cv_func_printf_positions="guessing no";;
+ # Guess yes on Android.
+ linux*-android*) gl_cv_func_printf_positions="guessing yes";;
+ # Guess no on native Windows.
+ mingw* | pw*) gl_cv_func_printf_positions="guessing no";;
+ *) gl_cv_func_printf_positions="guessing yes";;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports POSIX/XSI format
+dnl strings with the ' flag for grouping of decimal digits. (POSIX:2001)
+dnl Result is gl_cv_func_printf_flag_grouping.
+
+AC_DEFUN([gl_PRINTF_FLAG_GROUPING],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports the grouping flag],
+ [gl_cv_func_printf_flag_grouping],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+static char buf[100];
+int main ()
+{
+ if (sprintf (buf, "%'d %d", 1234567, 99) < 0
+ || buf[strlen (buf) - 1] != '9')
+ return 1;
+ return 0;
+}]])],
+ [gl_cv_func_printf_flag_grouping=yes],
+ [gl_cv_func_printf_flag_grouping=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ cygwin*) gl_cv_func_printf_flag_grouping="guessing no";;
+ netbsd*) gl_cv_func_printf_flag_grouping="guessing no";;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_printf_flag_grouping="guessing no";;
+ # Guess no on native Windows.
+ mingw* | pw*) gl_cv_func_printf_flag_grouping="guessing no";;
+ *) gl_cv_func_printf_flag_grouping="guessing yes";;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports the - flag correctly.
+dnl (ISO C99.) See
+dnl <https://lists.gnu.org/r/bug-coreutils/2008-02/msg00035.html>
+dnl Result is gl_cv_func_printf_flag_leftadjust.
+
+AC_DEFUN([gl_PRINTF_FLAG_LEFTADJUST],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports the left-adjust flag correctly],
+ [gl_cv_func_printf_flag_leftadjust],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+static char buf[100];
+int main ()
+{
+ /* Check that a '-' flag is not annihilated by a negative width. */
+ if (sprintf (buf, "a%-*sc", -3, "b") < 0
+ || strcmp (buf, "ab c") != 0)
+ return 1;
+ return 0;
+}]])],
+ [gl_cv_func_printf_flag_leftadjust=yes],
+ [gl_cv_func_printf_flag_leftadjust=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ # Guess yes on HP-UX 11.
+ hpux11*) gl_cv_func_printf_flag_leftadjust="guessing yes";;
+ # Guess no on HP-UX 10 and older.
+ hpux*) gl_cv_func_printf_flag_leftadjust="guessing no";;
+ # Guess yes on Android.
+ linux*-android*) gl_cv_func_printf_flag_leftadjust="guessing yes";;
+ # Guess yes on native Windows.
+ mingw*) gl_cv_func_printf_flag_leftadjust="guessing yes";;
+ # Guess yes otherwise.
+ *) gl_cv_func_printf_flag_leftadjust="guessing yes";;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports padding of non-finite
+dnl values with the 0 flag correctly. (ISO C99 + TC1 + TC2.) See
+dnl <https://lists.gnu.org/r/bug-gnulib/2007-04/msg00107.html>
+dnl Result is gl_cv_func_printf_flag_zero.
+
+AC_DEFUN([gl_PRINTF_FLAG_ZERO],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports the zero flag correctly],
+ [gl_cv_func_printf_flag_zero],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+static char buf[100];
+static double zero = 0.0;
+int main ()
+{
+ if (sprintf (buf, "%010f", 1.0 / zero, 33, 44, 55) < 0
+ || (strcmp (buf, " inf") != 0
+ && strcmp (buf, " infinity") != 0))
+ return 1;
+ return 0;
+}]])],
+ [gl_cv_func_printf_flag_zero=yes],
+ [gl_cv_func_printf_flag_zero=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_printf_flag_zero="guessing yes";;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_printf_flag_zero="guessing yes";;
+ # Guess yes on BeOS.
+ beos*) gl_cv_func_printf_flag_zero="guessing yes";;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_printf_flag_zero="guessing no";;
+ # Guess no on native Windows.
+ mingw*) gl_cv_func_printf_flag_zero="guessing no";;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_printf_flag_zero="$gl_cross_guess_normal";;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions supports large precisions.
+dnl On mingw, precisions larger than 512 are treated like 512, in integer,
+dnl floating-point or pointer output. On Solaris 10/x86, precisions larger
+dnl than 510 in floating-point output crash the program. On Solaris 10/SPARC,
+dnl precisions larger than 510 in floating-point output yield wrong results.
+dnl On AIX 7.1, precisions larger than 998 in floating-point output yield
+dnl wrong results. On BeOS, precisions larger than 1044 crash the program.
+dnl Result is gl_cv_func_printf_precision.
+
+AC_DEFUN([gl_PRINTF_PRECISION],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf supports large precisions],
+ [gl_cv_func_printf_precision],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+static char buf[5000];
+int main ()
+{
+ int result = 0;
+#ifdef __BEOS__
+ /* On BeOS, this would crash and show a dialog box. Avoid the crash. */
+ return 1;
+#endif
+ if (sprintf (buf, "%.4000d %d", 1, 33, 44) < 4000 + 3)
+ result |= 1;
+ if (sprintf (buf, "%.4000f %d", 1.0, 33, 44) < 4000 + 5)
+ result |= 2;
+ if (sprintf (buf, "%.511f %d", 1.0, 33, 44) < 511 + 5
+ || buf[0] != '1')
+ result |= 4;
+ if (sprintf (buf, "%.999f %d", 1.0, 33, 44) < 999 + 5
+ || buf[0] != '1')
+ result |= 4;
+ return result;
+}]])],
+ [gl_cv_func_printf_precision=yes],
+ [gl_cv_func_printf_precision=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ # Guess no only on Solaris, native Windows, and BeOS systems.
+ solaris*) gl_cv_func_printf_precision="guessing no" ;;
+ mingw* | pw*) gl_cv_func_printf_precision="guessing no" ;;
+ beos*) gl_cv_func_printf_precision="guessing no" ;;
+ # Guess yes on Android.
+ linux*-android*) gl_cv_func_printf_precision="guessing yes" ;;
+ *) gl_cv_func_printf_precision="guessing yes" ;;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
+
+dnl Test whether the *printf family of functions recovers gracefully in case
+dnl of an out-of-memory condition, or whether it crashes the entire program.
+dnl Result is gl_cv_func_printf_enomem.
+
+AC_DEFUN([gl_PRINTF_ENOMEM],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([gl_MULTIARCH])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether printf survives out-of-memory conditions],
+ [gl_cv_func_printf_enomem],
+ [
+ gl_cv_func_printf_enomem="guessing no"
+ if test "$cross_compiling" = no; then
+ if test $APPLE_UNIVERSAL_BUILD = 0; then
+ AC_LANG_CONFTEST([AC_LANG_SOURCE([[
+]GL_NOCRASH[
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <errno.h>
+int main()
+{
+ struct rlimit limit;
+ int ret;
+ nocrash_init ();
+ /* Some printf implementations allocate temporary space with malloc. */
+ /* On BSD systems, malloc() is limited by RLIMIT_DATA. */
+#ifdef RLIMIT_DATA
+ if (getrlimit (RLIMIT_DATA, &limit) < 0)
+ return 77;
+ if (limit.rlim_max == RLIM_INFINITY || limit.rlim_max > 5000000)
+ limit.rlim_max = 5000000;
+ limit.rlim_cur = limit.rlim_max;
+ if (setrlimit (RLIMIT_DATA, &limit) < 0)
+ return 77;
+#endif
+ /* On Linux systems, malloc() is limited by RLIMIT_AS. */
+#ifdef RLIMIT_AS
+ if (getrlimit (RLIMIT_AS, &limit) < 0)
+ return 77;
+ if (limit.rlim_max == RLIM_INFINITY || limit.rlim_max > 5000000)
+ limit.rlim_max = 5000000;
+ limit.rlim_cur = limit.rlim_max;
+ if (setrlimit (RLIMIT_AS, &limit) < 0)
+ return 77;
+#endif
+ /* Some printf implementations allocate temporary space on the stack. */
+#ifdef RLIMIT_STACK
+ if (getrlimit (RLIMIT_STACK, &limit) < 0)
+ return 77;
+ if (limit.rlim_max == RLIM_INFINITY || limit.rlim_max > 5000000)
+ limit.rlim_max = 5000000;
+ limit.rlim_cur = limit.rlim_max;
+ if (setrlimit (RLIMIT_STACK, &limit) < 0)
+ return 77;
+#endif
+ ret = printf ("%.5000000f", 1.0);
+ return !(ret == 5000002 || (ret < 0 && errno == ENOMEM));
+}
+ ]])])
+ if AC_TRY_EVAL([ac_link]) && test -s conftest$ac_exeext; then
+ (./conftest 2>&AS_MESSAGE_LOG_FD
+ result=$?
+ _AS_ECHO_LOG([\$? = $result])
+ if test $result != 0 && test $result != 77; then result=1; fi
+ exit $result
+ ) >/dev/null 2>/dev/null
+ case $? in
+ 0) gl_cv_func_printf_enomem="yes" ;;
+ 77) gl_cv_func_printf_enomem="guessing no" ;;
+ *) gl_cv_func_printf_enomem="no" ;;
+ esac
+ else
+ gl_cv_func_printf_enomem="guessing no"
+ fi
+ rm -fr conftest*
+ else
+ dnl A universal build on Apple Mac OS X platforms.
+ dnl The result would be 'no' in 32-bit mode and 'yes' in 64-bit mode.
+ dnl But we need a configuration result that is valid in both modes.
+ gl_cv_func_printf_enomem="guessing no"
+ fi
+ fi
+ if test "$gl_cv_func_printf_enomem" = "guessing no"; then
+changequote(,)dnl
+ case "$host_os" in
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_printf_enomem="guessing yes";;
+ # Guess yes on Solaris.
+ solaris*) gl_cv_func_printf_enomem="guessing yes";;
+ # Guess yes on AIX.
+ aix*) gl_cv_func_printf_enomem="guessing yes";;
+ # Guess yes on HP-UX/hppa.
+ hpux*) case "$host_cpu" in
+ hppa*) gl_cv_func_printf_enomem="guessing yes";;
+ *) gl_cv_func_printf_enomem="guessing no";;
+ esac
+ ;;
+ # Guess yes on IRIX.
+ irix*) gl_cv_func_printf_enomem="guessing yes";;
+ # Guess yes on OSF/1.
+ osf*) gl_cv_func_printf_enomem="guessing yes";;
+ # Guess yes on BeOS.
+ beos*) gl_cv_func_printf_enomem="guessing yes";;
+ # Guess yes on Haiku.
+ haiku*) gl_cv_func_printf_enomem="guessing yes";;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_printf_enomem="guessing no";;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_printf_enomem="$gl_cross_guess_normal";;
+ esac
+changequote([,])dnl
+ fi
+ ])
+])
+
+dnl Test whether the snprintf function exists. (ISO C99, POSIX:2001)
+dnl Result is ac_cv_func_snprintf.
+
+AC_DEFUN([gl_SNPRINTF_PRESENCE],
+[
+ AC_CHECK_FUNCS_ONCE([snprintf])
+])
+
+dnl Test whether the string produced by the snprintf function is always NUL
+dnl terminated. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_snprintf_truncation_c99.
+
+AC_DEFUN_ONCE([gl_SNPRINTF_TRUNCATION_C99],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_REQUIRE([gl_SNPRINTF_PRESENCE])
+ AC_CACHE_CHECK([whether snprintf truncates the result as in C99],
+ [gl_cv_func_snprintf_truncation_c99],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+#if HAVE_SNPRINTF
+# define my_snprintf snprintf
+#else
+# include <stdarg.h>
+static int my_snprintf (char *buf, int size, const char *format, ...)
+{
+ va_list args;
+ int ret;
+ va_start (args, format);
+ ret = vsnprintf (buf, size, format, args);
+ va_end (args);
+ return ret;
+}
+#endif
+static char buf[100];
+int main ()
+{
+ strcpy (buf, "ABCDEF");
+ my_snprintf (buf, 3, "%d %d", 4567, 89);
+ if (memcmp (buf, "45\0DEF", 6) != 0)
+ return 1;
+ return 0;
+}]])],
+ [gl_cv_func_snprintf_truncation_c99=yes],
+ [gl_cv_func_snprintf_truncation_c99=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on FreeBSD >= 5.
+ freebsd[1-4].*) gl_cv_func_snprintf_truncation_c99="guessing no";;
+ freebsd* | kfreebsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ midnightbsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on Mac OS X >= 10.3.
+ darwin[1-6].*) gl_cv_func_snprintf_truncation_c99="guessing no";;
+ darwin*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on OpenBSD >= 3.9.
+ openbsd[1-2].* | openbsd3.[0-8] | openbsd3.[0-8].*)
+ gl_cv_func_snprintf_truncation_c99="guessing no";;
+ openbsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on Solaris >= 2.6.
+ solaris2.[0-5] | solaris2.[0-5].*)
+ gl_cv_func_snprintf_truncation_c99="guessing no";;
+ solaris*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on AIX >= 4.
+ aix[1-3]*) gl_cv_func_snprintf_truncation_c99="guessing no";;
+ aix*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on HP-UX >= 11.
+ hpux[7-9]* | hpux10*) gl_cv_func_snprintf_truncation_c99="guessing no";;
+ hpux*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on IRIX >= 6.5.
+ irix6.5) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on OSF/1 >= 5.
+ osf[3-4]*) gl_cv_func_snprintf_truncation_c99="guessing no";;
+ osf*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on NetBSD >= 3.
+ netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*)
+ gl_cv_func_snprintf_truncation_c99="guessing no";;
+ netbsd*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on BeOS.
+ beos*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess yes on Android.
+ linux*-android*) gl_cv_func_snprintf_truncation_c99="guessing yes";;
+ # Guess no on native Windows.
+ mingw*) gl_cv_func_snprintf_truncation_c99="guessing no";;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_snprintf_truncation_c99="$gl_cross_guess_normal";;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
+
+dnl Test whether the return value of the snprintf function is the number
+dnl of bytes (excluding the terminating NUL) that would have been produced
+dnl if the buffer had been large enough. (ISO C99, POSIX:2001)
+dnl For example, this test program fails on IRIX 6.5:
+dnl ---------------------------------------------------------------------
+dnl #include <stdio.h>
+dnl int main()
+dnl {
+dnl static char buf[8];
+dnl int retval = snprintf (buf, 3, "%d", 12345);
+dnl return retval >= 0 && retval < 3;
+dnl }
+dnl ---------------------------------------------------------------------
+dnl Result is gl_cv_func_snprintf_retval_c99.
+
+AC_DEFUN_ONCE([gl_SNPRINTF_RETVAL_C99],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_REQUIRE([gl_SNPRINTF_PRESENCE])
+ AC_CACHE_CHECK([whether snprintf returns a byte count as in C99],
+ [gl_cv_func_snprintf_retval_c99],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+#if HAVE_SNPRINTF
+# define my_snprintf snprintf
+#else
+# include <stdarg.h>
+static int my_snprintf (char *buf, int size, const char *format, ...)
+{
+ va_list args;
+ int ret;
+ va_start (args, format);
+ ret = vsnprintf (buf, size, format, args);
+ va_end (args);
+ return ret;
+}
+#endif
+static char buf[100];
+int main ()
+{
+ strcpy (buf, "ABCDEF");
+ if (my_snprintf (buf, 3, "%d %d", 4567, 89) != 7)
+ return 1;
+ if (my_snprintf (buf, 0, "%d %d", 4567, 89) != 7)
+ return 2;
+ if (my_snprintf (NULL, 0, "%d %d", 4567, 89) != 7)
+ return 3;
+ return 0;
+}]])],
+ [gl_cv_func_snprintf_retval_c99=yes],
+ [gl_cv_func_snprintf_retval_c99=no],
+ [case "$host_os" in
+changequote(,)dnl
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+ # Guess yes on FreeBSD >= 5.
+ freebsd[1-4].*) gl_cv_func_snprintf_retval_c99="guessing no";;
+ freebsd* | kfreebsd*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+ midnightbsd*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+ # Guess yes on Mac OS X >= 10.3.
+ darwin[1-6].*) gl_cv_func_snprintf_retval_c99="guessing no";;
+ darwin*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+ # Guess yes on OpenBSD >= 3.9.
+ openbsd[1-2].* | openbsd3.[0-8] | openbsd3.[0-8].*)
+ gl_cv_func_snprintf_retval_c99="guessing no";;
+ openbsd*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+ # Guess yes on Solaris >= 2.10.
+ solaris2.[1-9][0-9]*) gl_cv_func_printf_sizes_c99="guessing yes";;
+ solaris*) gl_cv_func_printf_sizes_c99="guessing no";;
+ # Guess yes on AIX >= 4.
+ aix[1-3]*) gl_cv_func_snprintf_retval_c99="guessing no";;
+ aix*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+ # Guess yes on NetBSD >= 3.
+ netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*)
+ gl_cv_func_snprintf_retval_c99="guessing no";;
+ netbsd*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+ # Guess yes on BeOS.
+ beos*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+ # Guess yes on Android.
+ linux*-android*) gl_cv_func_snprintf_retval_c99="guessing yes";;
+changequote([,])dnl
+ # Guess yes on MSVC, no on mingw.
+ mingw*) AC_EGREP_CPP([Known], [
+#ifdef _MSC_VER
+ Known
+#endif
+ ],
+ [gl_cv_func_snprintf_retval_c99="guessing yes"],
+ [gl_cv_func_snprintf_retval_c99="guessing no"])
+ ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_snprintf_retval_c99="$gl_cross_guess_normal";;
+ esac
+ ])
+ ])
+])
+
+dnl Test whether the snprintf function supports the %n format directive
+dnl also in truncated portions of the format string. (ISO C99, POSIX:2001)
+dnl Result is gl_cv_func_snprintf_directive_n.
+
+AC_DEFUN([gl_SNPRINTF_DIRECTIVE_N],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_REQUIRE([gl_SNPRINTF_PRESENCE])
+ AC_CACHE_CHECK([whether snprintf fully supports the 'n' directive],
+ [gl_cv_func_snprintf_directive_n],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#include <string.h>
+#if HAVE_SNPRINTF
+# define my_snprintf snprintf
+#else
+# include <stdarg.h>
+static int my_snprintf (char *buf, int size, const char *format, ...)
+{
+ va_list args;
+ int ret;
+ va_start (args, format);
+ ret = vsnprintf (buf, size, format, args);
+ va_end (args);
+ return ret;
+}
+#endif
+static char fmtstring[10];
+static char buf[100];
+int main ()
+{
+ int count = -1;
+ /* Copy the format string. Some systems (glibc with _FORTIFY_SOURCE=2)
+ support %n in format strings in read-only memory but not in writable
+ memory. */
+ strcpy (fmtstring, "%d %n");
+ my_snprintf (buf, 4, fmtstring, 12345, &count, 33, 44, 55);
+ if (count != 6)
+ return 1;
+ return 0;
+}]])],
+ [gl_cv_func_snprintf_directive_n=yes],
+ [gl_cv_func_snprintf_directive_n=no],
+ [
+ case "$host_os" in
+ # Guess no on glibc when _FORTIFY_SOURCE >= 2.
+ *-gnu* | gnu*) AC_COMPILE_IFELSE(
+ [AC_LANG_SOURCE(
+ [[#if _FORTIFY_SOURCE >= 2
+ error fail
+ #endif
+ ]])],
+ [gl_cv_func_snprintf_directive_n="guessing yes"],
+ [gl_cv_func_snprintf_directive_n="guessing no"])
+ ;;
+changequote(,)dnl
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_snprintf_directive_n="guessing yes";;
+ # Guess yes on FreeBSD >= 5.
+ freebsd[1-4].*) gl_cv_func_snprintf_directive_n="guessing no";;
+ freebsd* | kfreebsd*) gl_cv_func_snprintf_directive_n="guessing yes";;
+ midnightbsd*) gl_cv_func_snprintf_directive_n="guessing yes";;
+ # Guess yes on Mac OS X >= 10.3.
+ darwin[1-6].*) gl_cv_func_snprintf_directive_n="guessing no";;
+ darwin*) gl_cv_func_snprintf_directive_n="guessing yes";;
+ # Guess yes on Solaris >= 2.6.
+ solaris2.[0-5] | solaris2.[0-5].*)
+ gl_cv_func_snprintf_directive_n="guessing no";;
+ solaris*) gl_cv_func_snprintf_directive_n="guessing yes";;
+ # Guess yes on AIX >= 4.
+ aix[1-3]*) gl_cv_func_snprintf_directive_n="guessing no";;
+ aix*) gl_cv_func_snprintf_directive_n="guessing yes";;
+ # Guess yes on IRIX >= 6.5.
+ irix6.5) gl_cv_func_snprintf_directive_n="guessing yes";;
+ # Guess yes on OSF/1 >= 5.
+ osf[3-4]*) gl_cv_func_snprintf_directive_n="guessing no";;
+ osf*) gl_cv_func_snprintf_directive_n="guessing yes";;
+ # Guess yes on NetBSD >= 3.
+ netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*)
+ gl_cv_func_snprintf_directive_n="guessing no";;
+ netbsd*) gl_cv_func_snprintf_directive_n="guessing yes";;
+ # Guess yes on BeOS.
+ beos*) gl_cv_func_snprintf_directive_n="guessing yes";;
+ # Guess no on Android.
+ linux*-android*) gl_cv_func_snprintf_directive_n="guessing no";;
+ # Guess no on native Windows.
+ mingw*) gl_cv_func_snprintf_directive_n="guessing no";;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_snprintf_directive_n="$gl_cross_guess_normal";;
+changequote([,])dnl
+ esac
+ ])
+ ])
+])
+
+dnl Test whether the snprintf function, when passed a size = 1, writes any
+dnl output without bounds in this case, behaving like sprintf. This is the
+dnl case on Linux libc5.
+dnl Result is gl_cv_func_snprintf_size1.
+
+AC_DEFUN([gl_SNPRINTF_SIZE1],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_REQUIRE([gl_SNPRINTF_PRESENCE])
+ AC_CACHE_CHECK([whether snprintf respects a size of 1],
+ [gl_cv_func_snprintf_size1],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdio.h>
+#if HAVE_SNPRINTF
+# define my_snprintf snprintf
+#else
+# include <stdarg.h>
+static int my_snprintf (char *buf, int size, const char *format, ...)
+{
+ va_list args;
+ int ret;
+ va_start (args, format);
+ ret = vsnprintf (buf, size, format, args);
+ va_end (args);
+ return ret;
+}
+#endif
+int main()
+{
+ static char buf[8] = { 'D', 'E', 'A', 'D', 'B', 'E', 'E', 'F' };
+ my_snprintf (buf, 1, "%d", 12345);
+ return buf[1] != 'E';
+}]])],
+ [gl_cv_func_snprintf_size1=yes],
+ [gl_cv_func_snprintf_size1=no],
+ [case "$host_os" in
+ # Guess yes on Android.
+ linux*-android*) gl_cv_func_snprintf_size1="guessing yes" ;;
+ # Guess yes on native Windows.
+ mingw*) gl_cv_func_snprintf_size1="guessing yes" ;;
+ *) gl_cv_func_snprintf_size1="guessing yes" ;;
+ esac
+ ])
+ ])
+])
+
+dnl Test whether the vsnprintf function, when passed a zero size, produces no
+dnl output. (ISO C99, POSIX:2001)
+dnl For example, snprintf nevertheless writes a NUL byte in this case
+dnl on OSF/1 5.1:
+dnl ---------------------------------------------------------------------
+dnl #include <stdio.h>
+dnl int main()
+dnl {
+dnl static char buf[8] = { 'D', 'E', 'A', 'D', 'B', 'E', 'E', 'F' };
+dnl snprintf (buf, 0, "%d", 12345);
+dnl return buf[0] != 'D';
+dnl }
+dnl ---------------------------------------------------------------------
+dnl And vsnprintf writes any output without bounds in this case, behaving like
+dnl vsprintf, on HP-UX 11 and OSF/1 5.1:
+dnl ---------------------------------------------------------------------
+dnl #include <stdarg.h>
+dnl #include <stdio.h>
+dnl static int my_snprintf (char *buf, int size, const char *format, ...)
+dnl {
+dnl va_list args;
+dnl int ret;
+dnl va_start (args, format);
+dnl ret = vsnprintf (buf, size, format, args);
+dnl va_end (args);
+dnl return ret;
+dnl }
+dnl int main()
+dnl {
+dnl static char buf[8] = { 'D', 'E', 'A', 'D', 'B', 'E', 'E', 'F' };
+dnl my_snprintf (buf, 0, "%d", 12345);
+dnl return buf[0] != 'D';
+dnl }
+dnl ---------------------------------------------------------------------
+dnl Result is gl_cv_func_vsnprintf_zerosize_c99.
+
+AC_DEFUN([gl_VSNPRINTF_ZEROSIZE_C99],
+[
+ AC_REQUIRE([AC_PROG_CC])
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+ AC_CACHE_CHECK([whether vsnprintf respects a zero size as in C99],
+ [gl_cv_func_vsnprintf_zerosize_c99],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdarg.h>
+#include <stdio.h>
+static int my_snprintf (char *buf, int size, const char *format, ...)
+{
+ va_list args;
+ int ret;
+ va_start (args, format);
+ ret = vsnprintf (buf, size, format, args);
+ va_end (args);
+ return ret;
+}
+int main()
+{
+ static char buf[8] = { 'D', 'E', 'A', 'D', 'B', 'E', 'E', 'F' };
+ my_snprintf (buf, 0, "%d", 12345);
+ return buf[0] != 'D';
+}]])],
+ [gl_cv_func_vsnprintf_zerosize_c99=yes],
+ [gl_cv_func_vsnprintf_zerosize_c99=no],
+ [
+changequote(,)dnl
+ case "$host_os" in
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on FreeBSD >= 5.
+ freebsd[1-4].*) gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+ freebsd* | kfreebsd*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ midnightbsd*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on Mac OS X >= 10.3.
+ darwin[1-6].*) gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+ darwin*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on Cygwin.
+ cygwin*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on Solaris >= 2.6.
+ solaris2.[0-5] | solaris2.[0-5].*)
+ gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+ solaris*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on AIX >= 4.
+ aix[1-3]*) gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+ aix*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on IRIX >= 6.5.
+ irix6.5) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on NetBSD >= 3.
+ netbsd[1-2]* | netbsdelf[1-2]* | netbsdaout[1-2]* | netbsdcoff[1-2]*)
+ gl_cv_func_vsnprintf_zerosize_c99="guessing no";;
+ netbsd*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on BeOS.
+ beos*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on Android.
+ linux*-android*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # Guess yes on native Windows.
+ mingw* | pw*) gl_cv_func_vsnprintf_zerosize_c99="guessing yes";;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_vsnprintf_zerosize_c99="$gl_cross_guess_normal";;
+ esac
+changequote([,])dnl
+ ])
+ ])
+])
+
+dnl The results of these tests on various platforms are:
+dnl
+dnl 1 = gl_PRINTF_SIZES_C99
+dnl 2 = gl_PRINTF_LONG_DOUBLE
+dnl 3 = gl_PRINTF_INFINITE
+dnl 4 = gl_PRINTF_INFINITE_LONG_DOUBLE
+dnl 5 = gl_PRINTF_DIRECTIVE_A
+dnl 6 = gl_PRINTF_DIRECTIVE_F
+dnl 7 = gl_PRINTF_DIRECTIVE_N
+dnl 8 = gl_PRINTF_DIRECTIVE_LS
+dnl 9 = gl_PRINTF_POSITIONS
+dnl 10 = gl_PRINTF_FLAG_GROUPING
+dnl 11 = gl_PRINTF_FLAG_LEFTADJUST
+dnl 12 = gl_PRINTF_FLAG_ZERO
+dnl 13 = gl_PRINTF_PRECISION
+dnl 14 = gl_PRINTF_ENOMEM
+dnl 15 = gl_SNPRINTF_PRESENCE
+dnl 16 = gl_SNPRINTF_TRUNCATION_C99
+dnl 17 = gl_SNPRINTF_RETVAL_C99
+dnl 18 = gl_SNPRINTF_DIRECTIVE_N
+dnl 19 = gl_SNPRINTF_SIZE1
+dnl 20 = gl_VSNPRINTF_ZEROSIZE_C99
+dnl
+dnl 1 = checking whether printf supports size specifiers as in C99...
+dnl 2 = checking whether printf supports 'long double' arguments...
+dnl 3 = checking whether printf supports infinite 'double' arguments...
+dnl 4 = checking whether printf supports infinite 'long double' arguments...
+dnl 5 = checking whether printf supports the 'a' and 'A' directives...
+dnl 6 = checking whether printf supports the 'F' directive...
+dnl 7 = checking whether printf supports the 'n' directive...
+dnl 8 = checking whether printf supports the 'ls' directive...
+dnl 9 = checking whether printf supports POSIX/XSI format strings with positions...
+dnl 10 = checking whether printf supports the grouping flag...
+dnl 11 = checking whether printf supports the left-adjust flag correctly...
+dnl 12 = checking whether printf supports the zero flag correctly...
+dnl 13 = checking whether printf supports large precisions...
+dnl 14 = checking whether printf survives out-of-memory conditions...
+dnl 15 = checking for snprintf...
+dnl 16 = checking whether snprintf truncates the result as in C99...
+dnl 17 = checking whether snprintf returns a byte count as in C99...
+dnl 18 = checking whether snprintf fully supports the 'n' directive...
+dnl 19 = checking whether snprintf respects a size of 1...
+dnl 20 = checking whether vsnprintf respects a zero size as in C99...
+dnl
+dnl . = yes, # = no.
+dnl
+dnl 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
+dnl glibc 2.5 . . . . . . . . . . . . . . . . . . . .
+dnl glibc 2.3.6 . . . . # . . . . . . . . . . . . . . .
+dnl FreeBSD 13.0 . . . . # . . . . . . . . # . . . . . .
+dnl FreeBSD 5.4, 6.1 . . . . # . . . . . . # . # . . . . . .
+dnl Mac OS X 10.13.5 . . . # # . # . . . . . . . . . . # . .
+dnl Mac OS X 10.5.8 . . . # # . . . . . . # . . . . . . . .
+dnl Mac OS X 10.3.9 . . . . # . . . . . . # . # . . . . . .
+dnl OpenBSD 6.0, 6.7 . . . . # . . . . . . . . # . . . . . .
+dnl OpenBSD 3.9, 4.0 . . # # # # . # . # . # . # . . . . . .
+dnl Cygwin 1.7.0 (2009) . . . # . . . ? . . . . . ? . . . . . .
+dnl Cygwin 1.5.25 (2008) . . . # # . . # . . . . . # . . . . . .
+dnl Cygwin 1.5.19 (2006) # . . # # # . # . # . # # # . . . . . .
+dnl Solaris 11.4 . . # # # . . # . . . # . . . . . . . .
+dnl Solaris 11.3 . . . . # . . # . . . . . . . . . . . .
+dnl Solaris 11.0 . . # # # . . # . . . # . . . . . . . .
+dnl Solaris 10 . . # # # . . # . . . # # . . . . . . .
+dnl Solaris 2.6 ... 9 # . # # # # . # . . . # # . . . # . . .
+dnl Solaris 2.5.1 # . # # # # . # . . . # . . # # # # # #
+dnl AIX 7.1 . . # # # . . . . . . # # . . . . . . .
+dnl AIX 5.2 . . # # # . . . . . . # . . . . . . . .
+dnl AIX 4.3.2, 5.1 # . # # # # . . . . . # . . . . # . . .
+dnl HP-UX 11.31 . . . . # . . . . . . # . . . . # # . .
+dnl HP-UX 11.{00,11,23} # . . . # # . . . . . # . . . . # # . #
+dnl HP-UX 10.20 # . # . # # . ? . . # # . . . . # # ? #
+dnl IRIX 6.5 # . # # # # . # . . . # . . . . # . . .
+dnl OSF/1 5.1 # . # # # # . . . . . # . . . . # . . #
+dnl OSF/1 4.0d # . # # # # . . . . . # . . # # # # # #
+dnl NetBSD 9.0 . . . . # . . . . . . . . . . . . . . .
+dnl NetBSD 5.0 . . . # # . . . . . . # . # . . . . . .
+dnl NetBSD 4.0 . ? ? ? ? ? . ? . ? ? ? ? ? . . . ? ? ?
+dnl NetBSD 3.0 . . . . # # . ? # # ? # . # . . . . . .
+dnl Haiku . . . # # # . # . . . . . ? . . ? . . .
+dnl BeOS # # . # # # . ? # . ? . # ? . . ? . . .
+dnl Android 4.3 . . # # # # # # . # . # . # . . . # . .
+dnl old mingw / msvcrt # # # # # # . . # # . # # ? . # # # . .
+dnl MSVC 9 # # # # # # # . # # . # # ? # # # # . .
+dnl mingw 2009-2011 . # . # . . . . # # . . . ? . . . . . .
+dnl mingw-w64 2011 # # # # # # . . # # . # # ? . # # # . .
diff --git a/m4/signbit.m4 b/m4/signbit.m4
new file mode 100644
index 00000000000..2fea73f854c
--- /dev/null
+++ b/m4/signbit.m4
@@ -0,0 +1,393 @@
+# signbit.m4 serial 21
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_SIGNBIT],
+[
+ AC_REQUIRE([gl_MATH_H_DEFAULTS])
+ AC_REQUIRE([AC_CANONICAL_HOST])
+ AC_CACHE_CHECK([for signbit macro], [gl_cv_func_signbit],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <math.h>
+/* If signbit is defined as a function, don't use it, since calling it for
+ 'float' or 'long double' arguments would involve conversions.
+ If signbit is not declared at all but exists as a library function, don't
+ use it, since the prototype may not match.
+ If signbit is not declared at all but exists as a compiler built-in, don't
+ use it, since it's preferable to use __builtin_signbit* (no warnings,
+ no conversions). */
+#ifndef signbit
+# error "signbit should be a macro"
+#endif
+#include <string.h>
+]gl_SIGNBIT_TEST_PROGRAM
+])],
+ [gl_cv_func_signbit=yes],
+ [gl_cv_func_signbit=no],
+ [case "$host_os" in
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_signbit="guessing yes" ;;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_signbit="guessing yes" ;;
+ # Guess yes on native Windows.
+ mingw*) gl_cv_func_signbit="guessing yes" ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_signbit="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ ])
+ dnl GCC >= 4.0 and clang provide three built-ins for signbit.
+ dnl They can be used without warnings, also in C++, regardless of <math.h>.
+ dnl But they may expand to calls to functions, which may or may not be in
+ dnl libc.
+ AC_CACHE_CHECK([for signbit compiler built-ins],
+ [gl_cv_func_signbit_builtins],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#if (__GNUC__ >= 4) || (__clang_major__ >= 4)
+# define signbit(x) \
+ (sizeof (x) == sizeof (long double) ? __builtin_signbitl (x) : \
+ sizeof (x) == sizeof (double) ? __builtin_signbit (x) : \
+ __builtin_signbitf (x))
+#else
+# error "signbit should be three compiler built-ins"
+#endif
+#include <string.h>
+]gl_SIGNBIT_TEST_PROGRAM
+])],
+ [gl_cv_func_signbit_builtins=yes],
+ [gl_cv_func_signbit_builtins=no],
+ [case "$host_os" in
+ # Guess yes on glibc systems.
+ *-gnu* | gnu*) gl_cv_func_signbit_builtins="guessing yes" ;;
+ # Guess yes on musl systems.
+ *-musl* | midipix*) gl_cv_func_signbit_builtins="guessing yes" ;;
+ # Guess yes on mingw, no on MSVC.
+ mingw*) if test -n "$GCC"; then
+ gl_cv_func_signbit_builtins="guessing yes"
+ else
+ gl_cv_func_signbit_builtins="guessing no"
+ fi
+ ;;
+ # If we don't know, obey --enable-cross-guesses.
+ *) gl_cv_func_signbit_builtins="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ ])
+ dnl Use the compiler built-ins whenever possible, because they are more
+ dnl efficient than the system library functions (if they exist).
+ case "$gl_cv_func_signbit_builtins" in
+ *yes)
+ REPLACE_SIGNBIT_USING_BUILTINS=1
+ ;;
+ *)
+ case "$gl_cv_func_signbit" in
+ *yes) ;;
+ *)
+ dnl REPLACE_SIGNBIT=1 makes sure the signbit[fdl] functions get built.
+ REPLACE_SIGNBIT=1
+ ;;
+ esac
+ ;;
+ esac
+ dnl On Solaris 10, with CC in C++ mode, signbit is not available although
+ dnl is with cc in C mode. This cannot be worked around by defining
+ dnl _XOPEN_SOURCE=600, because the latter does not work in C++ mode on
+ dnl Solaris 11.0. Therefore use the replacement functions on Solaris.
+ case "$host_os" in
+ solaris*)
+ REPLACE_SIGNBIT=1
+ ;;
+ esac
+ if test $REPLACE_SIGNBIT = 1; then
+ gl_FLOAT_SIGN_LOCATION
+ gl_DOUBLE_SIGN_LOCATION
+ gl_LONG_DOUBLE_SIGN_LOCATION
+ if test "$gl_cv_cc_float_signbit" = unknown; then
+ dnl Test whether copysignf() is declared.
+ AC_CHECK_DECLS([copysignf], , , [[#include <math.h>]])
+ if test "$ac_cv_have_decl_copysignf" = yes; then
+ dnl Test whether copysignf() can be used without libm.
+ AC_CACHE_CHECK([whether copysignf can be used without linking with libm],
+ [gl_cv_func_copysignf_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ float x, y;]],
+ [[return copysignf (x, y) < 0;]])],
+ [gl_cv_func_copysignf_no_libm=yes],
+ [gl_cv_func_copysignf_no_libm=no])
+ ])
+ if test $gl_cv_func_copysignf_no_libm = yes; then
+ AC_DEFINE([HAVE_COPYSIGNF_IN_LIBC], [1],
+ [Define if the copysignf function is declared in <math.h> and available in libc.])
+ fi
+ fi
+ fi
+ if test "$gl_cv_cc_double_signbit" = unknown; then
+ dnl Test whether copysign() is declared.
+ AC_CHECK_DECLS([copysign], , , [[#include <math.h>]])
+ if test "$ac_cv_have_decl_copysign" = yes; then
+ dnl Test whether copysign() can be used without libm.
+ AC_CACHE_CHECK([whether copysign can be used without linking with libm],
+ [gl_cv_func_copysign_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ double x, y;]],
+ [[return copysign (x, y) < 0;]])],
+ [gl_cv_func_copysign_no_libm=yes],
+ [gl_cv_func_copysign_no_libm=no])
+ ])
+ if test $gl_cv_func_copysign_no_libm = yes; then
+ AC_DEFINE([HAVE_COPYSIGN_IN_LIBC], [1],
+ [Define if the copysign function is declared in <math.h> and available in libc.])
+ fi
+ fi
+ fi
+ if test "$gl_cv_cc_long_double_signbit" = unknown; then
+ dnl Test whether copysignl() is declared.
+ AC_CHECK_DECLS([copysignl], , , [[#include <math.h>]])
+ if test "$ac_cv_have_decl_copysignl" = yes; then
+ dnl Test whether copysignl() can be used without libm.
+ AC_CACHE_CHECK([whether copysignl can be used without linking with libm],
+ [gl_cv_func_copysignl_no_libm],
+ [
+ AC_LINK_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <math.h>
+ long double x, y;]],
+ [[return copysignl (x, y) < 0;]])],
+ [gl_cv_func_copysignl_no_libm=yes],
+ [gl_cv_func_copysignl_no_libm=no])
+ ])
+ if test $gl_cv_func_copysignl_no_libm = yes; then
+ AC_DEFINE([HAVE_COPYSIGNL_IN_LIBC], [1],
+ [Define if the copysignl function is declared in <math.h> and available in libc.])
+ fi
+ fi
+ fi
+ fi
+])
+
+AC_DEFUN([gl_SIGNBIT_TEST_PROGRAM], [[
+/* Global variables.
+ Needed because GCC 4 constant-folds __builtin_signbitl (literal)
+ but cannot constant-fold __builtin_signbitl (variable). */
+float vf;
+double vd;
+long double vl;
+int main ()
+{
+/* HP cc on HP-UX 10.20 has a bug with the constant expression -0.0.
+ So we use -p0f and -p0d instead. */
+float p0f = 0.0f;
+float m0f = -p0f;
+double p0d = 0.0;
+double m0d = -p0d;
+/* On HP-UX 10.20, negating 0.0L does not yield -0.0L.
+ So we use another constant expression instead.
+ But that expression does not work on other platforms, such as when
+ cross-compiling to PowerPC on Mac OS X 10.5. */
+long double p0l = 0.0L;
+#if defined __hpux || defined __sgi
+long double m0l = -LDBL_MIN * LDBL_MIN;
+#else
+long double m0l = -p0l;
+#endif
+ int result = 0;
+ if (signbit (vf)) /* link check */
+ vf++;
+ {
+ float plus_inf = 1.0f / p0f;
+ float minus_inf = -1.0f / p0f;
+ if (!(!signbit (255.0f)
+ && signbit (-255.0f)
+ && !signbit (p0f)
+ && (memcmp (&m0f, &p0f, sizeof (float)) == 0 || signbit (m0f))
+ && !signbit (plus_inf)
+ && signbit (minus_inf)))
+ result |= 1;
+ }
+ if (signbit (vd)) /* link check */
+ vd++;
+ {
+ double plus_inf = 1.0 / p0d;
+ double minus_inf = -1.0 / p0d;
+ if (!(!signbit (255.0)
+ && signbit (-255.0)
+ && !signbit (p0d)
+ && (memcmp (&m0d, &p0d, sizeof (double)) == 0 || signbit (m0d))
+ && !signbit (plus_inf)
+ && signbit (minus_inf)))
+ result |= 2;
+ }
+ if (signbit (vl)) /* link check */
+ vl++;
+ {
+ long double plus_inf = 1.0L / p0l;
+ long double minus_inf = -1.0L / p0l;
+ if (signbit (255.0L))
+ result |= 4;
+ if (!signbit (-255.0L))
+ result |= 4;
+ if (signbit (p0l))
+ result |= 8;
+ if (!(memcmp (&m0l, &p0l, sizeof (long double)) == 0 || signbit (m0l)))
+ result |= 16;
+ if (signbit (plus_inf))
+ result |= 32;
+ if (!signbit (minus_inf))
+ result |= 64;
+ }
+ return result;
+}
+]])
+
+AC_DEFUN([gl_FLOAT_SIGN_LOCATION],
+[
+ gl_FLOATTYPE_SIGN_LOCATION([float], [gl_cv_cc_float_signbit], [f], [FLT])
+])
+
+AC_DEFUN([gl_DOUBLE_SIGN_LOCATION],
+[
+ gl_FLOATTYPE_SIGN_LOCATION([double], [gl_cv_cc_double_signbit], [], [DBL])
+])
+
+AC_DEFUN([gl_LONG_DOUBLE_SIGN_LOCATION],
+[
+ gl_FLOATTYPE_SIGN_LOCATION([long double], [gl_cv_cc_long_double_signbit], [L], [LDBL])
+])
+
+AC_DEFUN([gl_FLOATTYPE_SIGN_LOCATION],
+[
+ AC_CACHE_CHECK([where to find the sign bit in a '$1'],
+ [$2],
+ [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stddef.h>
+#include <stdio.h>
+#define NWORDS \
+ ((sizeof ($1) + sizeof (unsigned int) - 1) / sizeof (unsigned int))
+typedef union { $1 value; unsigned int word[NWORDS]; }
+ memory_float;
+static memory_float plus = { 1.0$3 };
+static memory_float minus = { -1.0$3 };
+int main ()
+{
+ size_t j, k, i;
+ unsigned int m;
+ FILE *fp = fopen ("conftest.out", "w");
+ if (fp == NULL)
+ return 1;
+ /* Find the different bit. */
+ k = 0; m = 0;
+ for (j = 0; j < NWORDS; j++)
+ {
+ unsigned int x = plus.word[j] ^ minus.word[j];
+ if ((x & (x - 1)) || (x && m))
+ {
+ /* More than one bit difference. */
+ fprintf (fp, "unknown");
+ fclose (fp);
+ return 2;
+ }
+ if (x)
+ {
+ k = j;
+ m = x;
+ }
+ }
+ if (m == 0)
+ {
+ /* No difference. */
+ fprintf (fp, "unknown");
+ fclose (fp);
+ return 3;
+ }
+ /* Now m = plus.word[k] ^ ~minus.word[k]. */
+ if (plus.word[k] & ~minus.word[k])
+ {
+ /* Oh? The sign bit is set in the positive and cleared in the negative
+ numbers? */
+ fprintf (fp, "unknown");
+ fclose (fp);
+ return 4;
+ }
+ for (i = 0; ; i++)
+ if ((m >> i) & 1)
+ break;
+ fprintf (fp, "word %d bit %d", (int) k, (int) i);
+ if (fclose (fp) != 0)
+ return 5;
+ return 0;
+}
+ ]])],
+ [$2=`cat conftest.out`],
+ [$2="unknown"],
+ [
+ dnl When cross-compiling, we don't know. It depends on the
+ dnl ABI and compiler version. There are too many cases.
+ $2="unknown"
+ ])
+ rm -f conftest.out
+ ])
+ case "$]$2[" in
+ word*bit*)
+ word=`echo "$]$2[" | sed -e 's/word //' -e 's/ bit.*//'`
+ bit=`echo "$]$2[" | sed -e 's/word.*bit //'`
+ AC_DEFINE_UNQUOTED([$4][_SIGNBIT_WORD], [$word],
+ [Define as the word index where to find the sign of '$1'.])
+ AC_DEFINE_UNQUOTED([$4][_SIGNBIT_BIT], [$bit],
+ [Define as the bit index in the word where to find the sign of '$1'.])
+ ;;
+ esac
+])
+
+# Expands to code that defines a function signbitf(float).
+# It extracts the sign bit of a non-NaN value.
+AC_DEFUN([gl_FLOAT_SIGNBIT_CODE],
+[
+ gl_FLOATTYPE_SIGNBIT_CODE([float], [f], [f])
+])
+
+# Expands to code that defines a function signbitd(double).
+# It extracts the sign bit of a non-NaN value.
+AC_DEFUN([gl_DOUBLE_SIGNBIT_CODE],
+[
+ gl_FLOATTYPE_SIGNBIT_CODE([double], [d], [])
+])
+
+# Expands to code that defines a function signbitl(long double).
+# It extracts the sign bit of a non-NaN value.
+AC_DEFUN([gl_LONG_DOUBLE_SIGNBIT_CODE],
+[
+ gl_FLOATTYPE_SIGNBIT_CODE([long double], [l], [L])
+])
+
+AC_DEFUN([gl_FLOATTYPE_SIGNBIT_CODE],
+[[
+static int
+signbit$2 ($1 value)
+{
+ typedef union { $1 f; unsigned char b[sizeof ($1)]; } float_union;
+ static float_union plus_one = { 1.0$3 }; /* unused bits are zero here */
+ static float_union minus_one = { -1.0$3 }; /* unused bits are zero here */
+ /* Compute the sign bit mask as the XOR of plus_one and minus_one. */
+ float_union u;
+ unsigned int i;
+ u.f = value;
+ for (i = 0; i < sizeof ($1); i++)
+ if (u.b[i] & (plus_one.b[i] ^ minus_one.b[i]))
+ return 1;
+ return 0;
+}
+]])
diff --git a/m4/size_max.m4 b/m4/size_max.m4
new file mode 100644
index 00000000000..0763366dfc3
--- /dev/null
+++ b/m4/size_max.m4
@@ -0,0 +1,75 @@
+# size_max.m4 serial 12
+dnl Copyright (C) 2003, 2005-2006, 2008-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Bruno Haible.
+
+AC_PREREQ([2.61])
+
+AC_DEFUN([gl_SIZE_MAX],
+[
+ AC_CHECK_HEADERS([stdint.h])
+ dnl First test whether the system already has SIZE_MAX.
+ AC_CACHE_CHECK([for SIZE_MAX], [gl_cv_size_max], [
+ gl_cv_size_max=no
+ AC_EGREP_CPP([Found it], [
+#include <limits.h>
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#ifdef SIZE_MAX
+Found it
+#endif
+], [gl_cv_size_max=yes])
+ if test $gl_cv_size_max != yes; then
+ dnl Define it ourselves. Here we assume that the type 'size_t' is not wider
+ dnl than the type 'unsigned long'. Try hard to find a definition that can
+ dnl be used in a preprocessor #if, i.e. doesn't contain a cast.
+ AC_COMPUTE_INT([size_t_bits_minus_1], [sizeof (size_t) * CHAR_BIT - 1],
+ [#include <stddef.h>
+#include <limits.h>], [size_t_bits_minus_1=])
+ AC_COMPUTE_INT([fits_in_uint], [sizeof (size_t) <= sizeof (unsigned int)],
+ [#include <stddef.h>], [fits_in_uint=])
+ if test -n "$size_t_bits_minus_1" && test -n "$fits_in_uint"; then
+ if test $fits_in_uint = 1; then
+ dnl Even though SIZE_MAX fits in an unsigned int, it must be of type
+ dnl 'unsigned long' if the type 'size_t' is the same as 'unsigned long'.
+ AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <stddef.h>
+ extern size_t foo;
+ extern unsigned long foo;
+ ]],
+ [[]])],
+ [fits_in_uint=0])
+ fi
+ dnl We cannot use 'expr' to simplify this expression, because 'expr'
+ dnl works only with 'long' integers in the host environment, while we
+ dnl might be cross-compiling from a 32-bit platform to a 64-bit platform.
+ if test $fits_in_uint = 1; then
+ gl_cv_size_max="(((1U << $size_t_bits_minus_1) - 1) * 2 + 1)"
+ else
+ gl_cv_size_max="(((1UL << $size_t_bits_minus_1) - 1) * 2 + 1)"
+ fi
+ else
+ dnl Shouldn't happen, but who knows...
+ gl_cv_size_max='((size_t)~(size_t)0)'
+ fi
+ fi
+ ])
+ if test "$gl_cv_size_max" != yes; then
+ AC_DEFINE_UNQUOTED([SIZE_MAX], [$gl_cv_size_max],
+ [Define as the maximum value of type 'size_t', if the system doesn't define it.])
+ fi
+ dnl Don't redefine SIZE_MAX in config.h if config.h is re-included after
+ dnl <stdint.h>. Remember that the #undef in AH_VERBATIM gets replaced with
+ dnl #define by AC_DEFINE_UNQUOTED.
+ AH_VERBATIM([SIZE_MAX],
+[/* Define as the maximum value of type 'size_t', if the system doesn't define
+ it. */
+#ifndef SIZE_MAX
+# undef SIZE_MAX
+#endif])
+])
diff --git a/m4/stdint_h.m4 b/m4/stdint_h.m4
new file mode 100644
index 00000000000..70349f6cb21
--- /dev/null
+++ b/m4/stdint_h.m4
@@ -0,0 +1,27 @@
+# stdint_h.m4 serial 9
+dnl Copyright (C) 1997-2004, 2006, 2008-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+dnl From Paul Eggert.
+
+# Define HAVE_STDINT_H_WITH_UINTMAX if <stdint.h> exists,
+# doesn't clash with <sys/types.h>, and declares uintmax_t.
+
+AC_DEFUN([gl_AC_HEADER_STDINT_H],
+[
+ AC_CACHE_CHECK([for stdint.h], [gl_cv_header_stdint_h],
+ [AC_COMPILE_IFELSE(
+ [AC_LANG_PROGRAM(
+ [[#include <sys/types.h>
+ #include <stdint.h>]],
+ [[uintmax_t i = (uintmax_t) -1; return !i;]])],
+ [gl_cv_header_stdint_h=yes],
+ [gl_cv_header_stdint_h=no])])
+ if test $gl_cv_header_stdint_h = yes; then
+ AC_DEFINE_UNQUOTED([HAVE_STDINT_H_WITH_UINTMAX], [1],
+ [Define if <stdint.h> exists, doesn't clash with <sys/types.h>,
+ and declares uintmax_t. ])
+ fi
+])
diff --git a/m4/stpncpy.m4 b/m4/stpncpy.m4
new file mode 100644
index 00000000000..073607004be
--- /dev/null
+++ b/m4/stpncpy.m4
@@ -0,0 +1,108 @@
+# stpncpy.m4 serial 22
+dnl Copyright (C) 2002-2003, 2005-2007, 2009-2023 Free Software Foundation,
+dnl Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_STPNCPY],
+[
+ AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles
+
+ dnl Persuade glibc <string.h> to declare stpncpy().
+ AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+
+ dnl The stpncpy() declaration in lib/string.in.h uses 'restrict'.
+ AC_REQUIRE([AC_C_RESTRICT])
+
+ AC_REQUIRE([gl_STRING_H_DEFAULTS])
+
+ dnl Both glibc and AIX (4.3.3, 5.1) have an stpncpy() function
+ dnl declared in <string.h>. Its side effects are the same as those
+ dnl of strncpy():
+ dnl stpncpy (dest, src, n)
+ dnl overwrites dest[0..n-1], min(strlen(src),n) bytes coming from src,
+ dnl and the remaining bytes being NULs. However, the return value is
+ dnl in glibc: dest + min(strlen(src),n)
+ dnl in AIX: dest + max(0,n-1)
+ dnl Only the glibc return value is useful in practice.
+
+ AC_CHECK_DECLS_ONCE([stpncpy])
+ gl_CHECK_FUNCS_ANDROID([stpncpy], [[#include <string.h>]])
+ if test $ac_cv_func_stpncpy = yes; then
+ AC_CACHE_CHECK([for working stpncpy], [gl_cv_func_stpncpy], [
+ AC_RUN_IFELSE(
+ [AC_LANG_SOURCE([[
+#include <stdlib.h>
+#include <string.h> /* for strcpy */
+/* The stpncpy prototype is missing in <string.h> on AIX 4. */
+#if !HAVE_DECL_STPNCPY
+extern
+# ifdef __cplusplus
+"C"
+# endif
+char *stpncpy (char *dest, const char *src, size_t n);
+#endif
+int main ()
+{
+ int result = 0;
+ const char *src = "Hello";
+ char dest[10];
+ /* AIX 4.3.3 and AIX 5.1 stpncpy() returns dest+1 here. */
+ {
+ strcpy (dest, "\377\377\377\377\377\377");
+ if (stpncpy (dest, src, 2) != dest + 2)
+ result |= 1;
+ }
+ /* AIX 4.3.3 and AIX 5.1 stpncpy() returns dest+4 here. */
+ {
+ strcpy (dest, "\377\377\377\377\377\377");
+ if (stpncpy (dest, src, 5) != dest + 5)
+ result |= 2;
+ }
+ /* AIX 4.3.3 and AIX 5.1 stpncpy() returns dest+6 here. */
+ {
+ strcpy (dest, "\377\377\377\377\377\377");
+ if (stpncpy (dest, src, 7) != dest + 5)
+ result |= 4;
+ }
+ return result;
+}
+]])],
+ [gl_cv_func_stpncpy=yes],
+ [gl_cv_func_stpncpy=no],
+ [dnl Guess yes on glibc systems and musl systems.
+ AC_EGREP_CPP([Thanks for using GNU], [
+#include <features.h>
+#ifdef __GNU_LIBRARY__
+ Thanks for using GNU
+#endif
+], [gl_cv_func_stpncpy="guessing yes"],
+ [case "$host_os" in
+ *-musl* | midipix*) gl_cv_func_stpncpy="guessing yes" ;;
+ *) gl_cv_func_stpncpy="$gl_cross_guess_normal" ;;
+ esac
+ ])
+ ])
+ ])
+ case "$gl_cv_func_stpncpy" in
+ *yes)
+ AC_DEFINE([HAVE_STPNCPY], [1],
+ [Define if you have the stpncpy() function and it works.])
+ ;;
+ *)
+ REPLACE_STPNCPY=1
+ ;;
+ esac
+ else
+ HAVE_STPNCPY=0
+ case "$gl_cv_onwards_func_stpncpy" in
+ future*) REPLACE_STPNCPY=1 ;;
+ esac
+ fi
+])
+
+# Prerequisites of lib/stpncpy.c.
+AC_DEFUN([gl_PREREQ_STPNCPY], [
+ :
+])
diff --git a/m4/vasnprintf.m4 b/m4/vasnprintf.m4
new file mode 100644
index 00000000000..fda90c402b4
--- /dev/null
+++ b/m4/vasnprintf.m4
@@ -0,0 +1,298 @@
+# vasnprintf.m4 serial 39
+dnl Copyright (C) 2002-2004, 2006-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_VASNPRINTF],
+[
+ AC_CHECK_FUNCS_ONCE([vasnprintf])
+ if test $ac_cv_func_vasnprintf = no; then
+ gl_REPLACE_VASNPRINTF
+ fi
+])
+
+AC_DEFUN([gl_REPLACE_VASNPRINTF],
+[
+ AC_CHECK_FUNCS_ONCE([vasnprintf])
+ AC_LIBOBJ([vasnprintf])
+ AC_LIBOBJ([printf-args])
+ AC_LIBOBJ([printf-parse])
+ AC_LIBOBJ([asnprintf])
+ if test $ac_cv_func_vasnprintf = yes; then
+ AC_DEFINE([REPLACE_VASNPRINTF], [1],
+ [Define if vasnprintf exists but is overridden by gnulib.])
+ fi
+ gl_PREREQ_PRINTF_ARGS
+ gl_PREREQ_PRINTF_PARSE
+ gl_PREREQ_VASNPRINTF
+ gl_PREREQ_ASNPRINTF
+])
+
+# Prerequisites of lib/printf-args.h, lib/printf-args.c.
+AC_DEFUN([gl_PREREQ_PRINTF_ARGS],
+[
+ AC_REQUIRE([gt_TYPE_WCHAR_T])
+ AC_REQUIRE([gt_TYPE_WINT_T])
+])
+
+# Prerequisites of lib/printf-parse.h, lib/printf-parse.c.
+AC_DEFUN([gl_PREREQ_PRINTF_PARSE],
+[
+ AC_REQUIRE([gl_FEATURES_H])
+ AC_REQUIRE([gt_TYPE_WCHAR_T])
+ AC_REQUIRE([gt_TYPE_WINT_T])
+ AC_REQUIRE([AC_TYPE_SIZE_T])
+ AC_CHECK_TYPE([ptrdiff_t], ,
+ [AC_DEFINE([ptrdiff_t], [long],
+ [Define as the type of the result of subtracting two pointers, if the system doesn't define it.])
+ ])
+ AC_REQUIRE([gt_AC_TYPE_INTMAX_T])
+])
+
+# Prerequisites of lib/vasnprintf.c.
+AC_DEFUN_ONCE([gl_PREREQ_VASNPRINTF],
+[
+ AC_REQUIRE([AC_FUNC_ALLOCA])
+ AC_REQUIRE([gt_TYPE_WCHAR_T])
+ AC_REQUIRE([gt_TYPE_WINT_T])
+ AC_CHECK_FUNCS([snprintf strnlen wcslen wcsnlen mbrtowc wcrtomb])
+ dnl Use the _snprintf function only if it is declared (because on NetBSD it
+ dnl is defined as a weak alias of snprintf; we prefer to use the latter).
+ AC_CHECK_DECLS([_snprintf], , , [[#include <stdio.h>]])
+ dnl Knowing DBL_EXPBIT0_WORD and DBL_EXPBIT0_BIT enables an optimization
+ dnl in the code for NEED_PRINTF_LONG_DOUBLE || NEED_PRINTF_DOUBLE.
+ AC_REQUIRE([gl_DOUBLE_EXPONENT_LOCATION])
+ dnl We can avoid a lot of code by assuming that snprintf's return value
+ dnl conforms to ISO C99. So check that.
+ AC_REQUIRE([gl_SNPRINTF_RETVAL_C99])
+ case "$gl_cv_func_snprintf_retval_c99" in
+ *yes)
+ AC_DEFINE([HAVE_SNPRINTF_RETVAL_C99], [1],
+ [Define if the return value of the snprintf function is the number of
+ of bytes (excluding the terminating NUL) that would have been produced
+ if the buffer had been large enough.])
+ ;;
+ esac
+ dnl Additionally, the use of %n can be eliminated by assuming that snprintf
+ dnl always produces NUL-terminated strings (no truncation).
+ AC_REQUIRE([gl_SNPRINTF_TRUNCATION_C99])
+ case "$gl_cv_func_snprintf_truncation_c99" in
+ *yes)
+ AC_DEFINE([HAVE_SNPRINTF_TRUNCATION_C99], [1],
+ [Define if the string produced by the snprintf function is always NUL
+ terminated.])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting 'long double'
+# arguments.
+AC_DEFUN_ONCE([gl_PREREQ_VASNPRINTF_LONG_DOUBLE],
+[
+ AC_REQUIRE([gl_PRINTF_LONG_DOUBLE])
+ case "$gl_cv_func_printf_long_double" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_LONG_DOUBLE], [1],
+ [Define if the vasnprintf implementation needs special code for
+ 'long double' arguments.])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting infinite 'double'
+# arguments.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE],
+[
+ AC_REQUIRE([gl_PRINTF_INFINITE])
+ case "$gl_cv_func_printf_infinite" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_INFINITE_DOUBLE], [1],
+ [Define if the vasnprintf implementation needs special code for
+ infinite 'double' arguments.])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting infinite 'long double'
+# arguments.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE],
+[
+ AC_REQUIRE([gl_PRINTF_INFINITE_LONG_DOUBLE])
+ dnl There is no need to set NEED_PRINTF_INFINITE_LONG_DOUBLE if
+ dnl NEED_PRINTF_LONG_DOUBLE is already set.
+ AC_REQUIRE([gl_PREREQ_VASNPRINTF_LONG_DOUBLE])
+ case "$gl_cv_func_printf_long_double" in
+ *yes)
+ case "$gl_cv_func_printf_infinite_long_double" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_INFINITE_LONG_DOUBLE], [1],
+ [Define if the vasnprintf implementation needs special code for
+ infinite 'long double' arguments.])
+ ;;
+ esac
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting the 'a' directive.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_A],
+[
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_A])
+ case "$gl_cv_func_printf_directive_a" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_DIRECTIVE_A], [1],
+ [Define if the vasnprintf implementation needs special code for
+ the 'a' and 'A' directives.])
+ gl_CHECK_FUNCS_ANDROID([nl_langinfo], [[#include <langinfo.h>]])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting the 'F' directive.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_F],
+[
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_F])
+ case "$gl_cv_func_printf_directive_f" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_DIRECTIVE_F], [1],
+ [Define if the vasnprintf implementation needs special code for
+ the 'F' directive.])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting the 'ls' directive.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_DIRECTIVE_LS],
+[
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_LS])
+ case "$gl_cv_func_printf_directive_ls" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_DIRECTIVE_LS], [1],
+ [Define if the vasnprintf implementation needs special code for
+ the 'ls' directive.])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting the ' flag.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_FLAG_GROUPING],
+[
+ AC_REQUIRE([gl_PRINTF_FLAG_GROUPING])
+ case "$gl_cv_func_printf_flag_grouping" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_FLAG_GROUPING], [1],
+ [Define if the vasnprintf implementation needs special code for the
+ ' flag.])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting the '-' flag.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_FLAG_LEFTADJUST],
+[
+ AC_REQUIRE([gl_PRINTF_FLAG_LEFTADJUST])
+ case "$gl_cv_func_printf_flag_leftadjust" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_FLAG_LEFTADJUST], [1],
+ [Define if the vasnprintf implementation needs special code for the
+ '-' flag.])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting the 0 flag.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_FLAG_ZERO],
+[
+ AC_REQUIRE([gl_PRINTF_FLAG_ZERO])
+ case "$gl_cv_func_printf_flag_zero" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_FLAG_ZERO], [1],
+ [Define if the vasnprintf implementation needs special code for the
+ 0 flag.])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for supporting large precisions.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_PRECISION],
+[
+ AC_REQUIRE([gl_PRINTF_PRECISION])
+ case "$gl_cv_func_printf_precision" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_UNBOUNDED_PRECISION], [1],
+ [Define if the vasnprintf implementation needs special code for
+ supporting large precisions without arbitrary bounds.])
+ AC_DEFINE([NEED_PRINTF_DOUBLE], [1],
+ [Define if the vasnprintf implementation needs special code for
+ 'double' arguments.])
+ AC_DEFINE([NEED_PRINTF_LONG_DOUBLE], [1],
+ [Define if the vasnprintf implementation needs special code for
+ 'long double' arguments.])
+ ;;
+ esac
+])
+
+# Extra prerequisites of lib/vasnprintf.c for surviving out-of-memory
+# conditions.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_ENOMEM],
+[
+ AC_REQUIRE([gl_PRINTF_ENOMEM])
+ case "$gl_cv_func_printf_enomem" in
+ *yes)
+ ;;
+ *)
+ AC_DEFINE([NEED_PRINTF_ENOMEM], [1],
+ [Define if the vasnprintf implementation needs special code for
+ surviving out-of-memory conditions.])
+ AC_DEFINE([NEED_PRINTF_DOUBLE], [1],
+ [Define if the vasnprintf implementation needs special code for
+ 'double' arguments.])
+ AC_DEFINE([NEED_PRINTF_LONG_DOUBLE], [1],
+ [Define if the vasnprintf implementation needs special code for
+ 'long double' arguments.])
+ ;;
+ esac
+])
+
+# Prerequisites of lib/vasnprintf.c including all extras for POSIX compliance.
+AC_DEFUN([gl_PREREQ_VASNPRINTF_WITH_EXTRAS],
+[
+ AC_REQUIRE([gl_PREREQ_VASNPRINTF])
+ gl_PREREQ_VASNPRINTF_LONG_DOUBLE
+ gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE
+ gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE
+ gl_PREREQ_VASNPRINTF_DIRECTIVE_A
+ gl_PREREQ_VASNPRINTF_DIRECTIVE_F
+ gl_PREREQ_VASNPRINTF_DIRECTIVE_LS
+ gl_PREREQ_VASNPRINTF_FLAG_GROUPING
+ gl_PREREQ_VASNPRINTF_FLAG_LEFTADJUST
+ gl_PREREQ_VASNPRINTF_FLAG_ZERO
+ gl_PREREQ_VASNPRINTF_PRECISION
+ gl_PREREQ_VASNPRINTF_ENOMEM
+])
+
+# Prerequisites of lib/asnprintf.c.
+AC_DEFUN([gl_PREREQ_ASNPRINTF],
+[
+])
diff --git a/m4/vasprintf-posix.m4 b/m4/vasprintf-posix.m4
new file mode 100644
index 00000000000..7c198a64108
--- /dev/null
+++ b/m4/vasprintf-posix.m4
@@ -0,0 +1,101 @@
+# vasprintf-posix.m4 serial 13
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_VASPRINTF_POSIX],
+[
+ AC_REQUIRE([gl_PRINTF_SIZES_C99])
+ AC_REQUIRE([gl_PRINTF_LONG_DOUBLE])
+ AC_REQUIRE([gl_PRINTF_INFINITE])
+ AC_REQUIRE([gl_PRINTF_INFINITE_LONG_DOUBLE])
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_A])
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_F])
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_N])
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_LS])
+ AC_REQUIRE([gl_PRINTF_POSITIONS])
+ AC_REQUIRE([gl_PRINTF_FLAG_GROUPING])
+ AC_REQUIRE([gl_PRINTF_FLAG_LEFTADJUST])
+ AC_REQUIRE([gl_PRINTF_FLAG_ZERO])
+ AC_REQUIRE([gl_PRINTF_PRECISION])
+ AC_REQUIRE([gl_PRINTF_ENOMEM])
+ gl_cv_func_vasprintf_posix=no
+ AC_CHECK_FUNCS([vasprintf])
+ case "$gl_cv_func_printf_sizes_c99" in
+ *yes)
+ case "$gl_cv_func_printf_long_double" in
+ *yes)
+ case "$gl_cv_func_printf_infinite" in
+ *yes)
+ case "$gl_cv_func_printf_infinite_long_double" in
+ *yes)
+ case "$gl_cv_func_printf_directive_a" in
+ *yes)
+ case "$gl_cv_func_printf_directive_f" in
+ *yes)
+ case "$gl_cv_func_printf_directive_n" in
+ *yes)
+ case "$gl_cv_func_printf_directive_ls" in
+ *yes)
+ case "$gl_cv_func_printf_positions" in
+ *yes)
+ case "$gl_cv_func_printf_flag_grouping" in
+ *yes)
+ case "$gl_cv_func_printf_flag_leftadjust" in
+ *yes)
+ case "$gl_cv_func_printf_flag_zero" in
+ *yes)
+ case "$gl_cv_func_printf_precision" in
+ *yes)
+ case "$gl_cv_func_printf_enomem" in
+ *yes)
+ if test $ac_cv_func_vasprintf = yes; then
+ # vasprintf exists and is
+ # already POSIX compliant.
+ gl_cv_func_vasprintf_posix=yes
+ fi
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ if test $gl_cv_func_vasprintf_posix = no; then
+ gl_PREREQ_VASNPRINTF_LONG_DOUBLE
+ gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE
+ gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE
+ gl_PREREQ_VASNPRINTF_DIRECTIVE_A
+ gl_PREREQ_VASNPRINTF_DIRECTIVE_F
+ gl_PREREQ_VASNPRINTF_DIRECTIVE_LS
+ gl_PREREQ_VASNPRINTF_FLAG_GROUPING
+ gl_PREREQ_VASNPRINTF_FLAG_LEFTADJUST
+ gl_PREREQ_VASNPRINTF_FLAG_ZERO
+ gl_PREREQ_VASNPRINTF_PRECISION
+ gl_PREREQ_VASNPRINTF_ENOMEM
+ gl_REPLACE_VASNPRINTF
+ gl_REPLACE_VASPRINTF
+ fi
+])
diff --git a/m4/vasprintf.m4 b/m4/vasprintf.m4
new file mode 100644
index 00000000000..6e6156a7549
--- /dev/null
+++ b/m4/vasprintf.m4
@@ -0,0 +1,46 @@
+# vasprintf.m4 serial 6
+dnl Copyright (C) 2002-2003, 2006-2007, 2009-2023 Free Software Foundation,
+dnl Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_VASPRINTF],
+[
+ AC_CHECK_FUNCS([vasprintf])
+ if test $ac_cv_func_vasprintf = no; then
+ gl_REPLACE_VASPRINTF
+ fi
+])
+
+AC_DEFUN([gl_REPLACE_VASPRINTF],
+[
+ AC_LIBOBJ([vasprintf])
+ AC_LIBOBJ([asprintf])
+ AC_REQUIRE([gl_STDIO_H_DEFAULTS])
+ if test $ac_cv_func_vasprintf = yes; then
+ REPLACE_VASPRINTF=1
+ else
+ HAVE_VASPRINTF=0
+ fi
+ gl_PREREQ_VASPRINTF_H
+ gl_PREREQ_VASPRINTF
+ gl_PREREQ_ASPRINTF
+])
+
+# Prerequisites of the vasprintf portion of lib/stdio.h.
+AC_DEFUN([gl_PREREQ_VASPRINTF_H],
+[
+ dnl Persuade glibc <stdio.h> to declare asprintf() and vasprintf().
+ AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])
+])
+
+# Prerequisites of lib/vasprintf.c.
+AC_DEFUN([gl_PREREQ_VASPRINTF],
+[
+])
+
+# Prerequisites of lib/asprintf.c.
+AC_DEFUN([gl_PREREQ_ASPRINTF],
+[
+])
diff --git a/m4/vfprintf-posix.m4 b/m4/vfprintf-posix.m4
new file mode 100644
index 00000000000..ec680522142
--- /dev/null
+++ b/m4/vfprintf-posix.m4
@@ -0,0 +1,110 @@
+# vfprintf-posix.m4 serial 14
+dnl Copyright (C) 2007-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_VFPRINTF_POSIX],
+[
+ AC_REQUIRE([gl_PRINTF_SIZES_C99])
+ AC_REQUIRE([gl_PRINTF_LONG_DOUBLE])
+ AC_REQUIRE([gl_PRINTF_INFINITE])
+ AC_REQUIRE([gl_PRINTF_INFINITE_LONG_DOUBLE])
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_A])
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_F])
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_N])
+ AC_REQUIRE([gl_PRINTF_DIRECTIVE_LS])
+ AC_REQUIRE([gl_PRINTF_POSITIONS])
+ AC_REQUIRE([gl_PRINTF_FLAG_GROUPING])
+ AC_REQUIRE([gl_PRINTF_FLAG_LEFTADJUST])
+ AC_REQUIRE([gl_PRINTF_FLAG_ZERO])
+ AC_REQUIRE([gl_PRINTF_PRECISION])
+ AC_REQUIRE([gl_PRINTF_ENOMEM])
+ gl_cv_func_vfprintf_posix=no
+ case "$gl_cv_func_printf_sizes_c99" in
+ *yes)
+ case "$gl_cv_func_printf_long_double" in
+ *yes)
+ case "$gl_cv_func_printf_infinite" in
+ *yes)
+ case "$gl_cv_func_printf_infinite_long_double" in
+ *yes)
+ case "$gl_cv_func_printf_directive_a" in
+ *yes)
+ case "$gl_cv_func_printf_directive_f" in
+ *yes)
+ case "$gl_cv_func_printf_directive_n" in
+ *yes)
+ case "$gl_cv_func_printf_directive_ls" in
+ *yes)
+ case "$gl_cv_func_printf_positions" in
+ *yes)
+ case "$gl_cv_func_printf_flag_grouping" in
+ *yes)
+ case "$gl_cv_func_printf_flag_leftadjust" in
+ *yes)
+ case "$gl_cv_func_printf_flag_zero" in
+ *yes)
+ case "$gl_cv_func_printf_precision" in
+ *yes)
+ case "$gl_cv_func_printf_enomem" in
+ *yes)
+ # vfprintf exists and is
+ # already POSIX compliant.
+ gl_cv_func_vfprintf_posix=yes
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ esac
+ if test $gl_cv_func_vfprintf_posix = no; then
+ gl_PREREQ_VASNPRINTF_LONG_DOUBLE
+ gl_PREREQ_VASNPRINTF_INFINITE_DOUBLE
+ gl_PREREQ_VASNPRINTF_INFINITE_LONG_DOUBLE
+ gl_PREREQ_VASNPRINTF_DIRECTIVE_A
+ gl_PREREQ_VASNPRINTF_DIRECTIVE_F
+ gl_PREREQ_VASNPRINTF_DIRECTIVE_LS
+ gl_PREREQ_VASNPRINTF_FLAG_GROUPING
+ gl_PREREQ_VASNPRINTF_FLAG_LEFTADJUST
+ gl_PREREQ_VASNPRINTF_FLAG_ZERO
+ gl_PREREQ_VASNPRINTF_PRECISION
+ gl_PREREQ_VASNPRINTF_ENOMEM
+ gl_REPLACE_VASNPRINTF
+ gl_REPLACE_VFPRINTF
+ fi
+])
+
+AC_DEFUN([gl_REPLACE_VFPRINTF],
+[
+ AC_REQUIRE([gl_STDIO_H_DEFAULTS])
+ AC_LIBOBJ([vfprintf])
+ REPLACE_VFPRINTF=1
+ AC_DEFINE([REPLACE_VFPRINTF_POSIX], [1],
+ [Define if vfprintf is overridden by a POSIX compliant gnulib implementation.])
+ gl_PREREQ_VFPRINTF
+])
+
+AC_DEFUN([gl_PREREQ_VFPRINTF], [:])
diff --git a/m4/xsize.m4 b/m4/xsize.m4
new file mode 100644
index 00000000000..649db9c5eab
--- /dev/null
+++ b/m4/xsize.m4
@@ -0,0 +1,12 @@
+# xsize.m4 serial 5
+dnl Copyright (C) 2003-2004, 2008-2023 Free Software Foundation, Inc.
+dnl This file is free software; the Free Software Foundation
+dnl gives unlimited permission to copy and/or distribute it,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_XSIZE],
+[
+ dnl Prerequisites of lib/xsize.h.
+ AC_REQUIRE([gl_SIZE_MAX])
+ AC_CHECK_HEADERS([stdint.h])
+])
diff --git a/msdos/sed1v2.inp b/msdos/sed1v2.inp
index 162ccb3e8d8..71aa27afce2 100644
--- a/msdos/sed1v2.inp
+++ b/msdos/sed1v2.inp
@@ -56,6 +56,9 @@ s/ *@LIBPNG@//
s/ *@LIBGIF@//
s/ *@LIBXPM@//
s/ *@WEBP_LIBS@//
+/^GIF_CFLAGS *=/s/@GIF_CFLAGS@//
+/^JPEG_CFLAGS *=/s/@JPEG_CFLAGS@//
+/^TIFF_CFLAGS *=/s/@TIFF_CFLAGS@//
/^HAVE_NATIVE_COMP *=/s/@HAVE_NATIVE_COMP@/no/
/^HAVE_PDUMPER *=/s/@HAVE_PDUMPER@/no/
/^HAVE_BE_APP *=/s/@HAVE_BE_APP@/no/
@@ -200,6 +203,14 @@ s/ *@WEBP_LIBS@//
/^PAXCTL_dumped *=/s/=.*$/=/
/^PAXCTL_notdumped *=/s/=.*$/=/
/^DUMPING *=/s/@DUMPING@/unexec/
+/^ANDROID_OBJ *=/s/@ANDROID_OBJ@//
+/^ANDROID_LIBS *=/s/@ANDROID_LIBS@//
+/^ANDROID_LDFLAGS *=/s/@ANDROID_LDFLAGS@//
+/^ANDROID_BUILD_CFLAGS *=/s/@ANDROID_CFLAGS@//
+/^LIBGMP_CFLAGS *=/s/@LIBGMP_CFLAGS@//
+/^SQLITE3_CFLAGS *=/s/@SQLITE3_CFLAGS@//
+/^LIBSELINUX_CFLAGS *=/s/@LIBSELINUX_CFLAGS@//
+/^XCONFIGURE *=/s/@XCONFIGURE@//
/^[ \t]*MAKE_PDUMPER_FINGERPRINT = *$/c\
MAKE_PDUMPER_FINGERPRINT =
/^lisp\.mk:/,/^$/c\
@@ -283,3 +294,4 @@ s| -I\. -I\$(srcdir)| -I.|
/^ *test "X/d
/\$(CC) -o \$@.tmp/s/\$@.tmp/\$@/
/mv \$@.tmp \$@/d
+/^top_builddir =*/s/@top_builddir@/../
diff --git a/msdos/sed3v2.inp b/msdos/sed3v2.inp
index 9688a27b066..0699fb68b02 100644
--- a/msdos/sed3v2.inp
+++ b/msdos/sed3v2.inp
@@ -57,3 +57,4 @@
/^GETOPT_H *=/s!@GETOPT_H@!getopt.h!
/^GETOPTOBJS *=/s!@GETOPTOBJS@!getopt.o getopt1.o!
/^INSTALLABLES/s/emacsclient[^ ]* *//
+/^XCONFIGURE *=/s/@XCONFIGURE@//
diff --git a/msdos/sedlibcf.inp b/msdos/sedlibcf.inp
index 931ceb8f044..8966e799a38 100644
--- a/msdos/sedlibcf.inp
+++ b/msdos/sedlibcf.inp
@@ -20,3 +20,4 @@
# ----------------------------------------------------------------------
s/c++defs/cxxdefs/g
s/\([a-zA-Z0-9_]*\)\.in\.h/\1.in-h/g
+/^XCONFIGURE *=/s/@XCONFIGURE@//
diff --git a/msdos/sedlibmk.inp b/msdos/sedlibmk.inp
index c3f410bd74d..cca2b46b018 100644
--- a/msdos/sedlibmk.inp
+++ b/msdos/sedlibmk.inp
@@ -156,6 +156,7 @@ s/@PACKAGE@/emacs/
/^HYBRID_MALLOC *=/s/@HYBRID_MALLOC@//
/^WARN_CFLAGS *=/s/@WARN_CFLAGS@//
/^WERROR_CFLAGS *=/s/@WERROR_CFLAGS@//
+/^ANDROID_BUILD_CFLAGS *=/s/@ANDROID_BUILD_CFLAGS@//
/^DEFS *=/s/@[^@\n]*@//
/^DEPDIR *=/s/@[^@\n]*@/deps/
/^ECHO_N *=/s/@[^@\n]*@/-n/
@@ -299,8 +300,10 @@ s/@PACKAGE@/emacs/
/^NEXT_DIRENT_H *=/s/@[^@\n]*@/<dirent.h>/
/^NEXT_ERRNO_H *=/s/@[^@\n]*@//
/^NEXT_FCNTL_H *=/s/@[^@\n]*@/<fcntl.h>/
+/^NEXT_FLOAT_H *=/s/@[^@\n]*@//
/^NEXT_GETOPT_H *=/s/@[^@\n]*@/<getopt.h>/
/^NEXT_LIMITS_H *=/s/@[^@\n]*@/<limits.h>/
+/^NEXT_MATH_H *=/s/@[^@\n]*@//
/^NEXT_SIGNAL_H *=/s/@[^@\n]*@/<signal.h>/
/^NEXT_STDDEF_H *=/s/@[^@\n]*@/<stddef.h>/
/^NEXT_STDIO_H *=/s/@[^@\n]*@/<stdio.h>/
@@ -309,9 +312,11 @@ s/@PACKAGE@/emacs/
/^NEXT_STRING_H *=/s/@[^@\n]*@/<string.h>/
/^NEXT_SYS_SELECT_H *=/s/@[^@\n]*@//
/^NEXT_SYS_STAT_H *=/s!@[^@\n]*@!<sys/stat.h>!
+/^NEXT_SYS_RANDOM_H *=/s/@[^@\n]*@//
/^NEXT_SYS_TIME_H *=/s/@[^@\n]*@//
/^NEXT_SYS_TYPES_H *=/s!@[^@\n]*@!<sys/types.h>!
/^NEXT_TIME_H *=/s/@[^@\n]*@/<time.h>/
+/^NEXT_INTTYPES_H *=/s/@[^@\n]*@//
/^NEXT_UNISTD_H *=/s/@[^@\n]*@/<unistd.h>/
/^OBJEXT *=/s/@[^@\n]*@/o/
/^PRAGMA_COLUMNS *=/s/@[^@\n]*@//
@@ -331,6 +336,7 @@ s/@PACKAGE@/emacs/
/^DIRENT_H *=/s/@[^@\n]*@//
/^ERRNO_H *=/s/@[^@\n]*@//
/^EXECINFO_H *=/s/@[^@\n]*@/execinfo.h/
+/^FLOAT_H *=/s/@[^@\n]*@//
/^GETOPT_CDEFS_H *=/s/@[^@\n]*@/getopt-cdefs.h/
/^GMP_H *=/s/@[^@\n]*@/gmp.h/
/^LIMITS_H *=/s/@[^@\n]*@/limits.h/
@@ -427,7 +433,7 @@ s/= @GL_GENERATE_STDDEF_H_CONDITION@/= 1/
s/= @GL_GENERATE_STDINT_H_CONDITION@/= 1/
s/= @GL_GENERATE_LIMITS_H_CONDITION@/= 1/
s/= @GL_GENERATE_ERRNO_H_CONDITION@/= /
-s/= @GL_GENERATE_LIMITS_H_CONDITION@/= /
+s/= @GL_GENERATE_FLOAT_H_CONDITION@/= /
s/= @GL_GENERATE_GETOPT_CDEFS_H_CONDITION@/= 1/
s/= @GL_GENERATE_GETOPT_H_CONDITION@/= 1/
s/= @GL_GENERATE_GMP_H_CONDITION@/= 1/
@@ -436,6 +442,8 @@ s/= @GL_GENERATE_MINI_GMP_H_CONDITION@/= 1/
s/= @GL_GENERATE_STDCKDINT_H_CONDITION@/= 1/
s/= @GL_COND_OBJ_STDIO_READ_CONDITION@/= /
s/= @GL_COND_OBJ_STDIO_WRITE_CONDITION@/= /
+s/= @GL_COND_OBJ_STPNCPY_CONDITION@/= /
+s/= @GL_COND_OBJ_.*@/= 1/
s/\$\(MKDIR_P\) malloc//
#
# Determine which modules to build and which to omit
@@ -453,8 +461,11 @@ OMIT_GNULIB_MODULE_euidaccess = true\
OMIT_GNULIB_MODULE_faccessat = true\
OMIT_GNULIB_MODULE_fcntl = true\
OMIT_GNULIB_MODULE_fdopendir = true\
+OMIT_GNULIB_MODULE_float = true\
OMIT_GNULIB_MODULE_fstatat = true\
OMIT_GNULIB_MODULE_fsync = true\
+OMIT_GNULIB_MODULE_getline = true\
+OMIT_GNULIB_MODULE_getdelim = true\
OMIT_GNULIB_MODULE_getdtablesize = true\
OMIT_GNULIB_MODULE_getgroups = true\
OMIT_GNULIB_MODULE_gettimeofday = true\
@@ -462,20 +473,25 @@ OMIT_GNULIB_MODULE_group-member = true\
OMIT_GNULIB_MODULE_inttypes-incomplete = true\
OMIT_GNULIB_MODULE_localtime-buffer = true\
OMIT_GNULIB_MODULE_lstat = true\
+OMIT_GNULIB_MODULE_math = true\
OMIT_GNULIB_MODULE_nanosleep = true\
OMIT_GNULIB_MODULE_open = true\
OMIT_GNULIB_MODULE_pipe2 = true\
+OMIT_GNULIB_MODULE_printf-posix = true\
+OMIT_GNULIB_MODULE_printf-frexpl = true\
OMIT_GNULIB_MODULE_pselect = true\
OMIT_GNULIB_MODULE_putenv = true\
OMIT_GNULIB_MODULE_qcopy-acl = true\
OMIT_GNULIB_MODULE_readlink = true\
OMIT_GNULIB_MODULE_readlinkat = true\
+OMIT_GNULIB_MODULE_stpcpy = true\
OMIT_GNULIB_MODULE_strtoimax = true\
OMIT_GNULIB_MODULE_strtoll = true\
OMIT_GNULIB_MODULE_symlink = true\
OMIT_GNULIB_MODULE_sys_select = true\
OMIT_GNULIB_MODULE_sys_time = true\
-OMIT_GNULIB_MODULE_crypto\/md5 = true
+OMIT_GNULIB_MODULE_crypto\/md5 = true\
+OMIT_GNULIB_MODULE_vprintf-posix = true
/^arg-nonnull\.h:/,/^[ ][ ]*mv /c\
arg-nonnull.h: $(top_srcdir)/build-aux/snippet/arg-nonnull.h\
sed -n -e '/GL_ARG_NONNULL/,$$p' < $(top_srcdir)/build-aux/snippet/arg-nonnull.h > $@
diff --git a/nt/gnulib-cfg.mk b/nt/gnulib-cfg.mk
index eca3778f203..d7241976f83 100644
--- a/nt/gnulib-cfg.mk
+++ b/nt/gnulib-cfg.mk
@@ -44,34 +44,55 @@
OMIT_GNULIB_MODULE_acl-permissions = true
OMIT_GNULIB_MODULE_allocator = true
OMIT_GNULIB_MODULE_at-internal = true
+OMIT_GNULIB_MODULE_canonicalize-lgpl = true
OMIT_GNULIB_MODULE_careadlinkat = true
OMIT_GNULIB_MODULE_dirent = true
OMIT_GNULIB_MODULE_dirfd = true
+OMIT_GNULIB_MODULE_fchmodat = true
OMIT_GNULIB_MODULE_fcntl = true
OMIT_GNULIB_MODULE_fcntl-h = true
+OMIT_GNULIB_MODULE_file-has-acl = true
+OMIT_GNULIB_MODULE_float = true
+OMIT_GNULIB_MODULE_fpucw = true
OMIT_GNULIB_MODULE_free-posix = true
+OMIT_GNULIB_MODULE_frexp-nolibm = true
+OMIT_GNULIB_MODULE_frexpl-nolibm = true
+OMIT_GNULIB_MODULE_fseterr = true
OMIT_GNULIB_MODULE_fsusage = true
+OMIT_GNULIB_MODULE_futimens = true
+OMIT_GNULIB_MODULE_getdelim = true
+OMIT_GNULIB_MODULE_getline = true
OMIT_GNULIB_MODULE_inttypes-incomplete = true
+OMIT_GNULIB_MODULE_isnand-nolibm = true
+OMIT_GNULIB_MODULE_isnanf-nolibm = true
+OMIT_GNULIB_MODULE_isnanl-nolibm = true
+OMIT_GNULIB_MODULE_lchmod = true
OMIT_GNULIB_MODULE_malloc-posix = true
+OMIT_GNULIB_MODULE_math = true
+OMIT_GNULIB_MODULE_nanosleep = true
+OMIT_GNULIB_MODULE_nproc = true
OMIT_GNULIB_MODULE_open = true
OMIT_GNULIB_MODULE_pipe2 = true
+OMIT_GNULIB_MODULE_printf-frexp = true
+OMIT_GNULIB_MODULE_printf-frexpl = true
+OMIT_GNULIB_MODULE_printf-posix = true
OMIT_GNULIB_MODULE_realloc-gnu = true
OMIT_GNULIB_MODULE_realloc-posix = true
OMIT_GNULIB_MODULE_secure_getenv = true
OMIT_GNULIB_MODULE_signal-h = true
+OMIT_GNULIB_MODULE_signbit = true
+OMIT_GNULIB_MODULE_size_max = true
OMIT_GNULIB_MODULE_stdio = true
OMIT_GNULIB_MODULE_stdlib = true
+OMIT_GNULIB_MODULE_stpncpy = true
OMIT_GNULIB_MODULE_sys_select = true
OMIT_GNULIB_MODULE_sys_stat = true
OMIT_GNULIB_MODULE_sys_time = true
OMIT_GNULIB_MODULE_sys_types = true
OMIT_GNULIB_MODULE_unistd = true
-OMIT_GNULIB_MODULE_canonicalize-lgpl = true
OMIT_GNULIB_MODULE_utimens = true
-OMIT_GNULIB_MODULE_fchmodat = true
-OMIT_GNULIB_MODULE_lchmod = true
-OMIT_GNULIB_MODULE_futimens = true
OMIT_GNULIB_MODULE_utimensat = true
-OMIT_GNULIB_MODULE_file-has-acl = true
-OMIT_GNULIB_MODULE_nproc = true
-OMIT_GNULIB_MODULE_nanosleep = true
+OMIT_GNULIB_MODULE_vasnprintf = true
+OMIT_GNULIB_MODULE_vasprintf = true
+OMIT_GNULIB_MODULE_vfprintf-posix = true
+OMIT_GNULIB_MODULE_xsize = true
diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site
index 425eaace30d..0e8c3aca3bb 100644
--- a/nt/mingw-cfg.site
+++ b/nt/mingw-cfg.site
@@ -173,3 +173,18 @@ gl_cv_func_nanosleep=yes
# Suppress configure-time diagnostic from unnecessary libxattr check,
# as xattr will not be supported here.
enable_xattr=no
+# Don't build gnulib printf either.
+gl_cv_func_printf_sizes_c99=yes
+gl_cv_func_printf_long_double=yes
+gl_cv_func_printf_infinite_long_double=yes
+gl_cv_func_printf_directive_a=yes
+gl_cv_func_printf_directive_f=yes
+gl_cv_func_printf_directive_n=yes
+gl_cv_func_printf_directive_ls=yes
+gl_cv_func_printf_positions=yes
+gl_cv_func_printf_flag_grouping=yes
+gl_cv_func_printf_flag_leftadjust=yes
+gl_cv_func_printf_flag_zero=yes
+gl_cv_func_printf_precision=yes
+gl_cv_func_printf_enomem=yes
+ac_cv_func_vasprintf=yes
diff --git a/src/Makefile.in b/src/Makefile.in
index e08e5eead28..9ac7983943e 100644
--- a/src/Makefile.in
+++ b/src/Makefile.in
@@ -33,6 +33,16 @@ top_builddir = @top_builddir@
# MinGW CPPFLAGS may use this.
abs_top_srcdir=@abs_top_srcdir@
VPATH = $(srcdir)
+
+# This is not empty if this is a Makefile that will be copied to
+# cross/src.
+XCONFIGURE = @XCONFIGURE@
+
+ifneq ($(XCONFIGURE),)
+vpath %.c := $(srcdir)
+vpath %.h := $(srcdir)
+endif
+
CC = @CC@
CXX = @CXX@
CFLAGS = @CFLAGS@
@@ -48,6 +58,7 @@ LIBOBJS = @LIBOBJS@
lispsource = $(top_srcdir)/lisp
lib = ../lib
+hostlib = $(top_builddir)/lib
libsrc = ../lib-src
etc = ../etc
oldXMenudir = ../oldXMenu
@@ -127,6 +138,10 @@ LIB_PTHREAD=@LIB_PTHREAD@
LIBIMAGE=@LIBTIFF@ @LIBJPEG@ @LIBPNG@ @LIBGIF@ @LIBXPM@ @WEBP_LIBS@
+GIF_CFLAGS=@GIF_CFLAGS@
+JPEG_CFLAGS=@JPEG_CFLAGS@
+TIFF_CFLAGS=@TIFF_CFLAGS@
+
XCB_LIBS=@XCB_LIBS@
XFT_LIBS=@XFT_LIBS@
XRENDER_LIBS=@XRENDER_LIBS@
@@ -241,6 +256,7 @@ LIBXML2_LIBS = @LIBXML2_LIBS@
LIBXML2_CFLAGS = @LIBXML2_CFLAGS@
SQLITE3_LIBS = @SQLITE3_LIBS@
+SQLITE3_CFLAGS = @SQLITE3_CFLAGS@
GETADDRINFO_A_LIBS = @GETADDRINFO_A_LIBS@
@@ -327,12 +343,13 @@ W32_RES_LINK=@W32_RES_LINK@
## if HAVE_HARFBUZZ, hbfont.o is added regardless of the rest
FONT_OBJ=@FONT_OBJ@
-## Empty for MinGW, cm.o for the rest.
+## Empty for MinGW and Android, cm.o for the rest.
CM_OBJ=@CM_OBJ@
LIBGPM = @LIBGPM@
LIBSELINUX_LIBS = @LIBSELINUX_LIBS@
+LIBSELINUX_CFLAGS = @LIBSELINUX_CFLAGS@
LIBGNUTLS_LIBS = @LIBGNUTLS_LIBS@
LIBGNUTLS_CFLAGS = @LIBGNUTLS_CFLAGS@
@@ -371,6 +388,13 @@ HAIKU_CXX_OBJ = @HAIKU_CXX_OBJ@
HAIKU_LIBS = @HAIKU_LIBS@
HAIKU_CFLAGS = @HAIKU_CFLAGS@
+ANDROID_OBJ = @ANDROID_OBJ@
+ANDROID_LIBS = @ANDROID_LIBS@
+ANDROID_LDFLAGS = @ANDROID_LDFLAGS@
+ANDROID_BUILD_CFLAGS = @ANDROID_BUILD_CFLAGS@
+
+LIBGMP_CFLAGS = @LIBGMP_CFLAGS@
+
DUMPING=@DUMPING@
CHECK_STRUCTS = @CHECK_STRUCTS@
HAVE_PDUMPER = @HAVE_PDUMPER@
@@ -412,7 +436,9 @@ EMACS_CFLAGS=-Demacs $(MYCPPFLAGS) -I. -I$(srcdir) \
$(HARFBUZZ_CFLAGS) $(LIBOTF_CFLAGS) $(M17N_FLT_CFLAGS) $(DEPFLAGS) \
$(LIBSYSTEMD_CFLAGS) $(JSON_CFLAGS) $(XSYNC_CFLAGS) $(TREE_SITTER_CFLAGS) \
$(LIBGNUTLS_CFLAGS) $(NOTIFY_CFLAGS) $(CAIRO_CFLAGS) \
- $(WERROR_CFLAGS) $(HAIKU_CFLAGS) $(XCOMPOSITE_CFLAGS) $(XSHAPE_CFLAGS)
+ $(WERROR_CFLAGS) $(HAIKU_CFLAGS) $(XCOMPOSITE_CFLAGS) $(XSHAPE_CFLAGS) \
+ $(ANDROID_BUILD_CFLAGS) $(GIF_CFLAGS) $(JPEG_CFLAGS) $(SQLITE3_CFLAGS) \
+ $(LIBGMP_CFLAGS) $(TIFF_CFLAGS) $(LIBSELINUX_CFLAGS)
ALL_CFLAGS = $(EMACS_CFLAGS) $(WARN_CFLAGS) $(CFLAGS)
ALL_OBJC_CFLAGS = $(EMACS_CFLAGS) \
$(filter-out $(NON_OBJC_CFLAGS),$(WARN_CFLAGS)) $(CFLAGS) \
@@ -450,7 +476,7 @@ base_obj = dispnew.o frame.o scroll.o xdisp.o menu.o $(XMENU_OBJ) window.o \
$(if $(HYBRID_MALLOC),sheap.o) \
$(MSDOS_OBJ) $(MSDOS_X_OBJ) $(NS_OBJ) $(CYGWIN_OBJ) $(FONT_OBJ) \
$(W32_OBJ) $(WINDOW_SYSTEM_OBJ) $(XGSELOBJ) $(JSON_OBJ) \
- $(HAIKU_OBJ) $(PGTK_OBJ)
+ $(HAIKU_OBJ) $(PGTK_OBJ) $(ANDROID_OBJ)
doc_obj = $(base_obj) $(NS_OBJC_OBJ)
obj = $(doc_obj) $(HAIKU_CXX_OBJ)
@@ -467,7 +493,8 @@ SOME_MACHINE_OBJECTS = dosfns.o msdos.o \
w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \
xsettings.o xgselect.o termcap.o hbfont.o \
- haikuterm.o haikufns.o haikumenu.o haikufont.o
+ haikuterm.o haikufns.o haikumenu.o haikufont.o androidterm.o androidfns.o \
+ androidfont.o
## gmalloc.o if !SYSTEM_MALLOC && !DOUG_LEA_MALLOC, else empty.
GMALLOC_OBJ=@GMALLOC_OBJ@
@@ -570,7 +597,8 @@ LIBES = $(LIBS) $(W32_LIBS) $(LIBS_GNUSTEP) $(PGTK_LIBS) $(LIBX_BASE) $(LIBIMAGE
$(LIBGNUTLS_LIBS) $(LIB_PTHREAD) $(GETADDRINFO_A_LIBS) $(LCMS2_LIBS) \
$(NOTIFY_LIBS) $(LIB_MATH) $(LIBZ) $(LIBMODULES) $(LIBSYSTEMD_LIBS) \
$(JSON_LIBS) $(LIBGMP) $(LIBGCCJIT_LIBS) $(XINPUT_LIBS) $(HAIKU_LIBS) \
- $(TREE_SITTER_LIBS) $(SQLITE3_LIBS) $(XCOMPOSITE_LIBS) $(XSHAPE_LIBS)
+ $(TREE_SITTER_LIBS) $(SQLITE3_LIBS) $(XCOMPOSITE_LIBS) $(XSHAPE_LIBS) \
+ $(ANDROID_LIBS)
## FORCE it so that admin/unidata can decide whether this file is
## up-to-date. Although since charprop depends on bootstrap-emacs,
@@ -659,7 +687,7 @@ $(etc)/DOC: $(libsrc)/make-docfile$(EXEEXT) $(doc_obj)
$(SOME_MACHINE_OBJECTS) $(doc_obj) > $(etc)/DOC
$(libsrc)/make-docfile$(EXEEXT) $(libsrc)/make-fingerprint$(EXEEXT): \
- $(lib)/libgnu.a
+ $(hostlib)/libgnu.a
$(MAKE) -C $(dir $@) $(notdir $@)
buildobj.h: Makefile
@@ -720,6 +748,37 @@ ifeq ($(DUMPING),unexec)
endif
endif
+ifeq ($(XCONFIGURE),android)
+## The Android package internally links to and communicates with a
+## shared library named `libemacs.so' at startup. This is built
+## almost the same way temacs is. But it is position independent. It
+## is not dumped here. Instead, it dumps itself the first time it
+## starts on the user's device.
+
+# Include ndk-build.mk in order to build Emacs dependencies.
+old_top_builddir := $(top_builddir)
+top_builddir := $(old_top_builddir)/..
+include $(old_top_builddir)/ndk-build/ndk-build.mk
+top_builddir := $(old_top_builddir)
+
+libemacs.so: $(ALLOBJS) $(LIBEGNU_ARCHIVE) $(EMACSRES) \
+ $(MAKE_PDUMPER_FINGERPRINT) $(NDK_BUILD_SHARED) \
+ $(NDK_BUILD_STATIC)
+ $(AM_V_CCLD)$(CC) -o $@ $(ALL_CFLAGS) $(TEMACS_LDFLAGS) \
+ $(ANDROID_LDFLAGS) $(LDFLAGS) -shared $(ALLOBJS) \
+ $(LIBEGNU_ARCHIVE) $(LIBES)
+ $(AM_V_at)$(MAKE_PDUMPER_FINGERPRINT) $@
+
+# There is also a binary named `android-emacs' which simply calls
+# emacs.so. It need not link against libemacs because app_process
+# will do that instead.
+
+android-emacs: android-emacs.c
+ $(AM_V_CCLD)$(CC) $(lastword $^) -o $@ \
+ $(ALL_CFLAGS) $(LDFLAGS) \
+ $(LIBEGNU_ARCHIVE)
+endif
+
## The following oldxmenu-related rules are only (possibly) used if
## HAVE_X11 && !USE_GTK, but there is no harm in always defining them.
$(lwlibdir)/liblw.a: $(config_h) globals.h lisp.h FORCE
@@ -748,6 +807,7 @@ ns-app: emacs$(EXEEXT) $(pdmp)
.PHONY: versionclean
mostlyclean:
+ rm -f android-emacs libemacs.so
rm -f temacs$(EXEEXT) core ./*.core \#* ./*.o
rm -f dmpstruct.h
rm -f emacs.pdmp
diff --git a/src/alloc.c b/src/alloc.c
index 29393deeff4..d87cc44b59b 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -50,6 +50,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
#ifdef HAVE_TREE_SITTER
#include "treesit.h"
#endif
@@ -3346,6 +3350,15 @@ cleanup_vector (struct Lisp_Vector *vector)
drv->close_font (font);
}
}
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* The Android font driver needs the ability to associate extra
+ information with font entities. */
+ if (((vector->header.size & PSEUDOVECTOR_SIZE_MASK)
+ == FONT_ENTITY_MAX)
+ && PSEUDOVEC_STRUCT (vector, font_entity)->is_android)
+ android_finalize_font_entity (PSEUDOVEC_STRUCT (vector, font_entity));
+#endif
}
else if (PSEUDOVECTOR_TYPEP (&vector->header, PVEC_THREAD))
finalize_one_thread (PSEUDOVEC_STRUCT (vector, thread_state));
@@ -5645,6 +5658,22 @@ find_string_data_in_pure (const char *data, ptrdiff_t nbytes)
if (pure_bytes_used_non_lisp <= nbytes)
return NULL;
+ /* The Android GCC generates code like:
+
+ 0xa539e755 <+52>: lea 0x430(%esp),%esi
+=> 0xa539e75c <+59>: movdqa %xmm0,0x0(%ebp)
+ 0xa539e761 <+64>: add $0x10,%ebp
+
+ but data is not aligned appropriately, so a GP fault results. */
+
+#if defined __i386__ \
+ && defined HAVE_ANDROID \
+ && !defined ANDROID_STUBIFY \
+ && !defined (__clang__)
+ if ((intptr_t) data & 15)
+ return NULL;
+#endif
+
/* Set up the Boyer-Moore table. */
skip = nbytes + 1;
for (i = 0; i < 256; i++)
@@ -6155,16 +6184,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));
@@ -6463,6 +6520,13 @@ garbage_collect (void)
mark_xselect ();
#endif
+#ifdef HAVE_ANDROID
+ mark_androidterm ();
+#ifndef ANDROID_STUBIFY
+ mark_sfntfont ();
+#endif
+#endif
+
#ifdef HAVE_NS
mark_nsterm ();
#endif
@@ -6871,6 +6935,11 @@ static void
mark_frame (struct Lisp_Vector *ptr)
{
struct frame *f = (struct frame *) ptr;
+#ifdef HAVE_TEXT_CONVERSION
+ struct text_conversion_action *tem;
+#endif
+
+
mark_vectorlike (&ptr->header);
mark_face_cache (f->face_cache);
#ifdef HAVE_WINDOW_SYSTEM
@@ -6882,6 +6951,15 @@ mark_frame (struct Lisp_Vector *ptr)
mark_vectorlike (&font->header);
}
#endif
+
+#ifdef HAVE_TEXT_CONVERSION
+ mark_object (f->conversion.compose_region_start);
+ mark_object (f->conversion.compose_region_end);
+ mark_object (f->conversion.compose_region_overlay);
+
+ for (tem = f->conversion.actions; tem; tem = tem->next)
+ mark_object (tem->data);
+#endif
}
static void
diff --git a/src/android-asset.h b/src/android-asset.h
new file mode 100644
index 00000000000..4fb309f1645
--- /dev/null
+++ b/src/android-asset.h
@@ -0,0 +1,422 @@
+/* Android initialization for GNU Emacs.
+
+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 <android/log.h>
+
+/* This file contains an emulation of the Android asset manager API
+ used on builds for Android 2.2. It is included by android.c
+ whenever appropriate.
+
+ The replacements in this file are not thread safe and must only be
+ called from the creating thread. */
+
+struct android_asset_manager
+{
+ /* JNI environment. */
+ JNIEnv *env;
+
+ /* Asset manager class and functions. */
+ jclass class;
+ jmethodID open_fd;
+
+ /* Asset file descriptor class and functions. */
+ jclass fd_class;
+ jmethodID get_length;
+ jmethodID create_input_stream;
+ jmethodID close;
+
+ /* Input stream class and functions. */
+ jclass input_stream_class;
+ jmethodID read;
+ jmethodID stream_close;
+
+ /* Associated asset manager object. */
+ jobject asset_manager;
+};
+
+typedef struct android_asset_manager AAssetManager;
+
+struct android_asset
+{
+ /* The asset manager. */
+ AAssetManager *manager;
+
+ /* The length of the asset, or -1. */
+ jlong length;
+
+ /* The asset file descriptor and input stream. */
+ jobject fd, stream;
+
+ /* The mode. */
+ int mode;
+};
+
+typedef struct android_asset AAsset;
+
+static AAssetManager *
+AAssetManager_fromJava (JNIEnv *env, jobject java_manager)
+{
+ AAssetManager *manager;
+ jclass temp;
+
+ manager = malloc (sizeof *manager);
+
+ if (!manager)
+ return NULL;
+
+ manager->env = env;
+ manager->asset_manager
+ = (*env)->NewGlobalRef (env, java_manager);
+
+ if (!manager->asset_manager)
+ {
+ free (manager);
+ return NULL;
+ }
+
+ manager->class
+ = (*env)->FindClass (env, "android/content/res/AssetManager");
+ assert (manager->class);
+
+ manager->open_fd
+ = (*env)->GetMethodID (env, manager->class, "openFd",
+ "(Ljava/lang/String;)"
+ "Landroid/content/res/AssetFileDescriptor;");
+ assert (manager->open);
+
+ manager->fd_class
+ = (*env)->FindClass (env, "android/content/res/AssetFileDescriptor");
+ assert (manager->fd_class);
+
+ manager->get_length
+ = (*env)->GetMethodID (env, manager->fd_class, "getLength",
+ "()J");
+ assert (manager->get_length);
+
+ manager->create_input_stream
+ = (*env)->GetMethodID (env, manager->fd_class,
+ "createInputStream",
+ "()Ljava/io/FileInputStream;");
+ assert (manager->create_input_stream);
+
+ manager->close
+ = (*env)->GetMethodID (env, manager->fd_class,
+ "close", "()V");
+ assert (manager->close);
+
+ manager->input_stream_class
+ = (*env)->FindClass (env, "java/io/InputStream");
+ assert (manager->input_stream_class);
+
+ manager->read
+ = (*env)->GetMethodID (env, manager->input_stream_class,
+ "read", "([B)I");
+ assert (manager->read);
+
+ manager->stream_close
+ = (*env)->GetMethodID (env, manager->input_stream_class,
+ "close", "()V");
+ assert (manager->stream_close);
+
+ /* Now convert all the class references to global ones. */
+ temp = manager->class;
+ manager->class
+ = (*env)->NewGlobalRef (env, temp);
+ assert (manager->class);
+ (*env)->DeleteLocalRef (env, temp);
+ temp = manager->fd_class;
+ manager->fd_class
+ = (*env)->NewGlobalRef (env, temp);
+ assert (manager->fd_class);
+ (*env)->DeleteLocalRef (env, temp);
+ temp = manager->input_stream_class;
+ manager->input_stream_class
+ = (*env)->NewGlobalRef (env, temp);
+ assert (manager->input_stream_class);
+ (*env)->DeleteLocalRef (env, temp);
+
+ /* Return the asset manager. */
+ return manager;
+}
+
+enum
+ {
+ AASSET_MODE_STREAMING = 0,
+ AASSET_MODE_BUFFER = 1,
+ };
+
+static AAsset *
+AAssetManager_open (AAssetManager *manager, const char *c_name,
+ int mode)
+{
+ jobject desc;
+ jstring name;
+ AAsset *asset;
+
+ /* Push a local frame. */
+ asset = NULL;
+
+ (*(manager->env))->PushLocalFrame (manager->env, 3);
+
+ if ((*(manager->env))->ExceptionCheck (manager->env))
+ goto fail;
+
+ /* Encoding issues can be ignored for now as there are only ASCII
+ file names in Emacs. */
+ name = (*(manager->env))->NewStringUTF (manager->env, c_name);
+
+ if (!name)
+ goto fail;
+
+ /* Now try to open an ``AssetFileDescriptor''. */
+ desc = (*(manager->env))->CallObjectMethod (manager->env,
+ manager->asset_manager,
+ manager->open_fd,
+ name);
+
+ if (!desc)
+ goto fail;
+
+ /* Allocate the asset. */
+ asset = calloc (1, sizeof *asset);
+
+ if (!asset)
+ {
+ (*(manager->env))->CallVoidMethod (manager->env,
+ desc,
+ manager->close);
+ goto fail;
+ }
+
+ /* Pop the local frame and return desc. */
+ desc = (*(manager->env))->NewGlobalRef (manager->env, desc);
+
+ if (!desc)
+ goto fail;
+
+ (*(manager->env))->PopLocalFrame (manager->env, NULL);
+
+ asset->manager = manager;
+ asset->length = -1;
+ asset->fd = desc;
+ asset->mode = mode;
+
+ return asset;
+
+ fail:
+ (*(manager->env))->ExceptionClear (manager->env);
+ (*(manager->env))->PopLocalFrame (manager->env, NULL);
+ free (asset);
+
+ return NULL;
+}
+
+static AAsset *
+AAsset_close (AAsset *asset)
+{
+ JNIEnv *env;
+
+ env = asset->manager->env;
+
+ (*env)->CallVoidMethod (asset->manager->env,
+ asset->fd,
+ asset->manager->close);
+ (*env)->DeleteGlobalRef (asset->manager->env,
+ asset->fd);
+
+ if (asset->stream)
+ {
+ (*env)->CallVoidMethod (asset->manager->env,
+ asset->stream,
+ asset->manager->stream_close);
+ (*env)->DeleteGlobalRef (asset->manager->env,
+ asset->stream);
+ }
+
+ free (asset);
+}
+
+/* Create an input stream associated with the given ASSET. Set
+ ASSET->stream to its global reference.
+
+ Value is 1 upon failure, else 0. ASSET must not already have an
+ input stream. */
+
+static int
+android_asset_create_stream (AAsset *asset)
+{
+ jobject stream;
+ JNIEnv *env;
+
+ env = asset->manager->env;
+ stream
+ = (*env)->CallObjectMethod (env, asset->fd,
+ asset->manager->create_input_stream);
+
+ if (!stream)
+ {
+ (*env)->ExceptionClear (env);
+ return 1;
+ }
+
+ asset->stream
+ = (*env)->NewGlobalRef (env, stream);
+
+ if (!asset->stream)
+ {
+ (*env)->ExceptionClear (env);
+ (*env)->DeleteLocalRef (env, stream);
+ return 1;
+ }
+
+ (*env)->DeleteLocalRef (env, stream);
+ return 0;
+}
+
+/* Read NBYTES from the specified asset into the given BUFFER;
+
+ Internally, allocate a Java byte array containing 4096 elements and
+ copy the data to and from that array.
+
+ Value is the number of bytes actually read, 0 at EOF, or -1 upon
+ failure, in which case errno is set accordingly. If NBYTES is
+ zero, behavior is undefined. */
+
+static int
+android_asset_read_internal (AAsset *asset, int nbytes, char *buffer)
+{
+ jbyteArray stash;
+ JNIEnv *env;
+ jint bytes_read, total;
+
+ /* Allocate a suitable amount of storage. Either nbytes or 4096,
+ whichever is larger. */
+ env = asset->manager->env;
+ stash = (*env)->NewByteArray (env, MIN (nbytes, 4096));
+
+ if (!stash)
+ {
+ (*env)->ExceptionClear (env);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Try to create an input stream. */
+
+ if (!asset->stream
+ && android_asset_create_stream (asset))
+ {
+ (*env)->DeleteLocalRef (env, stash);
+ errno = ENOMEM;
+ return -1;
+ }
+
+ /* Start reading. */
+
+ total = 0;
+
+ while (nbytes)
+ {
+ bytes_read = (*env)->CallIntMethod (env, asset->stream,
+ asset->manager->read,
+ stash);
+
+ /* Detect error conditions. */
+
+ if ((*env)->ExceptionCheck (env))
+ goto out;
+
+ /* Detect EOF. */
+
+ if (bytes_read == -1)
+ goto out;
+
+ /* Finally write out the amount that was read. */
+ bytes_read = MIN (bytes_read, nbytes);
+ (*env)->GetByteArrayRegion (env, stash, 0, bytes_read, buffer);
+
+ buffer += bytes_read;
+ total += bytes_read;
+ nbytes -= bytes_read;
+ }
+
+ /* Make sure the value of nbytes still makes sense. */
+ assert (nbytes >= 0);
+
+ out:
+ (*env)->ExceptionClear (env);
+ (*env)->DeleteLocalRef (env, stash);
+ return total;
+}
+
+static long
+AAsset_getLength (AAsset *asset)
+{
+ JNIEnv *env;
+
+ if (asset->length != -1)
+ return asset->length;
+
+ env = asset->manager->env;
+ asset->length
+ = (*env)->CallLongMethod (env, asset->fd,
+ asset->manager->get_length);
+ return asset->length;
+}
+
+static char *
+AAsset_getBuffer (AAsset *asset)
+{
+ long length;
+ char *buffer;
+
+ length = AAsset_getLength (asset);
+
+ if (!length)
+ return NULL;
+
+ buffer = malloc (length);
+
+ if (!buffer)
+ return NULL;
+
+ if (android_asset_read_internal (asset, length, buffer)
+ != length)
+ {
+ xfree (buffer);
+ return NULL;
+ }
+
+ return buffer;
+}
+
+static size_t
+AAsset_read (AAsset *asset, void *buffer, size_t size)
+{
+ return android_asset_read_internal (asset, MIN (size, INT_MAX),
+ buffer);
+}
+
+static off_t
+AAsset_seek (AAsset *asset, off_t offset, int whence)
+{
+ /* Java InputStreams don't support seeking at all. */
+ errno = ESPIPE;
+ return -1;
+}
diff --git a/src/android-emacs.c b/src/android-emacs.c
new file mode 100644
index 00000000000..e64caf9a9d4
--- /dev/null
+++ b/src/android-emacs.c
@@ -0,0 +1,169 @@
+/* Android initialization for GNU Emacs.
+
+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 <alloca.h>
+#include <string.h>
+#include <unistd.h>
+
+/* android-emacs is a wrapper around /system/bin/app_process(64).
+ It invokes app_process(64) with the right class path and then
+ starts org.gnu.emacs.EmacsNoninteractive.
+
+ The main function in that class tries to load an activity thread
+ and obtain a context and asset manager before calling
+ android_emacs_init, which is required for Emacs to find required
+ preloaded Lisp. */
+
+int
+main (int argc, char **argv)
+{
+ char **args;
+ int i;
+ char *bootclasspath, *emacs_class_path;
+
+ /* Allocate enough to hold the arguments to app_process. */
+ args = alloca ((10 + argc) * sizeof *args);
+
+ /* Clear args. */
+ memset (args, 0, (10 + argc) * sizeof *args);
+
+ /* First, figure out what program to start. */
+#if defined __x86_64__ || defined __aarch64__
+ args[0] = (char *) "/system/bin/app_process64";
+#else
+ args[0] = (char *) "/system/bin/app_process";
+#endif
+
+ /* Machines with ART require the boot classpath to be manually
+ specified. Machines with Dalvik however refuse to do so, as they
+ open the jars inside the BOOTCLASSPATH environment variable at
+ startup, resulting in the following crash:
+
+ W/dalvikvm( 1608): Refusing to reopen boot DEX
+ '/system/framework/core.jar'
+ W/dalvikvm( 1608): Refusing to reopen boot DEX
+ '/system/framework/bouncycastle.jar'
+ E/dalvikvm( 1608): Too many exceptions during init (failed on
+ 'Ljava/io/IOException;' 'Re-opening BOOTCLASSPATH DEX files is
+ not allowed')
+ E/dalvikvm( 1608): VM aborting */
+
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
+ if (android_get_device_api_level () < 21)
+ {
+ bootclasspath = NULL;
+ goto skip_setup;
+ }
+#else
+ if (__ANDROID_API__ < 21)
+ {
+ bootclasspath = NULL;
+ goto skip_setup;
+ }
+#endif
+
+ /* Next, obtain the boot class path. */
+ bootclasspath = getenv ("BOOTCLASSPATH");
+
+ if (!bootclasspath)
+ {
+ fprintf (stderr, "The BOOTCLASSPATH environment variable"
+ " is not set. As a result, Emacs does not know"
+ " how to start app_process.\n"
+ "This is likely a change in the Android platform."
+ " Please report this to bug-gnu-emacs@gnu.org.\n");
+ return 1;
+ }
+
+ skip_setup:
+
+ /* And the Emacs class path. */
+ emacs_class_path = getenv ("EMACS_CLASS_PATH");
+
+ if (!emacs_class_path)
+ {
+ fprintf (stderr, "EMACS_CLASS_PATH not set."
+ " Please make sure Emacs is being started"
+ " from within a running copy of Emacs.\n");
+ return 1;
+ }
+
+ if (bootclasspath)
+ {
+ if (asprintf (&bootclasspath, "-Djava.class.path=%s:%s",
+ bootclasspath, emacs_class_path) < 0)
+ {
+ perror ("asprintf");
+ return 1;
+ }
+ }
+ else
+ {
+ if (asprintf (&bootclasspath, "-Djava.class.path=%s",
+ emacs_class_path) < 0)
+ {
+ perror ("asprintf");
+ return 1;
+ }
+ }
+
+ args[1] = bootclasspath;
+ args[2] = (char *) "/system/bin";
+
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
+ /* I don't know exactly when --nice-name was introduced; this is
+ just a guess. */
+ if (android_get_device_api_level () >= 26)
+ {
+ args[3] = (char *) "--nice-name=emacs";
+ args[4] = (char *) "org.gnu.emacs.EmacsNoninteractive";
+
+ /* Arguments from here on are passed to main in
+ EmacsNoninteractive.java. */
+ args[5] = argv[0];
+
+ /* Now copy the rest of the arguments over. */
+ for (i = 1; i < argc; ++i)
+ args[5 + i] = argv[i];
+ }
+ else
+ {
+#endif
+ args[3] = (char *) "org.gnu.emacs.EmacsNoninteractive";
+
+ /* Arguments from here on are passed to main in
+ EmacsNoninteractive.java. */
+ args[4] = argv[0];
+
+ /* Now copy the rest of the arguments over. */
+ for (i = 1; i < argc; ++i)
+ args[4 + i] = argv[i];
+#if HAVE_DECL_ANDROID_GET_DEVICE_API_LEVEL
+ }
+#endif
+
+ /* Finally, try to start the app_process. */
+ execvp (args[0], args);
+
+ /* If exit fails, return an error indication. */
+ perror ("exec");
+ return 1;
+}
diff --git a/src/android.c b/src/android.c
new file mode 100644
index 00000000000..8a41a7cdec5
--- /dev/null
+++ b/src/android.c
@@ -0,0 +1,6726 @@
+/* Android initialization for GNU Emacs.
+
+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 <fcntl.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <limits.h>
+#include <signal.h>
+#include <semaphore.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <math.h>
+#include <string.h>
+
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+/* Old NDK versions lack MIN and MAX. */
+#include <minmax.h>
+
+#include <assert.h>
+#include <fingerprint.h>
+
+#include "android.h"
+#include "androidgui.h"
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "epaths.h"
+
+/* Whether or not Emacs is running inside the application process and
+ Android windowing should be enabled. */
+bool android_init_gui;
+
+#ifndef ANDROID_STUBIFY
+
+#if __ANDROID_API__ >= 9
+#include <android/asset_manager.h>
+#include <android/asset_manager_jni.h>
+#else
+#include "android-asset.h"
+#endif
+
+#include <android/bitmap.h>
+#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))
+
+#define ANDROID_MAX_ASSET_FD 65535
+
+struct android_fd_table_entry
+{
+ /* Various flags associated with this table. */
+ short flags;
+
+ /* The stat buffer associated with this entry. */
+ struct stat statb;
+};
+
+enum android_fd_table_entry_flags
+ {
+ ANDROID_FD_TABLE_ENTRY_IS_VALID = 1,
+ };
+
+struct android_emacs_service
+{
+ jclass class;
+ jmethodID fill_rectangle;
+ jmethodID fill_polygon;
+ jmethodID draw_rectangle;
+ jmethodID draw_line;
+ jmethodID draw_point;
+ jmethodID copy_area;
+ jmethodID clear_window;
+ jmethodID clear_area;
+ jmethodID ring_bell;
+ jmethodID query_tree;
+ jmethodID get_screen_width;
+ jmethodID get_screen_height;
+ jmethodID detect_mouse;
+ jmethodID name_keysym;
+ jmethodID browse_url;
+ jmethodID restart_emacs;
+ jmethodID update_ic;
+ jmethodID reset_ic;
+ jmethodID open_content_uri;
+ jmethodID check_content_uri;
+ jmethodID query_battery;
+ jmethodID display_toast;
+ jmethodID update_extracted_text;
+ jmethodID update_cursor_anchor_info;
+};
+
+struct android_emacs_pixmap
+{
+ jclass class;
+ jmethodID constructor;
+ jmethodID constructor_mutable;
+};
+
+struct android_graphics_point
+{
+ jclass class;
+ jmethodID constructor;
+};
+
+struct android_emacs_drawable
+{
+ jclass class;
+ jmethodID get_bitmap;
+ jmethodID damage_rect;
+};
+
+struct android_emacs_window
+{
+ jclass class;
+ jmethodID swap_buffers;
+ jmethodID toggle_on_screen_keyboard;
+ jmethodID lookup_string;
+ jmethodID set_fullscreen;
+ jmethodID change_window_background;
+ jmethodID reparent_to;
+ jmethodID map_window;
+ jmethodID unmap_window;
+ jmethodID resize_window;
+ jmethodID move_window;
+ jmethodID make_input_focus;
+ jmethodID raise;
+ jmethodID lower;
+ jmethodID get_window_geometry;
+ jmethodID translate_coordinates;
+ jmethodID set_dont_accept_focus;
+ jmethodID set_dont_focus_on_map;
+ jmethodID define_cursor;
+};
+
+struct android_emacs_cursor
+{
+ jclass class;
+ jmethodID constructor;
+};
+
+/* The API level of the current device. */
+static int android_api_level;
+
+/* The asset manager being used. */
+static AAssetManager *asset_manager;
+
+/* Whether or not Emacs has been initialized. */
+static int emacs_initialized;
+
+/* The directory used to store site-lisp. */
+char *android_site_load_path;
+
+/* The directory used to store native libraries. */
+char *android_lib_dir;
+
+/* The directory used to store game files. */
+char *android_game_path;
+
+/* The directory used to store temporary files. */
+char *android_cache_dir;
+
+/* The list of archive files within which the Java virtual macine
+ looks for class files. */
+char *android_class_path;
+
+/* The display's pixel densities. */
+double android_pixel_density_x, android_pixel_density_y;
+
+/* The Android application data directory. */
+static char *android_files_dir;
+
+/* Array of structures used to hold asset information corresponding to
+ a file descriptor. This assumes Emacs does not do funny things
+ with dup. It currently does not. */
+static struct android_fd_table_entry android_table[ANDROID_MAX_ASSET_FD];
+
+/* The Java environment being used for the main thread. */
+JNIEnv *android_java_env;
+
+/* The EmacsGC class. */
+static jclass emacs_gc_class;
+
+/* Various fields. */
+static jfieldID emacs_gc_foreground, emacs_gc_background;
+static jfieldID emacs_gc_function, emacs_gc_clip_rects;
+static jfieldID emacs_gc_clip_x_origin, emacs_gc_clip_y_origin;
+static jfieldID emacs_gc_stipple, emacs_gc_clip_mask;
+static jfieldID emacs_gc_fill_style, emacs_gc_ts_origin_x;
+static jfieldID emacs_gc_ts_origin_y;
+
+/* The constructor and one function. */
+static jmethodID emacs_gc_constructor, emacs_gc_mark_dirty;
+
+/* The Rect class. */
+static jclass android_rect_class;
+
+/* Its constructor. */
+static jmethodID android_rect_constructor;
+
+/* The EmacsService object. */
+static jobject emacs_service;
+
+/* Various methods associated with the EmacsService. */
+static struct android_emacs_service service_class;
+
+/* Various methods associated with the EmacsPixmap class. */
+static struct android_emacs_pixmap pixmap_class;
+
+/* Various methods associated with the Point class. */
+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;
+
+/* Various methods associated with the EmacsCursor class. */
+static struct android_emacs_cursor cursor_class;
+
+/* The last event serial used. This is a 32 bit value, but it is
+ stored in unsigned long to be consistent with X. */
+unsigned int event_serial;
+
+#ifdef __i386__
+
+/* Unused pointer used to control compiler optimizations. */
+void *unused_pointer;
+
+#endif /* __i386__ */
+
+
+
+/* Event handling functions. Events are stored on a (circular) queue
+ that is read synchronously. The Android port replaces pselect with
+ a function android_select, which runs pselect in a separate thread,
+ but more importantly also waits for events to be available on the
+ android event queue. */
+
+struct android_event_container
+{
+ /* The next and last events in this queue. */
+ struct android_event_container *volatile next, *last;
+
+ /* The event itself. */
+ union android_event event;
+};
+
+struct android_event_queue
+{
+ /* Mutex protecting the event queue. */
+ pthread_mutex_t mutex;
+
+ /* Mutex protecting the select data. */
+ pthread_mutex_t select_mutex;
+
+ /* The thread used to run select. */
+ pthread_t select_thread;
+
+ /* Condition variables for the reading side. */
+ pthread_cond_t read_var;
+
+ /* The number of events in the queue. If this is greater than 1024,
+ writing will block. */
+ volatile int num_events;
+
+ /* Circular queue of events. */
+ struct android_event_container events;
+};
+
+/* Arguments to pselect used by the select thread. */
+static volatile int android_pselect_nfds;
+static fd_set *volatile android_pselect_readfds;
+static fd_set *volatile android_pselect_writefds;
+static fd_set *volatile android_pselect_exceptfds;
+static struct timespec *volatile android_pselect_timeout;
+
+/* Value of pselect. */
+static int android_pselect_rc;
+
+/* The global event queue. */
+static struct android_event_queue event_queue;
+
+/* Semaphores used to signal select completion and start. */
+static sem_t android_pselect_sem, android_pselect_start_sem;
+
+#if __ANDROID_API__ < 16
+
+/* Select self-pipe. */
+static int select_pipe[2];
+
+#else
+
+/* Whether or not pselect has been interrupted. */
+static volatile sig_atomic_t android_pselect_interrupted;
+
+#endif
+
+static void *
+android_run_select_thread (void *data)
+{
+ /* Apparently this is required too. */
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ int rc;
+#if __ANDROID_API__ < 16
+ int nfds;
+ fd_set readfds, writefds;
+ char byte;
+#else
+ sigset_t signals, waitset;
+ int sig;
+#endif
+
+#if __ANDROID_API__ < 16
+ /* A completely different implementation is used when building for
+ Android versions earlier than 16, because pselect with a signal
+ mask does not work there. Instead of blocking SIGUSR1 and
+ unblocking it inside pselect, a file descriptor is used instead.
+ Something is written to the file descriptor every time select is
+ supposed to return. */
+
+ while (true)
+ {
+ /* Wait for the thread to be released. */
+ while (sem_wait (&android_pselect_start_sem) < 0)
+ ;;
+
+ /* Get the select lock and call pselect. API 8 does not have
+ working pselect in any sense. Instead, pselect wakes up on
+ select_pipe[0]. */
+
+ pthread_mutex_lock (&event_queue.select_mutex);
+ nfds = android_pselect_nfds;
+
+ if (android_pselect_readfds)
+ readfds = *android_pselect_readfds;
+ else
+ FD_ZERO (&readfds);
+
+ if (nfds < select_pipe[0] + 1)
+ nfds = select_pipe[0] + 1;
+ FD_SET (select_pipe[0], &readfds);
+
+ rc = pselect (nfds, &readfds, &writefds,
+ android_pselect_exceptfds,
+ android_pselect_timeout,
+ NULL);
+
+ /* Subtract 1 from rc if writefds contains the select pipe. */
+ if (FD_ISSET (select_pipe[0], &writefds))
+ rc -= 1;
+
+ /* Save the writefds back again. */
+ if (android_pselect_writefds)
+ *android_pselect_writefds = writefds;
+
+ android_pselect_rc = rc;
+ pthread_mutex_unlock (&event_queue.select_mutex);
+
+ /* Signal the main thread that there is now data to read. Hold
+ the event queue lock during this process to make sure this
+ does not happen before the main thread begins to wait for the
+ condition variable. */
+
+ pthread_mutex_lock (&event_queue.mutex);
+ pthread_cond_broadcast (&event_queue.read_var);
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Read a single byte from the select pipe. */
+ read (select_pipe[0], &byte, 1);
+
+ /* Signal the Emacs thread that pselect is done. If read_var
+ was signaled by android_write_event, event_queue.mutex could
+ still be locked, so this must come before. */
+ sem_post (&android_pselect_sem);
+ }
+#else
+ if (pthread_sigmask (SIG_BLOCK, &signals, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_sigmask: %s",
+ strerror (errno));
+
+ sigfillset (&signals);
+ sigdelset (&signals, SIGUSR1);
+ sigemptyset (&waitset);
+ sigaddset (&waitset, SIGUSR1);
+
+ while (true)
+ {
+ /* Wait for the thread to be released. */
+ while (sem_wait (&android_pselect_start_sem) < 0)
+ ;;
+
+ /* Clear the ``pselect interrupted'' flag. This is safe because
+ right now, SIGUSR1 is blocked. */
+ android_pselect_interrupted = 0;
+
+ /* Get the select lock and call pselect. */
+ pthread_mutex_lock (&event_queue.select_mutex);
+ rc = pselect (android_pselect_nfds,
+ android_pselect_readfds,
+ android_pselect_writefds,
+ android_pselect_exceptfds,
+ android_pselect_timeout,
+ &signals);
+ android_pselect_rc = rc;
+ pthread_mutex_unlock (&event_queue.select_mutex);
+
+ /* Signal the main thread that there is now data to read. Hold
+ the event queue lock during this process to make sure this
+ does not happen before the main thread begins to wait for the
+ condition variable. */
+
+ pthread_mutex_lock (&event_queue.mutex);
+ pthread_cond_broadcast (&event_queue.read_var);
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Check `android_pselect_interrupted' instead of rc and errno.
+
+ This is because `pselect' does not return an rc of -1 upon
+ being interrupted in some versions of Android, but does set
+ signal masks correctly. */
+
+ if (!android_pselect_interrupted)
+ /* Now, wait for SIGUSR1, unless pselect was interrupted and
+ the signal was already delivered. The Emacs thread will
+ always send this signal after read_var is triggered or the
+ UI thread has sent an event. */
+ sigwait (&waitset, &sig);
+
+ /* Signal the Emacs thread that pselect is done. If read_var
+ was signaled by android_write_event, event_queue.mutex could
+ still be locked, so this must come before. */
+ sem_post (&android_pselect_sem);
+ }
+#endif
+
+ return NULL;
+}
+
+#if __ANDROID_API__ >= 16
+
+static void
+android_handle_sigusr1 (int sig, siginfo_t *siginfo, void *arg)
+{
+ /* Notice that pselect has been interrupted. */
+ android_pselect_interrupted = 1;
+}
+
+#endif
+
+/* Semaphore used to indicate completion of a query.
+ This should ideally be defined further down. */
+static sem_t android_query_sem;
+
+/* Set up the global event queue by initializing the mutex and two
+ condition variables, and the linked list of events. This must be
+ called before starting the Emacs thread. Also, initialize the
+ thread used to run pselect.
+
+ These functions must also use the C library malloc and free,
+ because xmalloc is not thread safe. */
+
+static void
+android_init_events (void)
+{
+ struct sigaction sa;
+
+ if (pthread_mutex_init (&event_queue.mutex, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_mutex_init: %s",
+ strerror (errno));
+
+ if (pthread_mutex_init (&event_queue.select_mutex, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_mutex_init: %s",
+ strerror (errno));
+
+ if (pthread_cond_init (&event_queue.read_var, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_cond_init: %s",
+ strerror (errno));
+
+ sem_init (&android_pselect_sem, 0, 0);
+ sem_init (&android_pselect_start_sem, 0, 0);
+ sem_init (&android_query_sem, 0, 0);
+
+ event_queue.events.next = &event_queue.events;
+ event_queue.events.last = &event_queue.events;
+
+#if __ANDROID_API__ >= 16
+
+ /* Before starting the select thread, make sure the disposition for
+ SIGUSR1 is correct. */
+ sigfillset (&sa.sa_mask);
+ sa.sa_sigaction = android_handle_sigusr1;
+ sa.sa_flags = SA_SIGINFO;
+
+#else
+
+ /* Set up the file descriptor used to wake up pselect. */
+ if (pipe2 (select_pipe, O_CLOEXEC) < 0)
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pipe2: %s", strerror (errno));
+
+ /* Make sure the read end will fit in fd_set. */
+ if (select_pipe[0] >= FD_SETSIZE)
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "read end of select pipe"
+ " lies outside FD_SETSIZE!");
+
+#endif
+
+ if (sigaction (SIGUSR1, &sa, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "sigaction: %s",
+ strerror (errno));
+
+ /* Start the select thread. */
+ if (pthread_create (&event_queue.select_thread, NULL,
+ android_run_select_thread, NULL))
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "pthread_create: %s",
+ strerror (errno));
+}
+
+int
+android_pending (void)
+{
+ int i;
+
+ pthread_mutex_lock (&event_queue.mutex);
+ i = event_queue.num_events;
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ return i;
+}
+
+/* Wait for events to become available synchronously. Return once an
+ event arrives. */
+
+void
+android_wait_event (void)
+{
+ pthread_mutex_lock (&event_queue.mutex);
+
+ /* Wait for events to appear if there are none available to
+ read. */
+ if (!event_queue.num_events)
+ pthread_cond_wait (&event_queue.read_var,
+ &event_queue.mutex);
+
+ pthread_mutex_unlock (&event_queue.mutex);
+}
+
+void
+android_next_event (union android_event *event_return)
+{
+ struct android_event_container *container;
+
+ pthread_mutex_lock (&event_queue.mutex);
+
+ /* Wait for events to appear if there are none available to
+ read. */
+ if (!event_queue.num_events)
+ pthread_cond_wait (&event_queue.read_var,
+ &event_queue.mutex);
+
+ /* Obtain the event from the end of the queue. */
+ container = event_queue.events.last;
+ eassert (container != &event_queue.events);
+
+ /* Remove the event from the queue and copy it to the caller
+ supplied buffer. */
+ container->last->next = container->next;
+ container->next->last = container->last;
+ *event_return = container->event;
+ event_queue.num_events--;
+
+ /* Free the container. */
+ free (container);
+
+ /* Unlock the queue. */
+ pthread_mutex_unlock (&event_queue.mutex);
+}
+
+bool
+android_check_if_event (union android_event *event_return,
+ bool (*predicate) (union android_event *,
+ void *),
+ void *arg)
+{
+ struct android_event_container *container;
+
+ pthread_mutex_lock (&event_queue.mutex);
+
+ /* Loop over each event. */
+ container = event_queue.events.last;
+ for (; container != &event_queue.events; container = container->last)
+ {
+ /* See if the predicate matches. */
+ if ((*predicate) (&container->event, arg))
+ {
+ /* Copy out the event and return true. */
+ *event_return = container->event;
+ --event_queue.num_events;
+
+ /* Unlink container. */
+ container->last->next = container->next;
+ container->next->last = container->last;
+ free (container);
+ pthread_mutex_unlock (&event_queue.mutex);
+ return true;
+ }
+ }
+
+ pthread_mutex_unlock (&event_queue.mutex);
+ return false;
+}
+
+void
+android_write_event (union android_event *event)
+{
+ struct android_event_container *container;
+
+ container = malloc (sizeof *container);
+
+ if (!container)
+ return;
+
+ /* If the event queue hasn't been initialized yet, return false. */
+ if (!event_queue.events.next)
+ return;
+
+ pthread_mutex_lock (&event_queue.mutex);
+ container->next = event_queue.events.next;
+ container->last = &event_queue.events;
+ container->next->last = container;
+ container->last->next = container;
+ container->event = *event;
+ event_queue.num_events++;
+ pthread_cond_broadcast (&event_queue.read_var);
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Now set pending_signals to true, and raise SIGIO to interrupt any
+ ongoing reads if the event is important. */
+ pending_signals = true;
+
+ switch (event->type)
+ {
+ /* Key press and window action events are considered important,
+ as they either end up quitting or asking for responses to the
+ IME. */
+ case ANDROID_KEY_PRESS:
+ case ANDROID_WINDOW_ACTION:
+ raise (SIGIO);
+ break;
+
+ default:
+ break;
+ }
+}
+
+int
+android_select (int nfds, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds, struct timespec *timeout)
+{
+ int nfds_return;
+#if __ANDROID_API__ < 16
+ static char byte;
+#endif
+
+ /* Check for and run anything the UI thread wants to run on the main
+ thread. */
+ android_check_query ();
+
+ pthread_mutex_lock (&event_queue.mutex);
+
+ if (event_queue.num_events)
+ {
+ pthread_mutex_unlock (&event_queue.mutex);
+ return 1;
+ }
+
+ nfds_return = 0;
+
+ pthread_mutex_lock (&event_queue.select_mutex);
+ android_pselect_nfds = nfds;
+ android_pselect_readfds = readfds;
+ android_pselect_writefds = writefds;
+ android_pselect_exceptfds = exceptfds;
+ android_pselect_timeout = timeout;
+ pthread_mutex_unlock (&event_queue.select_mutex);
+
+ /* Release the select thread. */
+ sem_post (&android_pselect_start_sem);
+
+ /* Start waiting for the event queue condition to be set. */
+ pthread_cond_wait (&event_queue.read_var, &event_queue.mutex);
+
+#if __ANDROID_API__ >= 16
+ /* Interrupt the select thread now, in case it's still in
+ pselect. */
+ pthread_kill (event_queue.select_thread, SIGUSR1);
+#else
+ /* Interrupt the select thread by writing to the select pipe. */
+ if (write (select_pipe[1], &byte, 1) != 1)
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "write: %s", strerror (errno));
+#endif
+
+ /* Unlock the event queue mutex. */
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Wait for pselect to return in any case. This must be done with
+ the event queue mutex unlocked. Otherwise, the pselect thread
+ can hang if it tries to lock the event queue mutex to signal
+ read_var after the UI thread has already done so. */
+ while (sem_wait (&android_pselect_sem) < 0)
+ ;;
+
+ /* If there are now events in the queue, return 1. */
+
+ pthread_mutex_lock (&event_queue.mutex);
+ if (event_queue.num_events)
+ nfds_return = 1;
+ pthread_mutex_unlock (&event_queue.mutex);
+
+ /* Add the return value of pselect. */
+ if (android_pselect_rc >= 0)
+ nfds_return += android_pselect_rc;
+
+ if (!nfds_return && android_pselect_rc < 0)
+ nfds_return = android_pselect_rc;
+
+ /* This is to shut up process.c when pselect gets EINTR. */
+ if (nfds_return < 0)
+ errno = EINTR;
+
+ /* Now check for and run anything the UI thread wants to run in the
+ main thread. */
+ android_check_query ();
+
+ return nfds_return;
+}
+
+
+
+static void *
+android_run_debug_thread (void *data)
+{
+ FILE *file;
+ int fd;
+ char *line;
+ size_t n;
+
+ fd = (int) (intptr_t) data;
+ file = fdopen (fd, "r");
+
+ if (!file)
+ return NULL;
+
+ line = NULL;
+
+ while (true)
+ {
+ if (getline (&line, &n, file) < 0)
+ {
+ free (line);
+ break;
+ }
+
+ __android_log_print (ANDROID_LOG_INFO, __func__, "%s", line);
+ }
+
+ fclose (file);
+ return NULL;
+}
+
+
+
+/* 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 ();
+
+ /* Return the size of the directory tree as the limit.
+ Do not subtract the initial header bytes, as the limit
+ is an offset from the start of the file. */
+
+ if (limit_return)
+ *limit_return = directory_tree_size;
+
+ 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;
+}
+
+/* Return whether or not the directory tree entry DIR is a
+ directory.
+
+ DIR should be a value returned by
+ `android_scan_directory_tree'. */
+
+static bool
+android_is_directory (const char *dir)
+{
+ /* If the directory is the directory tree, then it is a
+ directory. */
+ if (dir == directory_tree + 5)
+ return true;
+
+ /* Otherwise, look 5 bytes behind. If it is `/', then it is a
+ directory. */
+ return (dir - 6 >= directory_tree
+ && *(dir - 6) == '/');
+}
+
+
+
+/* Intercept USER_FULL_NAME and return something that makes sense if
+ pw->pw_gecos is NULL. */
+
+char *
+android_user_full_name (struct passwd *pw)
+{
+#ifdef HAVE_STRUCT_PASSWD_PW_GECOS
+ if (!pw->pw_gecos)
+ return (char *) "Android user";
+
+ return pw->pw_gecos;
+#else
+ return "Android user";
+#endif
+}
+
+/* Given a real file name, return the part that describes its asset
+ path, or NULL if it is not an asset. */
+
+static const char *
+android_get_asset_name (const char *filename)
+{
+ if (!strcmp (filename, "/assets") || !strcmp (filename, "/assets/"))
+ return "/";
+
+ if (!strncmp (filename, "/assets/", sizeof "/assets/" - 1))
+ return filename + (sizeof "/assets/" - 1);
+
+ return NULL;
+}
+
+/* Return whether or not the specified FILENAME actually resolves to a
+ content resolver URI. */
+
+static bool
+android_content_name_p (const char *filename)
+{
+ /* Content URIs aren't supported before Android 4.4, so return
+ false. */
+
+ if (android_api_level < 19)
+ return false;
+
+ return (!strcmp (filename, "/content")
+ || !strncmp (filename, "/content/",
+ sizeof "/content/" - 1));
+}
+
+/* Return the content URI corresponding to a `/content' file name,
+ or NULL if it is not a content URI.
+
+ This function is not reentrant. */
+
+static const char *
+android_get_content_name (const char *filename)
+{
+ static char buffer[PATH_MAX + 1];
+ char *head, *token, *saveptr, *copy;
+ size_t n;
+
+ n = PATH_MAX;
+
+ /* First handle content ``URIs'' without a provider. */
+
+ if (!strcmp (filename, "/content")
+ || !strcmp (filename, "/content/"))
+ return "content://";
+
+ /* Next handle ordinary file names. */
+
+ if (strncmp (filename, "/content/", sizeof "/content/" - 1))
+ return NULL;
+
+ /* Forward past the first directory specifying the schema. */
+
+ copy = xstrdup (filename + sizeof "/content");
+ token = saveptr = NULL;
+ head = stpcpy (buffer, "content:/");
+
+ /* Split FILENAME by slashes. */
+
+ while ((token = strtok_r (!token ? copy : NULL,
+ "/", &saveptr)))
+ {
+ head = stpncpy (head, "/", n--);
+ head = stpncpy (head, token, n);
+
+ /* Check that head has not overflown the buffer. */
+ eassert ((head - buffer) <= PATH_MAX);
+
+ n = PATH_MAX - (head - buffer);
+ }
+
+ /* Make sure the given buffer ends up NULL terminated. */
+ buffer[PATH_MAX] = '\0';
+ xfree (copy);
+
+ return buffer;
+}
+
+/* Return whether or not the specified FILENAME is an accessible
+ content URI. MODE specifies what to check. */
+
+static bool
+android_check_content_access (const char *filename, int mode)
+{
+ const char *name;
+ jobject string;
+ size_t length;
+ jboolean rc;
+
+ name = android_get_content_name (filename);
+ length = strlen (name);
+
+ string = (*android_java_env)->NewByteArray (android_java_env,
+ length);
+ android_exception_check ();
+
+ (*android_java_env)->SetByteArrayRegion (android_java_env,
+ string, 0, length,
+ (jbyte *) name);
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ emacs_service,
+ service_class.check_content_uri,
+ string,
+ (jboolean) ((mode & R_OK)
+ != 0),
+ (jboolean) ((mode & W_OK)
+ != 0));
+ android_exception_check_1 (string);
+ ANDROID_DELETE_LOCAL_REF (string);
+
+ return rc;
+}
+
+/* Like fstat. However, look up the asset corresponding to the file
+ descriptor. If it exists, return the right information. */
+
+int
+android_fstat (int fd, struct stat *statb)
+{
+ if (fd < ANDROID_MAX_ASSET_FD
+ && (android_table[fd].flags
+ & ANDROID_FD_TABLE_ENTRY_IS_VALID))
+ {
+ memcpy (statb, &android_table[fd].statb,
+ sizeof *statb);
+ return 0;
+ }
+
+ return fstat (fd, statb);
+}
+
+static int android_lookup_asset_directory_fd (int,
+ const char *restrict *,
+ const char *restrict);
+
+/* Like fstatat. However, if dirfd is AT_FDCWD and PATHNAME is an
+ asset, find the information for the corresponding asset, and if
+ dirfd is an offset into directory_tree as returned by
+ `android_dirfd', find the information within the corresponding
+ directory tree entry. */
+
+int
+android_fstatat (int dirfd, const char *restrict pathname,
+ struct stat *restrict statbuf, int flags)
+{
+ AAsset *asset_desc;
+ const char *asset;
+ const char *asset_dir;
+ int fd, rc;
+
+ /* Look up whether or not DIRFD belongs to an open struct
+ android_dir. */
+
+ if (dirfd != AT_FDCWD)
+ dirfd
+ = android_lookup_asset_directory_fd (dirfd, &pathname,
+ pathname);
+
+ if (dirfd == AT_FDCWD
+ && asset_manager
+ && (asset = android_get_asset_name (pathname)))
+ {
+ /* Look up whether or not PATHNAME happens to be a
+ directory. */
+ asset_dir = android_scan_directory_tree ((char *) asset,
+ NULL);
+
+ if (!asset_dir)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (android_is_directory (asset_dir))
+ {
+ memset (statbuf, 0, sizeof *statbuf);
+
+ /* Fill in the stat buffer. */
+ statbuf->st_mode = S_IFDIR | S_IRUSR | S_IRGRP | S_IROTH;
+ return 0;
+ }
+
+ /* AASSET_MODE_STREAMING is fastest here. */
+ asset_desc = AAssetManager_open (asset_manager, asset,
+ AASSET_MODE_STREAMING);
+
+ if (!asset_desc)
+ return ENOENT;
+
+ memset (statbuf, 0, sizeof *statbuf);
+
+ /* Fill in the stat buffer. */
+ statbuf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+ statbuf->st_size = AAsset_getLength (asset_desc);
+
+ /* Close the asset. */
+ AAsset_close (asset_desc);
+ return 0;
+ }
+
+ if (dirfd == AT_FDCWD
+ && android_init_gui
+ && android_content_name_p (pathname))
+ {
+ /* This is actually a content:// URI. Open that file and call
+ stat on it. */
+
+ fd = android_open (pathname, O_RDONLY, 0);
+
+ if (fd < 0)
+ return -1;
+
+ rc = fstat (fd, statbuf);
+ android_close (fd);
+ return rc;
+ }
+
+ return fstatat (dirfd, pathname, statbuf, flags);
+}
+
+/* Return if NAME, a file name relative to the /assets directory, is
+ accessible, as long as !(amode & W_OK). */
+
+static bool
+android_file_access_p (const char *name, int amode)
+{
+ if (!asset_manager)
+ return false;
+
+ if (!(amode & W_OK))
+ {
+ if (!strcmp (name, "") || !strcmp (name, "/"))
+ /* /assets always exists. */
+ 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;
+}
+
+/* Do the same as android_hack_asset_fd, but use an unlinked temporary
+ file to cater to old Android kernels where ashmem files are not
+ readable. */
+
+static int
+android_hack_asset_fd_fallback (AAsset *asset)
+{
+ int fd;
+ char filename[PATH_MAX];
+ size_t size;
+ void *mem;
+
+ /* Assets must be small enough to fit in size_t, if off_t is
+ larger. */
+ size = AAsset_getLength (asset);
+
+ /* Get an unlinked file descriptor from a file in the cache
+ directory, which is guaranteed to only be written to by Emacs.
+ Creating an ashmem file descriptor and reading from it doesn't
+ work on these old Android versions. */
+
+ snprintf (filename, PATH_MAX, "%s/temp~unlinked.%d",
+ android_cache_dir, getpid ());
+ fd = open (filename, O_CREAT | O_RDWR | O_TRUNC,
+ S_IRUSR | S_IWUSR);
+
+ if (fd < 0)
+ return -1;
+
+ if (unlink (filename))
+ goto fail;
+
+ if (ftruncate (fd, size))
+ goto fail;
+
+ mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "mmap: %s", strerror (errno));
+ goto fail;
+ }
+
+ if (AAsset_read (asset, mem, size) != size)
+ {
+ /* Too little was read. Close the file descriptor and
+ report an error. */
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "AAsset_read: %s", strerror (errno));
+ goto fail;
+ }
+
+ munmap (mem, size);
+ return fd;
+
+ fail:
+ close (fd);
+ return -1;
+}
+
+/* Pointer to the `ASharedMemory_create' function which is loaded
+ dynamically. */
+static int (*asharedmemory_create) (const char *, size_t);
+
+/* Return whether or not shared memory file descriptors can also be
+ read from, and are thus suitable for creating asset files.
+
+ This does not work on some ancient Android systems running old
+ versions of the kernel. */
+
+static bool
+android_detect_ashmem (void)
+{
+ int fd, rc;
+ void *mem;
+ char test_buffer[10];
+
+ memcpy (test_buffer, "abcdefghi", 10);
+
+ /* Create the file descriptor to be used for the test. */
+
+ /* Android 28 and earlier let Emacs access /dev/ashmem directly, so
+ prefer that over using ASharedMemory. */
+
+ if (android_api_level <= 28)
+ {
+ fd = open ("/dev/ashmem", O_RDWR);
+
+ if (fd < 0)
+ return false;
+
+ /* An empty name means the memory area will exist until the file
+ descriptor is closed, because no other process can
+ attach. */
+ rc = ioctl (fd, ASHMEM_SET_NAME, "");
+
+ if (rc < 0)
+ {
+ close (fd);
+ return false;
+ }
+
+ rc = ioctl (fd, ASHMEM_SET_SIZE, sizeof test_buffer);
+
+ if (rc < 0)
+ {
+ close (fd);
+ return false;
+ }
+ }
+ else
+ {
+ /* On the other hand, SELinux restrictions on Android 29 and
+ later require that Emacs use a system service to obtain
+ shared memory. Load this dynamically, as this service is not
+ available on all versions of the NDK. */
+
+ if (!asharedmemory_create)
+ {
+ *(void **) (&asharedmemory_create)
+ = dlsym (RTLD_DEFAULT, "ASharedMemory_create");
+
+ if (!asharedmemory_create)
+ {
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "dlsym: %s\n",
+ strerror (errno));
+ emacs_abort ();
+ }
+ }
+
+ fd = asharedmemory_create ("", sizeof test_buffer);
+
+ if (fd < 0)
+ return false;
+ }
+
+ /* Now map the resource and write the test contents. */
+
+ mem = mmap (NULL, sizeof test_buffer, PROT_WRITE,
+ MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ {
+ close (fd);
+ return false;
+ }
+
+ /* Copy over the test contents. */
+ memcpy (mem, test_buffer, sizeof test_buffer);
+
+ /* Return anyway even if munmap fails. */
+ munmap (mem, sizeof test_buffer);
+
+ /* Try to read the content back into test_buffer. If this does not
+ compare equal to the original string, or the read fails, then
+ ashmem descriptors are not readable on this system. */
+
+ if ((read (fd, test_buffer, sizeof test_buffer)
+ != sizeof test_buffer)
+ || memcmp (test_buffer, "abcdefghi", sizeof test_buffer))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "/dev/ashmem does not produce real"
+ " temporary files on this system, so"
+ " Emacs will fall back to creating"
+ " unlinked temporary files.");
+ close (fd);
+ return false;
+ }
+
+ close (fd);
+ return true;
+}
+
+/* Get a file descriptor backed by a temporary in-memory file for the
+ given asset. */
+
+static int
+android_hack_asset_fd (AAsset *asset)
+{
+ static bool ashmem_readable_p;
+ static bool ashmem_initialized;
+ int fd, rc;
+ unsigned char *mem;
+ size_t size;
+
+ /* The first time this function is called, try to determine whether
+ or not ashmem file descriptors can be read from. */
+
+ if (!ashmem_initialized)
+ ashmem_readable_p
+ = android_detect_ashmem ();
+ ashmem_initialized = true;
+
+ /* If it isn't, fall back. */
+
+ if (!ashmem_readable_p)
+ return android_hack_asset_fd_fallback (asset);
+
+ /* Assets must be small enough to fit in size_t, if off_t is
+ larger. */
+ size = AAsset_getLength (asset);
+
+ /* Android 28 and earlier let Emacs access /dev/ashmem directly, so
+ prefer that over using ASharedMemory. */
+
+ if (android_api_level <= 28)
+ {
+ fd = open ("/dev/ashmem", O_RDWR);
+
+ if (fd < 0)
+ return -1;
+
+ /* An empty name means the memory area will exist until the file
+ descriptor is closed, because no other process can
+ attach. */
+ rc = ioctl (fd, ASHMEM_SET_NAME, "");
+
+ if (rc < 0)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "ioctl ASHMEM_SET_NAME: %s",
+ strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ rc = ioctl (fd, ASHMEM_SET_SIZE, size);
+
+ if (rc < 0)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "ioctl ASHMEM_SET_SIZE: %s",
+ strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (!size)
+ return fd;
+
+ /* Now map the resource. */
+ mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "mmap: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (AAsset_read (asset, mem, size) != size)
+ {
+ /* Too little was read. Close the file descriptor and
+ report an error. */
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "AAsset_read: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ /* Return anyway even if munmap fails. */
+ munmap (mem, size);
+ return fd;
+ }
+
+ /* On the other hand, SELinux restrictions on Android 29 and later
+ require that Emacs use a system service to obtain shared memory.
+ Load this dynamically, as this service is not available on all
+ versions of the NDK. */
+
+ if (!asharedmemory_create)
+ {
+ *(void **) (&asharedmemory_create)
+ = dlsym (RTLD_DEFAULT, "ASharedMemory_create");
+
+ if (!asharedmemory_create)
+ {
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "dlsym: %s\n",
+ strerror (errno));
+ emacs_abort ();
+ }
+ }
+
+ fd = asharedmemory_create ("", size);
+
+ if (fd < 0)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "ASharedMemory_create: %s",
+ strerror (errno));
+ return -1;
+ }
+
+ /* Now map the resource. */
+ mem = mmap (NULL, size, PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "mmap: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ if (AAsset_read (asset, mem, size) != size)
+ {
+ /* Too little was read. Close the file descriptor and
+ report an error. */
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "AAsset_read: %s", strerror (errno));
+ close (fd);
+ return -1;
+ }
+
+ /* Return anyway even if munmap fails. */
+ munmap (mem, size);
+ return fd;
+}
+
+/* Make FD close-on-exec. If any system call fails, do not abort, but
+ log a warning to the system log. */
+
+static void
+android_close_on_exec (int fd)
+{
+ int flags, rc;
+
+ flags = fcntl (fd, F_GETFD);
+
+ if (flags < 0)
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "fcntl: %s", strerror (errno));
+ return;
+ }
+
+ rc = fcntl (fd, F_SETFD, flags | O_CLOEXEC);
+
+ if (rc < 0)
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "fcntl: %s", strerror (errno));
+ return;
+ }
+}
+
+/* `open' and such are modified even though they exist on Android,
+ because Emacs treats "/assets/" as a special directory that must
+ contain all assets in the application package. */
+
+int
+android_open (const char *filename, int oflag, mode_t mode)
+{
+ const char *name;
+ AAsset *asset;
+ int fd;
+ size_t length;
+ jobject string;
+
+ if (asset_manager && (name = android_get_asset_name (filename)))
+ {
+ /* If Emacs is trying to write to the file, return NULL. */
+
+ if (oflag & O_WRONLY || oflag & O_RDWR)
+ {
+ errno = EROFS;
+ return -1;
+ }
+
+ if (oflag & O_DIRECTORY)
+ {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ /* If given AASSET_MODE_BUFFER (which is what Emacs probably
+ does, given that a file descriptor is not always available),
+ the framework fails to uncompress the data before it returns
+ a file descriptor. */
+ asset = AAssetManager_open (asset_manager, name,
+ AASSET_MODE_STREAMING);
+
+ if (!asset)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* Create a shared memory file descriptor containing the asset
+ contents.
+
+ The documentation misleads people into thinking that
+ AAsset_openFileDescriptor does precisely this. However, it
+ instead returns an offset into any uncompressed assets in the
+ ZIP archive. This cannot be found in its documentation. */
+
+ fd = android_hack_asset_fd (asset);
+
+ if (fd == -1)
+ {
+ AAsset_close (asset);
+ errno = ENXIO;
+ return -1;
+ }
+
+ /* If O_CLOEXEC is specified, make the file descriptor close on
+ exec too. */
+ if (oflag & O_CLOEXEC)
+ android_close_on_exec (fd);
+
+ if (fd >= ANDROID_MAX_ASSET_FD || fd < 0)
+ {
+ /* Too bad. Pretend this is an out of memory error. */
+ errno = ENOMEM;
+
+ if (fd >= 0)
+ close (fd);
+
+ fd = -1;
+ }
+ else
+ {
+ assert (!(android_table[fd].flags
+ & ANDROID_FD_TABLE_ENTRY_IS_VALID));
+ android_table[fd].flags = ANDROID_FD_TABLE_ENTRY_IS_VALID;
+ memset (&android_table[fd].statb, 0,
+ sizeof android_table[fd].statb);
+
+ /* Fill in some information that will be reported to
+ callers of android_fstat, among others. */
+ android_table[fd].statb.st_mode
+ = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+
+ /* Owned by root. */
+ android_table[fd].statb.st_uid = 0;
+ android_table[fd].statb.st_gid = 0;
+
+ /* Size of the file. */
+ android_table[fd].statb.st_size
+ = AAsset_getLength (asset);
+ }
+
+ AAsset_close (asset);
+ return fd;
+ }
+
+ if (android_init_gui && android_content_name_p (filename))
+ {
+ /* This is a content:// URI. Ask the system for a descriptor to
+ that file. */
+
+ name = android_get_content_name (filename);
+ length = strlen (name);
+
+ /* Check if the mode is valid. */
+
+ if (oflag & O_DIRECTORY)
+ {
+ errno = ENOTSUP;
+ return -1;
+ }
+
+ /* Allocate a buffer to hold the file name. */
+ string = (*android_java_env)->NewByteArray (android_java_env,
+ length);
+ if (!string)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ errno = ENOMEM;
+ return -1;
+ }
+ (*android_java_env)->SetByteArrayRegion (android_java_env,
+ string, 0, length,
+ (jbyte *) name);
+
+ /* Try to open the file descriptor. */
+
+ fd
+ = (*android_java_env)->CallIntMethod (android_java_env,
+ emacs_service,
+ service_class.open_content_uri,
+ string,
+ (jboolean) ((mode & O_WRONLY
+ || mode & O_RDWR)
+ != 0),
+ (jboolean) !(mode & O_WRONLY),
+ (jboolean) ((mode & O_TRUNC)
+ != 0));
+
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ errno = ENOMEM;
+ ANDROID_DELETE_LOCAL_REF (string);
+ return -1;
+ }
+
+ /* If fd is -1, just assume that the file does not exist,
+ and return -1 with errno set to ENOENT. */
+
+ if (fd == -1)
+ {
+ errno = ENOENT;
+ goto skip;
+ }
+
+ if (mode & O_CLOEXEC)
+ android_close_on_exec (fd);
+
+ skip:
+ ANDROID_DELETE_LOCAL_REF (string);
+ return fd;
+ }
+
+ return open (filename, oflag, mode);
+}
+
+/* Like close. However, remove the file descriptor from the asset
+ table as well. */
+
+int
+android_close (int fd)
+{
+ if (fd < ANDROID_MAX_ASSET_FD)
+ android_table[fd].flags = 0;
+
+ return close (fd);
+}
+
+/* Like fclose. However, remove any information associated with
+ FILE's file descriptor from the asset table as well. */
+
+int
+android_fclose (FILE *stream)
+{
+ int fd;
+
+ fd = fileno (stream);
+
+ if (fd != -1 && fd < ANDROID_MAX_ASSET_FD)
+ android_table[fd].flags = 0;
+
+ return fclose (stream);
+}
+
+/* Return the current user's ``home'' directory, which is actually the
+ app data directory on Android. */
+
+const char *
+android_get_home_directory (void)
+{
+ return android_files_dir;
+}
+
+/* Return the name of the file behind a file descriptor FD by reading
+ /proc/self/fd/. Place the name in BUFFER, which should be able to
+ hold size bytes. Value is 0 upon success, and 1 upon failure. */
+
+static int
+android_proc_name (int fd, char *buffer, size_t size)
+{
+ char format[sizeof "/proc/self/fd/"
+ + INT_STRLEN_BOUND (int)];
+ ssize_t read;
+
+ sprintf (format, "/proc/self/fd/%d", fd);
+ read = readlink (format, buffer, size - 1);
+
+ if (read == -1)
+ return 1;
+
+ buffer[read] = '\0';
+ return 0;
+}
+
+
+
+/* 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 jint JNICALL
+NATIVE_NAME (dup) (JNIEnv *env, jobject object, jint fd)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ return dup (fd);
+}
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getFingerprint) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ char buffer[sizeof fingerprint * 2 + 1];
+
+ memset (buffer, 0, sizeof buffer);
+ hexbuf_digest (buffer, (char *) fingerprint,
+ sizeof fingerprint);
+
+ return (*env)->NewStringUTF (env, buffer);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setEmacsParams) (JNIEnv *env, jobject object,
+ jobject local_asset_manager,
+ jobject files_dir, jobject libs_dir,
+ jobject cache_dir,
+ jfloat pixel_density_x,
+ jfloat pixel_density_y,
+ jobject class_path,
+ jobject emacs_service_object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ 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. */
+ if (__atomic_fetch_add (&emacs_initialized, -1, __ATOMIC_SEQ_CST))
+ {
+ ANDROID_THROW (env, "java/lang/IllegalArgumentException",
+ "Emacs was already initialized!");
+ return;
+ }
+
+ android_pixel_density_x = pixel_density_x;
+ android_pixel_density_y = pixel_density_y;
+
+ __android_log_print (ANDROID_LOG_INFO, __func__,
+ "Initializing "PACKAGE_STRING"...\nPlease report bugs to "
+ PACKAGE_BUGREPORT". Thanks.\n");
+
+ /* 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);
+
+ if (emacs_service_object)
+ {
+ /* Create a pipe and duplicate it to stdout and stderr. Next,
+ make a thread that prints stderr to the system log.
+
+ Notice that this function is called in one of two ways. The
+ first is when Emacs is being started as a GUI application by
+ the system, and the second is when Emacs is being started by
+ libandroid-emacs.so as an ordinary noninteractive Emacs.
+
+ In the second case, stderr is usually connected to a PTY, so
+ this is unnecessary. */
+
+ if (pipe2 (pipefd, O_CLOEXEC) < 0)
+ emacs_abort ();
+
+ if (dup2 (pipefd[1], 2) < 0)
+ emacs_abort ();
+ close (pipefd[1]);
+
+ if (pthread_create (&thread, NULL, android_run_debug_thread,
+ (void *) (intptr_t) pipefd[0]))
+ emacs_abort ();
+ }
+
+ /* Now set the path to the site load directory. */
+
+ java_string = (*env)->GetStringUTFChars (env, (jstring) files_dir,
+ NULL);
+
+ if (!java_string)
+ emacs_abort ();
+
+ android_files_dir = strdup ((const char *) java_string);
+
+ if (!android_files_dir)
+ emacs_abort ();
+
+ (*env)->ReleaseStringUTFChars (env, (jstring) files_dir,
+ java_string);
+
+ java_string = (*env)->GetStringUTFChars (env, (jstring) libs_dir,
+ NULL);
+
+ if (!java_string)
+ emacs_abort ();
+
+ android_lib_dir = strdup ((const char *) java_string);
+
+ if (!android_files_dir)
+ emacs_abort ();
+
+ (*env)->ReleaseStringUTFChars (env, (jstring) libs_dir,
+ java_string);
+
+ java_string = (*env)->GetStringUTFChars (env, (jstring) cache_dir,
+ NULL);
+
+ if (!java_string)
+ emacs_abort ();
+
+ android_cache_dir = strdup ((const char *) java_string);
+
+ if (!android_files_dir)
+ emacs_abort ();
+
+ (*env)->ReleaseStringUTFChars (env, (jstring) cache_dir,
+ java_string);
+
+ if (class_path)
+ {
+ java_string = (*env)->GetStringUTFChars (env, (jstring) class_path,
+ NULL);
+
+ if (!java_string)
+ emacs_abort ();
+
+ android_class_path = strdup ((const char *) java_string);
+
+ if (!android_files_dir)
+ emacs_abort ();
+
+ (*env)->ReleaseStringUTFChars (env, (jstring) class_path,
+ java_string);
+ }
+
+ /* Calculate the site-lisp path. */
+
+ android_site_load_path = malloc (PATH_MAX + 1);
+
+ if (!android_site_load_path)
+ emacs_abort ();
+
+ android_game_path = malloc (PATH_MAX + 1);
+
+ if (!android_game_path)
+ emacs_abort ();
+
+ snprintf (android_site_load_path, PATH_MAX, "%s/site-lisp",
+ android_files_dir);
+ snprintf (android_game_path, PATH_MAX, "%s/scores", android_files_dir);
+
+ __android_log_print (ANDROID_LOG_INFO, __func__,
+ "Site-lisp directory: %s\n"
+ "Files directory: %s\n"
+ "Native code directory: %s\n"
+ "Game score path: %s\n"
+ "Class path: %s\n",
+ android_site_load_path,
+ android_files_dir,
+ android_lib_dir, android_game_path,
+ (android_class_path
+ ? android_class_path
+ : "None"));
+
+ if (android_class_path)
+ /* Set EMACS_CLASS_PATH to the class path where
+ EmacsNoninteractive can be found. */
+ setenv ("EMACS_CLASS_PATH", android_class_path, 1);
+
+ /* Set LD_LIBRARY_PATH to an appropriate value. */
+ setenv ("LD_LIBRARY_PATH", android_lib_dir, 1);
+
+ /* Make a reference to the Emacs service. */
+
+ if (emacs_service_object)
+ {
+ emacs_service = (*env)->NewGlobalRef (env, emacs_service_object);
+
+ if (!emacs_service)
+ emacs_abort ();
+ }
+
+ /* Set up events. */
+ android_init_events ();
+
+ /* OK, setup is now complete. The caller may start the Emacs thread
+ now. */
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (getProcName) (JNIEnv *env, jobject object, jint fd)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ char buffer[PATH_MAX + 1];
+ size_t length;
+ jbyteArray array;
+
+ if (android_proc_name (fd, buffer, PATH_MAX + 1))
+ return NULL;
+
+ /* Return a byte array, as Java strings cannot always encode file
+ names. */
+ length = strlen (buffer);
+ array = (*env)->NewByteArray (env, length);
+ if (!array)
+ return NULL;
+
+ (*env)->SetByteArrayRegion (env, array, 0, length,
+ (jbyte *) buffer);
+
+ return array;
+}
+
+/* Initialize service_class, aborting if something goes wrong. */
+
+static void
+android_init_emacs_service (void)
+{
+ jclass old;
+
+ service_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsService");
+ eassert (service_class.class);
+
+ old = service_class.class;
+ service_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!service_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ service_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ service_class.class, \
+ name, signature); \
+ assert (service_class.c_name);
+
+ FIND_METHOD (fill_rectangle, "fillRectangle",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;IIII)V");
+ FIND_METHOD (fill_polygon, "fillPolygon",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;"
+ "[Landroid/graphics/Point;)V");
+ FIND_METHOD (draw_rectangle, "drawRectangle",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;IIII)V");
+ FIND_METHOD (draw_line, "drawLine",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;IIII)V");
+ FIND_METHOD (draw_point, "drawPoint",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;II)V");
+ FIND_METHOD (copy_area, "copyArea",
+ "(Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsDrawable;"
+ "Lorg/gnu/emacs/EmacsGC;IIIIII)V");
+ FIND_METHOD (clear_window, "clearWindow",
+ "(Lorg/gnu/emacs/EmacsWindow;)V");
+ FIND_METHOD (clear_area, "clearArea",
+ "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
+ FIND_METHOD (ring_bell, "ringBell", "()V");
+ FIND_METHOD (query_tree, "queryTree",
+ "(Lorg/gnu/emacs/EmacsWindow;)[S");
+ FIND_METHOD (get_screen_width, "getScreenWidth", "(Z)I");
+ FIND_METHOD (get_screen_height, "getScreenHeight", "(Z)I");
+ FIND_METHOD (detect_mouse, "detectMouse", "()Z");
+ FIND_METHOD (name_keysym, "nameKeysym", "(I)Ljava/lang/String;");
+ FIND_METHOD (browse_url, "browseUrl", "(Ljava/lang/String;)"
+ "Ljava/lang/String;");
+ FIND_METHOD (restart_emacs, "restartEmacs", "()V");
+ FIND_METHOD (update_ic, "updateIC",
+ "(Lorg/gnu/emacs/EmacsWindow;IIII)V");
+ FIND_METHOD (reset_ic, "resetIC",
+ "(Lorg/gnu/emacs/EmacsWindow;I)V");
+ FIND_METHOD (open_content_uri, "openContentUri",
+ "([BZZZ)I");
+ FIND_METHOD (check_content_uri, "checkContentUri",
+ "([BZZ)Z");
+ FIND_METHOD (query_battery, "queryBattery", "()[J");
+ FIND_METHOD (display_toast, "displayToast",
+ "(Ljava/lang/String;)V");
+ FIND_METHOD (update_extracted_text, "updateExtractedText",
+ "(Lorg/gnu/emacs/EmacsWindow;"
+ "Landroid/view/inputmethod/ExtractedText;I)V");
+ FIND_METHOD (update_cursor_anchor_info, "updateCursorAnchorInfo",
+ "(Lorg/gnu/emacs/EmacsWindow;FFFF)V");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_pixmap (void)
+{
+ jclass old;
+
+ pixmap_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsPixmap");
+ eassert (pixmap_class.class);
+
+ old = pixmap_class.class;
+ pixmap_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!pixmap_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ pixmap_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ pixmap_class.class, \
+ name, signature); \
+ assert (pixmap_class.c_name);
+
+ FIND_METHOD (constructor, "<init>", "(S[IIII)V");
+ FIND_METHOD (constructor_mutable, "<init>", "(SIII)V");
+
+#undef FIND_METHOD
+}
+
+static void
+android_init_graphics_point (void)
+{
+ jclass old;
+
+ point_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "android/graphics/Point");
+ eassert (point_class.class);
+
+ old = point_class.class;
+ point_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!point_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ point_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ point_class.class, \
+ name, signature); \
+ assert (point_class.c_name);
+
+ FIND_METHOD (constructor, "<init>", "(II)V");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_drawable (void)
+{
+ jclass old;
+
+ drawable_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsDrawable");
+ eassert (drawable_class.class);
+
+ old = drawable_class.class;
+ drawable_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!drawable_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ drawable_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ drawable_class.class, \
+ name, signature); \
+ assert (drawable_class.c_name);
+
+ FIND_METHOD (get_bitmap, "getBitmap", "()Landroid/graphics/Bitmap;");
+ FIND_METHOD (damage_rect, "damageRect", "(Landroid/graphics/Rect;)V");
+#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");
+ FIND_METHOD (toggle_on_screen_keyboard,
+ "toggleOnScreenKeyboard", "(Z)V");
+ FIND_METHOD (lookup_string, "lookupString", "(I)Ljava/lang/String;");
+ FIND_METHOD (set_fullscreen, "setFullscreen", "(Z)V");
+ FIND_METHOD (change_window_background, "changeWindowBackground",
+ "(I)V");
+ FIND_METHOD (reparent_to, "reparentTo",
+ "(Lorg/gnu/emacs/EmacsWindow;II)V");
+ FIND_METHOD (map_window, "mapWindow", "()V");
+ FIND_METHOD (unmap_window, "unmapWindow", "()V");
+ FIND_METHOD (resize_window, "resizeWindow", "(II)V");
+ FIND_METHOD (move_window, "moveWindow", "(II)V");
+ FIND_METHOD (make_input_focus, "makeInputFocus", "(J)V");
+ FIND_METHOD (raise, "raise", "()V");
+ FIND_METHOD (lower, "lower", "()V");
+ FIND_METHOD (get_window_geometry, "getWindowGeometry",
+ "()[I");
+ FIND_METHOD (translate_coordinates, "translateCoordinates",
+ "(II)[I");
+ FIND_METHOD (set_dont_focus_on_map, "setDontFocusOnMap", "(Z)V");
+ FIND_METHOD (set_dont_accept_focus, "setDontAcceptFocus", "(Z)V");
+ FIND_METHOD (define_cursor, "defineCursor",
+ "(Lorg/gnu/emacs/EmacsCursor;)V");
+#undef FIND_METHOD
+}
+
+static void
+android_init_emacs_cursor (void)
+{
+ jclass old;
+
+ cursor_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsCursor");
+ eassert (cursor_class.class);
+
+ old = cursor_class.class;
+ cursor_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!cursor_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ cursor_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ cursor_class.class, \
+ name, signature); \
+ assert (cursor_class.c_name);
+
+ FIND_METHOD (constructor, "<init>", "(SI)V");
+#undef FIND_METHOD
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (initEmacs) (JNIEnv *env, jobject object, jarray argv,
+ jobject dump_file_object, jint api_level)
+{
+ /* android_emacs_init is not main, so GCC is not nice enough to add
+ the stack alignment prologue.
+
+ Unfortunately for us, dalvik on Android 4.0.x calls native code
+ with a 4 byte aligned stack, so this prologue must be inserted
+ before each function exported via JNI. */
+
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ char **c_argv;
+ jsize nelements, i;
+ jobject argument;
+ const char *c_argument;
+ char *dump_file;
+
+ /* Set the Android API level. */
+ android_api_level = api_level;
+
+ android_java_env = env;
+
+ nelements = (*env)->GetArrayLength (env, argv);
+ c_argv = alloca (sizeof *c_argv * nelements);
+
+ for (i = 0; i < nelements; ++i)
+ {
+ argument = (*env)->GetObjectArrayElement (env, argv, i);
+ c_argument = (*env)->GetStringUTFChars (env, (jstring) argument,
+ NULL);
+
+ if (!c_argument)
+ emacs_abort ();
+
+ /* Note that c_argument is in ``modified UTF-8 encoding'', but
+ we don't care as NUL bytes are not being specified inside. */
+ c_argv[i] = alloca (strlen (c_argument) + 1);
+ strcpy (c_argv[i], c_argument);
+ (*env)->ReleaseStringUTFChars (env, (jstring) argument, c_argument);
+ }
+
+ android_init_emacs_service ();
+ android_init_emacs_pixmap ();
+ android_init_graphics_point ();
+ android_init_emacs_drawable ();
+ android_init_emacs_window ();
+ android_init_emacs_cursor ();
+
+ /* Set HOME to the app data directory. */
+ setenv ("HOME", android_files_dir, 1);
+
+ /* Set TMPDIR to the temporary files directory. */
+ setenv ("TMPDIR", android_cache_dir, 1);
+
+ /* Set the cwd to that directory as well. */
+ if (chdir (android_files_dir))
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "chdir: %s", strerror (errno));
+
+ /* Initialize the Android GUI as long as the service object was
+ set. */
+
+ if (emacs_service)
+ android_init_gui = true;
+
+ /* Now see if a dump file has been specified and should be used. */
+ dump_file = NULL;
+
+ if (dump_file_object)
+ {
+ c_argument
+ = (*env)->GetStringUTFChars (env, (jstring) dump_file_object,
+ NULL);
+
+ /* Copy the Java string data once. */
+ dump_file = strdup (c_argument);
+
+ /* Release the Java string data. */
+ (*env)->ReleaseStringUTFChars (env, (jstring) dump_file_object,
+ c_argument);
+ }
+
+ /* Delete local references to objects that are no longer needed. */
+ ANDROID_DELETE_LOCAL_REF (argv);
+ ANDROID_DELETE_LOCAL_REF (dump_file_object);
+
+ android_emacs_init (nelements, c_argv, dump_file);
+ /* android_emacs_init should never return. */
+ emacs_abort ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (emacsAbort) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ emacs_abort ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (quit) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ /* Raise sigio to interrupt anything that could be reading
+ input. */
+ Vquit_flag = Qt;
+ raise (SIGIO);
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendConfigureNotify) (JNIEnv *env, jobject object,
+ jshort window, jlong time,
+ jint x, jint y, jint width,
+ jint height)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xconfigure.type = ANDROID_CONFIGURE_NOTIFY;
+ event.xconfigure.serial = ++event_serial;
+ event.xconfigure.window = window;
+ event.xconfigure.time = time;
+ event.xconfigure.x = x;
+ event.xconfigure.y = y;
+ event.xconfigure.width = width;
+ event.xconfigure.height = height;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendKeyPress) (JNIEnv *env, jobject object,
+ jshort window, jlong time,
+ jint state, jint keycode,
+ jint unicode_char)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xkey.type = ANDROID_KEY_PRESS;
+ event.xkey.serial = ++event_serial;
+ event.xkey.window = window;
+ event.xkey.time = time;
+ event.xkey.state = state;
+ event.xkey.keycode = keycode;
+ event.xkey.unicode_char = unicode_char;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendKeyRelease) (JNIEnv *env, jobject object,
+ jshort window, jlong time,
+ jint state, jint keycode,
+ jint unicode_char)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xkey.type = ANDROID_KEY_RELEASE;
+ event.xkey.serial = ++event_serial;
+ event.xkey.window = window;
+ event.xkey.time = time;
+ event.xkey.state = state;
+ event.xkey.keycode = keycode;
+ event.xkey.unicode_char = unicode_char;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendFocusIn) (JNIEnv *env, jobject object,
+ jshort window, jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xfocus.type = ANDROID_FOCUS_IN;
+ event.xfocus.serial = ++event_serial;
+ event.xfocus.window = window;
+ event.xfocus.time = time;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendFocusOut) (JNIEnv *env, jobject object,
+ jshort window, jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xfocus.type = ANDROID_FOCUS_OUT;
+ event.xfocus.serial = ++event_serial;
+ event.xfocus.window = window;
+ event.xfocus.time = time;
+
+ android_write_event (&event);
+ return ++event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendWindowAction) (JNIEnv *env, jobject object,
+ jshort window, jint action)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xaction.type = ANDROID_WINDOW_ACTION;
+ event.xaction.serial = ++event_serial;
+ event.xaction.window = window;
+ event.xaction.action = action;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendEnterNotify) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xcrossing.type = ANDROID_ENTER_NOTIFY;
+ event.xcrossing.serial = ++event_serial;
+ event.xcrossing.window = window;
+ event.xcrossing.x = x;
+ event.xcrossing.y = y;
+ event.xcrossing.time = time;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendLeaveNotify) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xcrossing.type = ANDROID_LEAVE_NOTIFY;
+ event.xcrossing.serial = ++event_serial;
+ event.xcrossing.window = window;
+ event.xcrossing.x = x;
+ event.xcrossing.y = y;
+ event.xcrossing.time = time;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendMotionNotify) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xmotion.type = ANDROID_MOTION_NOTIFY;
+ event.xmotion.serial = ++event_serial;
+ event.xmotion.window = window;
+ event.xmotion.x = x;
+ event.xmotion.y = y;
+ event.xmotion.time = time;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendButtonPress) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint state,
+ jint button)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xbutton.type = ANDROID_BUTTON_PRESS;
+ event.xbutton.serial = ++event_serial;
+ event.xbutton.window = window;
+ event.xbutton.x = x;
+ event.xbutton.y = y;
+ event.xbutton.time = time;
+ event.xbutton.state = state;
+ event.xbutton.button = button;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendButtonRelease) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint state,
+ jint button)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xbutton.type = ANDROID_BUTTON_RELEASE;
+ event.xbutton.serial = ++event_serial;
+ event.xbutton.window = window;
+ event.xbutton.x = x;
+ event.xbutton.y = y;
+ event.xbutton.time = time;
+ event.xbutton.state = state;
+ event.xbutton.button = button;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendTouchDown) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint pointer_id)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.touch.type = ANDROID_TOUCH_DOWN;
+ event.touch.serial = ++event_serial;
+ event.touch.window = window;
+ event.touch.x = x;
+ event.touch.y = y;
+ event.touch.time = time;
+ event.touch.pointer_id = pointer_id;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendTouchUp) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint pointer_id)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.touch.type = ANDROID_TOUCH_UP;
+ event.touch.serial = ++event_serial;
+ event.touch.window = window;
+ event.touch.x = x;
+ event.touch.y = y;
+ event.touch.time = time;
+ event.touch.pointer_id = pointer_id;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendTouchMove) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint pointer_id)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.touch.type = ANDROID_TOUCH_MOVE;
+ event.touch.serial = ++event_serial;
+ event.touch.window = window;
+ event.touch.x = x;
+ event.touch.y = y;
+ event.touch.time = time;
+ event.touch.pointer_id = pointer_id;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendWheel) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jlong time, jint state,
+ jfloat x_delta, jfloat y_delta)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.wheel.type = ANDROID_WHEEL;
+ event.wheel.serial = ++event_serial;
+ event.wheel.window = window;
+ event.wheel.x = x;
+ event.wheel.y = y;
+ event.wheel.time = time;
+ event.wheel.state = state;
+ event.wheel.x_delta = x_delta;
+ event.wheel.y_delta = y_delta;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendIconified) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.iconified.type = ANDROID_ICONIFIED;
+ event.iconified.serial = ++event_serial;
+ event.iconified.window = window;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendDeiconified) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.iconified.type = ANDROID_DEICONIFIED;
+ event.iconified.serial = ++event_serial;
+ event.iconified.window = window;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendContextMenu) (JNIEnv *env, jobject object,
+ jshort window, jint menu_event_id,
+ jint menu_event_serial)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.menu.type = ANDROID_CONTEXT_MENU;
+ event.menu.serial = ++event_serial;
+ event.menu.window = window;
+ event.menu.menu_event_id = menu_event_id;
+ event.menu.menu_event_serial = menu_event_serial;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jlong JNICALL
+NATIVE_NAME (sendExpose) (JNIEnv *env, jobject object,
+ jshort window, jint x, jint y,
+ jint width, jint height)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.xexpose.type = ANDROID_EXPOSE;
+ event.xexpose.serial = ++event_serial;
+ event.xexpose.window = window;
+ event.xexpose.x = x;
+ event.xexpose.y = y;
+ event.xexpose.width = width;
+ event.xexpose.height = height;
+
+ android_write_event (&event);
+ return event_serial;
+}
+
+JNIEXPORT jboolean JNICALL
+NATIVE_NAME (shouldForwardMultimediaButtons) (JNIEnv *env,
+ jobject object)
+{
+ /* Yes, android_pass_multimedia_buttons_to_system is being
+ read from the UI thread. */
+ return !android_pass_multimedia_buttons_to_system;
+}
+
+/* Forward declarations of deadlock prevention functions. */
+
+static void android_begin_query (void);
+static void android_end_query (void);
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (beginSynchronous) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ android_begin_query ();
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (endSynchronous) (JNIEnv *env, jobject object)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ android_end_query ();
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#else
+#pragma GCC diagnostic pop
+#endif
+
+
+
+/* Java functions called by C.
+
+ Because all C code runs in the native function initEmacs, ALL LOCAL
+ REFERENCES WILL PERSIST!
+
+ This means that every local reference must be explicitly destroyed
+ with DeleteLocalRef. A helper macro is provided to do this. */
+
+struct android_handle_entry
+{
+ /* The type. */
+ enum android_handle_type type;
+
+ /* The handle. */
+ jobject handle;
+};
+
+/* Table of handles MAX_HANDLE long. */
+struct android_handle_entry android_handles[USHRT_MAX];
+
+/* The largest handle ID currently known, but subject to
+ wraparound. */
+static android_handle max_handle;
+
+/* Allocate a new, unused, handle identifier. If Emacs is out of
+ identifiers, return 0. */
+
+static android_handle
+android_alloc_id (void)
+{
+ android_handle handle;
+
+ /* 0 is never a valid handle ID. */
+
+ if (!max_handle)
+ max_handle++;
+
+ /* See if the handle is already occupied. */
+
+ if (android_handles[max_handle].handle)
+ {
+ /* Look for a fresh unoccupied handle. */
+
+ handle = max_handle;
+ max_handle++;
+
+ while (handle != max_handle)
+ {
+ ++max_handle;
+
+ /* Make sure the handle is valid. */
+ if (!max_handle)
+ ++max_handle;
+
+ if (!android_handles[max_handle].handle)
+ return max_handle++;
+ }
+
+ return ANDROID_NONE;
+ }
+
+ return max_handle++;
+}
+
+/* Destroy the specified handle and mark it as free on the Java side
+ as well. */
+
+static void
+android_destroy_handle (android_handle handle)
+{
+ static jclass old, class;
+ static jmethodID method;
+
+ if (!android_handles[handle].handle)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to destroy free handle!");
+ emacs_abort ();
+ }
+
+ if (!class)
+ {
+ class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsHandleObject");
+ assert (class != NULL);
+
+ method
+ = (*android_java_env)->GetMethodID (android_java_env, class,
+ "destroyHandle", "()V");
+ assert (method != NULL);
+
+ old = class;
+ class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) class);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+ }
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ android_handles[handle].handle,
+ method);
+
+ /* Just clear any exception thrown. If destroying the handle
+ fails from an out-of-memory error, then Emacs loses some
+ resources, but that is not as big deal as signalling. */
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ /* Delete the global reference regardless of any error. */
+ (*android_java_env)->DeleteGlobalRef (android_java_env,
+ android_handles[handle].handle);
+ android_handles[handle].handle = NULL;
+}
+
+jobject
+android_resolve_handle (android_handle handle,
+ enum android_handle_type type)
+{
+ if (!handle)
+ /* ANDROID_NONE. */
+ return NULL;
+
+ /* CheckJNI will normally ensure that the handle exists and is
+ the right type, but with a less informative error message.
+ Don't waste cycles doing our own checking here. */
+
+#ifdef ENABLE_CHECKING
+
+ if (!android_handles[handle].handle)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to resolve free handle!");
+ emacs_abort ();
+ }
+
+ if (android_handles[handle].type != type)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Handle has wrong type!");
+ emacs_abort ();
+ }
+
+#endif /* ENABLE_CHECKING */
+
+ return android_handles[handle].handle;
+}
+
+static jobject
+android_resolve_handle2 (android_handle handle,
+ enum android_handle_type type,
+ enum android_handle_type type2)
+{
+ if (!handle)
+ return NULL;
+
+ /* CheckJNI will normally ensure that the handle exists and is
+ the right type, but with a less informative error message.
+ Don't waste cycles doing our own checking here. */
+
+#ifdef ENABLE_CHECKING
+
+ if (!android_handles[handle].handle)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to resolve free handle!");
+ emacs_abort ();
+ }
+
+ if (android_handles[handle].type != type
+ && android_handles[handle].type != type2)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Handle has wrong type!");
+ emacs_abort ();
+ }
+
+#endif /* ENABLE_CHECKING */
+
+ return android_handles[handle].handle;
+}
+
+void
+android_change_window_attributes (android_window handle,
+ enum android_window_value_mask value_mask,
+ struct android_set_window_attributes *attrs)
+{
+ jmethodID method;
+ jobject window;
+ jint pixel;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ if (value_mask & ANDROID_CW_BACK_PIXEL)
+ {
+ method = window_class.change_window_background;
+ pixel = (jint) attrs->background_pixel;
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ method, pixel);
+ android_exception_check ();
+ }
+}
+
+/* Create a new window with the given width, height and
+ attributes. */
+
+android_window
+android_create_window (android_window parent, int x, int y,
+ int width, int height,
+ enum android_window_value_mask value_mask,
+ struct android_set_window_attributes *attrs)
+{
+ static jclass class;
+ static jmethodID constructor;
+ jobject object, parent_object, old;
+ android_window window;
+ android_handle prev_max_handle;
+ bool override_redirect;
+
+ parent_object = android_resolve_handle (parent, ANDROID_HANDLE_WINDOW);
+
+ prev_max_handle = max_handle;
+ window = android_alloc_id ();
+
+ if (!window)
+ error ("Out of window handles!");
+
+ if (!class)
+ {
+ class = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsWindow");
+ assert (class != NULL);
+
+ constructor
+ = (*android_java_env)->GetMethodID (android_java_env, class, "<init>",
+ "(SLorg/gnu/emacs/EmacsWindow;"
+ "IIIIZ)V");
+ assert (constructor != NULL);
+
+ old = class;
+ class = (*android_java_env)->NewGlobalRef (android_java_env, class);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+ }
+
+ /* N.B. that ANDROID_CW_OVERRIDE_REDIRECT can only be set at window
+ creation time. */
+ override_redirect = ((value_mask
+ & ANDROID_CW_OVERRIDE_REDIRECT)
+ && attrs->override_redirect);
+
+ object = (*android_java_env)->NewObject (android_java_env, class,
+ constructor, (jshort) window,
+ parent_object, (jint) x, (jint) y,
+ (jint) width, (jint) height,
+ (jboolean) override_redirect);
+ if (!object)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[window].type = ANDROID_HANDLE_WINDOW;
+ android_handles[window].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env,
+ object);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[window].handle)
+ memory_full (0);
+
+ android_change_window_attributes (window, value_mask, attrs);
+ return window;
+}
+
+void
+android_set_window_background (android_window window, unsigned long pixel)
+{
+ struct android_set_window_attributes attrs;
+
+ attrs.background_pixel = pixel;
+ android_change_window_attributes (window, ANDROID_CW_BACK_PIXEL,
+ &attrs);
+}
+
+void
+android_destroy_window (android_window window)
+{
+ if (android_handles[window].type != ANDROID_HANDLE_WINDOW)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to destroy something not a window!");
+ emacs_abort ();
+ }
+
+ android_destroy_handle (window);
+}
+
+static void
+android_init_android_rect_class (void)
+{
+ jclass old;
+
+ if (android_rect_class)
+ /* Already initialized. */
+ return;
+
+ android_rect_class
+ = (*android_java_env)->FindClass (android_java_env,
+ "android/graphics/Rect");
+ assert (android_rect_class);
+
+ android_rect_constructor
+ = (*android_java_env)->GetMethodID (android_java_env, android_rect_class,
+ "<init>", "(IIII)V");
+ assert (emacs_gc_constructor);
+
+ old = android_rect_class;
+ android_rect_class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) android_rect_class);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+}
+
+static void
+android_init_emacs_gc_class (void)
+{
+ jclass old;
+
+ if (emacs_gc_class)
+ /* Already initialized. */
+ return;
+
+ emacs_gc_class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsGC");
+ assert (emacs_gc_class);
+
+ emacs_gc_constructor
+ = (*android_java_env)->GetMethodID (android_java_env,
+ emacs_gc_class,
+ "<init>", "(S)V");
+ assert (emacs_gc_constructor);
+
+ emacs_gc_mark_dirty
+ = (*android_java_env)->GetMethodID (android_java_env,
+ emacs_gc_class,
+ "markDirty", "(Z)V");
+ assert (emacs_gc_mark_dirty);
+
+ old = emacs_gc_class;
+ emacs_gc_class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) emacs_gc_class);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ emacs_gc_foreground
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "foreground", "I");
+ emacs_gc_background
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "background", "I");
+ emacs_gc_function
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "function", "I");
+ emacs_gc_clip_rects
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "clip_rects",
+ "[Landroid/graphics/Rect;");
+ emacs_gc_clip_x_origin
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "clip_x_origin", "I");
+ emacs_gc_clip_y_origin
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "clip_y_origin", "I");
+ emacs_gc_stipple
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "stipple",
+ "Lorg/gnu/emacs/EmacsPixmap;");
+ emacs_gc_clip_mask
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "clip_mask",
+ "Lorg/gnu/emacs/EmacsPixmap;");
+ emacs_gc_fill_style
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "fill_style", "I");
+ emacs_gc_ts_origin_x
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "ts_origin_x", "I");
+ emacs_gc_ts_origin_y
+ = (*android_java_env)->GetFieldID (android_java_env,
+ emacs_gc_class,
+ "ts_origin_y", "I");
+}
+
+struct android_gc *
+android_create_gc (enum android_gc_value_mask mask,
+ struct android_gc_values *values)
+{
+ struct android_gc *gc;
+ android_handle prev_max_handle;
+ jobject object;
+
+ android_init_emacs_gc_class ();
+
+ gc = xmalloc (sizeof *gc);
+ prev_max_handle = max_handle;
+ gc->gcontext = android_alloc_id ();
+ gc->foreground = 0;
+ gc->background = 0xffffff;
+ gc->clip_rects = NULL;
+
+ /* This means to not apply any clipping. */
+ gc->num_clip_rects = -1;
+
+ if (!gc->gcontext)
+ {
+ xfree (gc);
+ error ("Out of GContext handles!");
+ }
+
+ object = (*android_java_env)->NewObject (android_java_env,
+ emacs_gc_class,
+ emacs_gc_constructor,
+ (jshort) gc->gcontext);
+
+ if (!object)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[gc->gcontext].type = ANDROID_HANDLE_GCONTEXT;
+ android_handles[gc->gcontext].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env, object);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[gc->gcontext].handle)
+ memory_full (0);
+
+ android_change_gc (gc, mask, values);
+ return gc;
+}
+
+void
+android_free_gc (struct android_gc *gc)
+{
+ android_destroy_handle (gc->gcontext);
+
+ xfree (gc->clip_rects);
+ xfree (gc);
+}
+
+void
+android_change_gc (struct android_gc *gc,
+ enum android_gc_value_mask mask,
+ 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,
+ ANDROID_HANDLE_GCONTEXT);
+
+ if (mask & ANDROID_GC_FOREGROUND)
+ {
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_foreground,
+ values->foreground);
+ gc->foreground = values->foreground;
+ }
+
+ if (mask & ANDROID_GC_BACKGROUND)
+ {
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_background,
+ values->background);
+ gc->background = values->background;
+ }
+
+ if (mask & ANDROID_GC_FUNCTION)
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_function,
+ 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);
+ 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);
+ clip_changed = true;
+ }
+
+ if (mask & ANDROID_GC_CLIP_MASK)
+ {
+ what = android_resolve_handle (values->clip_mask,
+ ANDROID_HANDLE_PIXMAP);
+ (*android_java_env)->SetObjectField (android_java_env,
+ gcontext,
+ emacs_gc_clip_mask,
+ what);
+
+ /* Changing GCClipMask also clears the clip rectangles. */
+ (*android_java_env)->SetObjectField (android_java_env,
+ gcontext,
+ emacs_gc_clip_rects,
+ NULL);
+
+ xfree (gc->clip_rects);
+ gc->clip_rects = NULL;
+ gc->num_clip_rects = -1;
+ clip_changed = true;
+ }
+
+ if (mask & ANDROID_GC_STIPPLE)
+ {
+ what = android_resolve_handle (values->stipple,
+ ANDROID_HANDLE_PIXMAP);
+ (*android_java_env)->SetObjectField (android_java_env,
+ gcontext,
+ emacs_gc_stipple,
+ what);
+ }
+
+ if (mask & ANDROID_GC_FILL_STYLE)
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_fill_style,
+ values->fill_style);
+
+ if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN)
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_ts_origin_x,
+ values->ts_x_origin);
+
+ if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN)
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_ts_origin_y,
+ values->ts_y_origin);
+
+ if (mask)
+ {
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ gcontext,
+ emacs_gc_class,
+ emacs_gc_mark_dirty,
+ (jboolean) clip_changed);
+ android_exception_check ();
+ }
+}
+
+void
+android_set_clip_rectangles (struct android_gc *gc, int clip_x_origin,
+ int clip_y_origin,
+ struct android_rectangle *clip_rects,
+ int n_clip_rects)
+{
+ jobjectArray array;
+ jobject rect, gcontext;
+ int i;
+
+ android_init_android_rect_class ();
+ android_init_emacs_gc_class ();
+
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ array = (*android_java_env)->NewObjectArray (android_java_env,
+ n_clip_rects,
+ android_rect_class,
+ NULL);
+ android_exception_check ();
+
+ for (i = 0; i < n_clip_rects; ++i)
+ {
+ rect = (*android_java_env)->NewObject (android_java_env,
+ android_rect_class,
+ android_rect_constructor,
+ (jint) clip_rects[i].x,
+ (jint) clip_rects[i].y,
+ (jint) (clip_rects[i].x
+ + clip_rects[i].width),
+ (jint) (clip_rects[i].y
+ + clip_rects[i].height));
+
+ /* The meaning of this call is to check whether or not an
+ allocation error happened, and to delete ARRAY and signal an
+ out-of-memory error if that is the case. */
+ android_exception_check_1 (array);
+
+ (*android_java_env)->SetObjectArrayElement (android_java_env,
+ array, i, rect);
+ ANDROID_DELETE_LOCAL_REF (rect);
+ }
+
+ (*android_java_env)->SetObjectField (android_java_env,
+ gcontext,
+ emacs_gc_clip_rects,
+ (jobject) array);
+ ANDROID_DELETE_LOCAL_REF (array);
+
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_clip_x_origin,
+ clip_x_origin);
+ (*android_java_env)->SetIntField (android_java_env,
+ gcontext,
+ emacs_gc_clip_y_origin,
+ clip_y_origin);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ gcontext,
+ emacs_gc_class,
+ emacs_gc_mark_dirty,
+ (jboolean) true);
+ android_exception_check ();
+
+ /* Cache the clip rectangles on the C side for
+ sfntfont-android.c. */
+ if (gc->clip_rects)
+ xfree (gc->clip_rects);
+
+ /* If gc->num_clip_rects is 0, then no drawing will be performed at
+ all. */
+ gc->clip_rects = xmalloc (sizeof *gc->clip_rects
+ * n_clip_rects);
+ gc->num_clip_rects = n_clip_rects;
+ memcpy (gc->clip_rects, clip_rects,
+ n_clip_rects * sizeof *gc->clip_rects);
+}
+
+void
+android_reparent_window (android_window w, android_window parent_handle,
+ int x, int y)
+{
+ jobject window, parent;
+ jmethodID method;
+
+ window = android_resolve_handle (w, ANDROID_HANDLE_WINDOW);
+ parent = android_resolve_handle (parent_handle,
+ ANDROID_HANDLE_WINDOW);
+
+ method = window_class.reparent_to;
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window,
+ window_class.class, method,
+ parent, (jint) x, (jint) y);
+ android_exception_check ();
+}
+
+void
+android_clear_window (android_window handle)
+{
+ jobject window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.clear_window,
+ window);
+ android_exception_check ();
+}
+
+void
+android_map_window (android_window handle)
+{
+ jobject window;
+ jmethodID map_window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ map_window = window_class.map_window;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ map_window);
+ android_exception_check ();
+}
+
+void
+android_unmap_window (android_window handle)
+{
+ jobject window;
+ jmethodID unmap_window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ unmap_window = window_class.unmap_window;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ unmap_window);
+ android_exception_check ();
+}
+
+void
+android_resize_window (android_window handle, unsigned int width,
+ unsigned int height)
+{
+ jobject window;
+ jmethodID resize_window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ resize_window = window_class.resize_window;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ resize_window,
+ (jint) width,
+ (jint) height);
+ android_exception_check ();
+}
+
+void
+android_move_window (android_window handle, int x, int y)
+{
+ jobject window;
+ jmethodID move_window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ move_window = window_class.move_window;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ move_window,
+ (jint) x, (jint) y);
+ android_exception_check ();
+}
+
+void
+android_swap_buffers (struct android_swap_info *swap_info,
+ int num_windows)
+{
+ jobject window;
+ int i;
+
+ for (i = 0; i < num_windows; ++i)
+ {
+ window = android_resolve_handle (swap_info[i].swap_window,
+ ANDROID_HANDLE_WINDOW);
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ window_class.swap_buffers);
+ android_exception_check ();
+ }
+}
+
+void
+android_get_gc_values (struct android_gc *gc,
+ enum android_gc_value_mask mask,
+ struct android_gc_values *values)
+{
+ jobject gcontext;
+
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ if (mask & ANDROID_GC_FOREGROUND)
+ /* GCs never have 32 bit colors, so we don't have to worry about
+ sign extension here. */
+ values->foreground = gc->foreground;
+
+ if (mask & ANDROID_GC_BACKGROUND)
+ values->background = gc->background;
+
+ if (mask & ANDROID_GC_FUNCTION)
+ values->function
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_function);
+
+ if (mask & ANDROID_GC_CLIP_X_ORIGIN)
+ values->clip_x_origin
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_clip_x_origin);
+
+ if (mask & ANDROID_GC_CLIP_Y_ORIGIN)
+ values->clip_y_origin
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_clip_y_origin);
+
+ if (mask & ANDROID_GC_FILL_STYLE)
+ values->fill_style
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_fill_style);
+
+ if (mask & ANDROID_GC_TILE_STIP_X_ORIGIN)
+ values->ts_x_origin
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_ts_origin_x);
+
+ if (mask & ANDROID_GC_TILE_STIP_Y_ORIGIN)
+ values->ts_y_origin
+ = (*android_java_env)->GetIntField (android_java_env,
+ gcontext,
+ emacs_gc_ts_origin_y);
+
+ /* Fields involving handles are not used by Emacs, and thus not
+ implemented */
+}
+
+void
+android_set_foreground (struct android_gc *gc, unsigned long foreground)
+{
+ struct android_gc_values gcv;
+
+ gcv.foreground = foreground;
+ android_change_gc (gc, ANDROID_GC_FOREGROUND, &gcv);
+}
+
+void
+android_fill_rectangle (android_drawable handle, struct android_gc *gc,
+ int x, int y, unsigned int width,
+ unsigned int height)
+{
+ jobject drawable, gcontext;
+
+ drawable = android_resolve_handle2 (handle,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.fill_rectangle,
+ drawable,
+ gcontext,
+ (jint) x, (jint) y,
+ (jint) width,
+ (jint) height);
+}
+
+android_pixmap
+android_create_pixmap_from_bitmap_data (char *data, unsigned int width,
+ unsigned int height,
+ unsigned long foreground,
+ unsigned long background,
+ unsigned int depth)
+{
+ android_handle prev_max_handle;
+ jobject object;
+ jintArray colors;
+ android_pixmap pixmap;
+ unsigned int x, y;
+ jint *region;
+
+ USE_SAFE_ALLOCA;
+
+ /* Create the color array holding the data. */
+ colors = (*android_java_env)->NewIntArray (android_java_env,
+ width * height);
+ android_exception_check ();
+
+ SAFE_NALLOCA (region, sizeof *region, width);
+
+ for (y = 0; y < height; ++y)
+ {
+ for (x = 0; x < width; ++x)
+ {
+ if (depth == 24)
+ {
+ /* The alpha channels must be set, or otherwise, the
+ pixmap will be created entirely transparent. */
+
+ if (data[x / 8] & (1 << (x % 8)))
+ region[x] = foreground | 0xff000000;
+ else
+ region[x] = background | 0xff000000;
+ }
+ else
+ {
+ if (data[x / 8] & (1 << (x % 8)))
+ region[x] = foreground;
+ else
+ region[x] = background;
+ }
+ }
+
+ (*android_java_env)->SetIntArrayRegion (android_java_env,
+ colors,
+ width * y, width,
+ region);
+ data += width / 8;
+ }
+
+ /* First, allocate the pixmap handle. */
+ prev_max_handle = max_handle;
+ pixmap = android_alloc_id ();
+
+ if (!pixmap)
+ {
+ ANDROID_DELETE_LOCAL_REF ((jobject) colors);
+ error ("Out of pixmap handles!");
+ }
+
+ object = (*android_java_env)->NewObject (android_java_env,
+ pixmap_class.class,
+ pixmap_class.constructor,
+ (jshort) pixmap, colors,
+ (jint) width, (jint) height,
+ (jint) depth);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF ((jobject) colors);
+
+ if (!object)
+ {
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
+ android_handles[pixmap].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env, object);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[pixmap].handle)
+ memory_full (0);
+
+ SAFE_FREE ();
+ return pixmap;
+}
+
+void
+android_set_clip_mask (struct android_gc *gc, android_pixmap pixmap)
+{
+ struct android_gc_values gcv;
+
+ gcv.clip_mask = pixmap;
+ android_change_gc (gc, ANDROID_GC_CLIP_MASK, &gcv);
+}
+
+void
+android_set_fill_style (struct android_gc *gc,
+ enum android_fill_style fill_style)
+{
+ struct android_gc_values gcv;
+
+ gcv.fill_style = fill_style;
+ android_change_gc (gc, ANDROID_GC_FILL_STYLE, &gcv);
+}
+
+void
+android_copy_area (android_drawable src, android_drawable dest,
+ struct android_gc *gc, int src_x, int src_y,
+ unsigned int width, unsigned int height,
+ int dest_x, int dest_y)
+{
+ jobject src_object, dest_object, gcontext;
+
+ src_object = android_resolve_handle2 (src, ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ dest_object = android_resolve_handle2 (dest, ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.copy_area,
+ src_object,
+ dest_object,
+ gcontext,
+ (jint) src_x, (jint) src_y,
+ (jint) width, (jint) height,
+ (jint) dest_x, (jint) dest_y);
+ android_exception_check ();
+}
+
+void
+android_free_pixmap (android_pixmap pixmap)
+{
+ android_destroy_handle (pixmap);
+}
+
+void
+android_set_background (struct android_gc *gc, unsigned long background)
+{
+ struct android_gc_values gcv;
+
+ gcv.background = background;
+ android_change_gc (gc, ANDROID_GC_BACKGROUND, &gcv);
+}
+
+void
+android_fill_polygon (android_drawable drawable, struct android_gc *gc,
+ struct android_point *points, int npoints,
+ enum android_shape shape, enum android_coord_mode mode)
+{
+ jobjectArray array;
+ jobject point, drawable_object, gcontext;
+ int i;
+
+ drawable_object = android_resolve_handle2 (drawable,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ array = (*android_java_env)->NewObjectArray (android_java_env,
+ npoints,
+ point_class.class,
+ NULL);
+ android_exception_check ();
+
+ for (i = 0; i < npoints; ++i)
+ {
+ point = (*android_java_env)->NewObject (android_java_env,
+ point_class.class,
+ point_class.constructor,
+ (jint) points[i].x,
+ (jint) points[i].y);
+ android_exception_check_1 (array);
+
+ (*android_java_env)->SetObjectArrayElement (android_java_env,
+ array, i, point);
+ ANDROID_DELETE_LOCAL_REF (point);
+ }
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.fill_polygon,
+ drawable_object,
+ gcontext, array);
+ ANDROID_DELETE_LOCAL_REF (array);
+}
+
+void
+android_draw_rectangle (android_drawable handle, struct android_gc *gc,
+ int x, int y, unsigned int width, unsigned int height)
+{
+ jobject drawable, gcontext;
+
+ drawable = android_resolve_handle2 (handle,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.draw_rectangle,
+ drawable, gcontext,
+ (jint) x, (jint) y,
+ (jint) width, (jint) height);
+}
+
+void
+android_draw_point (android_drawable handle, struct android_gc *gc,
+ int x, int y)
+{
+ jobject drawable, gcontext;
+
+ drawable = android_resolve_handle2 (handle,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.draw_point,
+ drawable, gcontext,
+ (jint) x, (jint) y);
+}
+
+void
+android_draw_line (android_drawable handle, struct android_gc *gc,
+ int x, int y, int x2, int y2)
+{
+ jobject drawable, gcontext;
+
+ drawable = android_resolve_handle2 (handle,
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+ gcontext = android_resolve_handle (gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.draw_line,
+ drawable, gcontext,
+ (jint) x, (jint) y,
+ (jint) x2, (jint) y2);
+}
+
+android_pixmap
+android_create_pixmap (unsigned int width, unsigned int height,
+ int depth)
+{
+ android_handle prev_max_handle;
+ jobject object;
+ android_pixmap pixmap;
+
+ /* First, allocate the pixmap handle. */
+ prev_max_handle = max_handle;
+ pixmap = android_alloc_id ();
+
+ if (!pixmap)
+ error ("Out of pixmap handles!");
+
+ object = (*android_java_env)->NewObject (android_java_env,
+ pixmap_class.class,
+ pixmap_class.constructor_mutable,
+ (jshort) pixmap,
+ (jint) width, (jint) height,
+ (jint) depth);
+
+ if (!object)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[pixmap].type = ANDROID_HANDLE_PIXMAP;
+ android_handles[pixmap].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env, object);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[pixmap].handle)
+ memory_full (0);
+
+ return pixmap;
+}
+
+void
+android_set_ts_origin (struct android_gc *gc, int x, int y)
+{
+ struct android_gc_values gcv;
+
+ gcv.ts_x_origin = x;
+ gcv.ts_y_origin = y;
+ android_change_gc (gc, (ANDROID_GC_TILE_STIP_X_ORIGIN
+ | ANDROID_GC_TILE_STIP_Y_ORIGIN),
+ &gcv);
+}
+
+void
+android_clear_area (android_window handle, int x, int y,
+ unsigned int width, unsigned int height)
+{
+ jobject window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.clear_area,
+ window, (jint) x, (jint) y,
+ (jint) width, (jint) height);
+}
+
+android_pixmap
+android_create_bitmap_from_data (char *bits, unsigned int width,
+ unsigned int height)
+{
+ return android_create_pixmap_from_bitmap_data (bits, 1, 0,
+ width, height, 1);
+}
+
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+ char *data, unsigned int width, unsigned int height)
+{
+ struct android_image *image;
+
+ image = xmalloc (sizeof *image);
+
+ /* Fill in the fields required by image.c. N.B. that
+ android_destroy_image ostensibly will free data, but image.c
+ mostly sets and frees data itself. */
+ image->width = width;
+ image->height = height;
+ image->data = data;
+ image->depth = depth;
+ image->format = format;
+
+ /* Now fill in the image dimensions. There are only two depths
+ supported by this function. */
+
+ if (depth == 1)
+ {
+ image->bytes_per_line = (width + 7) / 8;
+ image->bits_per_pixel = 1;
+ }
+ else if (depth == 24)
+ {
+ image->bytes_per_line = width * 4;
+ image->bits_per_pixel = 32;
+ }
+ else
+ emacs_abort ();
+
+ return image;
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+ /* If XIMG->data is NULL, then it has already been freed by
+ image.c. */
+
+ if (ximg->data)
+ xfree (ximg->data);
+ xfree (ximg);
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+ unsigned long pixel)
+{
+ char *byte, *word;
+ unsigned int r, g, b;
+ unsigned int pixel_int;
+
+ /* Ignore out-of-bounds accesses. */
+
+ if (x >= ximg->width || y >= ximg->height || x < 0 || y < 0)
+ return;
+
+ switch (ximg->depth)
+ {
+ case 1:
+ byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+
+ if (pixel)
+ *byte |= (1 << x % 8);
+ else
+ *byte &= ~(1 << x % 8);
+ break;
+
+ case 24:
+ /* Unaligned accesses are problematic on Android devices. */
+ word = ximg->data + y * ximg->bytes_per_line + x * 4;
+
+ /* Swizzle the pixel into ABGR format. Android uses Skia's
+ ``native color type'', which is ABGR. This is despite the
+ format being named ``ARGB'', and more confusingly
+ `ANDROID_BITMAP_FORMAT_RGBA_8888' in bitmap.h. */
+ r = pixel & 0x00ff0000;
+ g = pixel & 0x0000ff00;
+ b = pixel & 0x000000ff;
+ pixel = (r >> 16) | g | (b << 16) | 0xff000000;
+
+ pixel_int = pixel;
+ memcpy (word, &pixel_int, sizeof pixel_int);
+ break;
+ }
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+ char *byte, *word;
+ unsigned int pixel, r, g, b;
+
+ if (x >= ximg->width || y >= ximg->height
+ || x < 0 || y < 0)
+ return 0;
+
+ switch (ximg->depth)
+ {
+ case 1:
+ byte = ximg->data + y * ximg->bytes_per_line + x / 8;
+ return (*byte & (1 << x % 8)) ? 1 : 0;
+
+ case 24:
+ word = ximg->data + y * ximg->bytes_per_line + x * 4;
+ memcpy (&pixel, word, sizeof pixel);
+
+ /* Convert the pixel back to RGB. */
+ b = pixel & 0x00ff0000;
+ g = pixel & 0x0000ff00;
+ r = pixel & 0x000000ff;
+ pixel = ((r << 16) | g | (b >> 16)) & ~0xff000000;
+
+ return pixel;
+ }
+
+ emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable handle,
+ enum android_image_format format)
+{
+ jobject drawable, bitmap;
+ AndroidBitmapInfo bitmap_info;
+ size_t byte_size;
+ void *data;
+ struct android_image *image;
+ unsigned char *data1, *data2;
+ int i, x;
+
+ drawable = android_resolve_handle2 (handle, ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_PIXMAP);
+
+ /* Look up the drawable and get the bitmap corresponding to it.
+ Then, lock the bitmap's bits. */
+ bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+ drawable,
+ drawable_class.get_bitmap);
+ android_exception_check ();
+
+ /* Clear the bitmap info structure. */
+ memset (&bitmap_info, 0, sizeof bitmap_info);
+
+ /* The NDK doc seems to imply this function can fail but doesn't say
+ what value it gives when it does! */
+ AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+ if (!bitmap_info.stride)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ /* Compute how big the image data will be. Fail if it would be too
+ big. */
+
+ if (bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8)
+ {
+ if (INT_MULTIPLY_WRAPV ((size_t) bitmap_info.stride,
+ (size_t) bitmap_info.height,
+ &byte_size))
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+ }
+ else
+ /* This A8 image will be packed into A1 later on. */
+ byte_size = (bitmap_info.width + 7) / 8;
+
+ /* Lock the image data. Once again, the NDK documentation says the
+ call can fail, but does not say how to determine whether or not
+ it has failed, nor how the address is aligned. */
+ data = NULL;
+ AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+ if (!data)
+ {
+ /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+ failed. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ /* Copy the data into a new struct android_image. */
+ image = xmalloc (sizeof *image);
+ image->width = bitmap_info.width;
+ image->height = bitmap_info.height;
+ image->data = malloc (byte_size);
+
+ if (!image->data)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ xfree (image);
+ memory_full (byte_size);
+ }
+
+ /* Use the format of the bitmap to determine the image depth. */
+ switch (bitmap_info.format)
+ {
+ case ANDROID_BITMAP_FORMAT_RGBA_8888:
+ image->depth = 24;
+ image->bits_per_pixel = 32;
+ break;
+
+ /* A8 images are used by Emacs to represent bitmaps. They have
+ to be packed manually. */
+ case ANDROID_BITMAP_FORMAT_A_8:
+ image->depth = 1;
+ image->bits_per_pixel = 1;
+ break;
+
+ /* Other formats are currently not supported. */
+ default:
+ emacs_abort ();
+ }
+
+ image->format = format;
+
+ if (image->depth == 24)
+ {
+ image->bytes_per_line = bitmap_info.stride;
+
+ /* Copy the bitmap data over. */
+ memcpy (image->data, data, byte_size);
+ }
+ else
+ {
+ /* Pack the A8 image data into bits manually. */
+ image->bytes_per_line = (image->width + 7) / 8;
+
+ data1 = (unsigned char *) image->data;
+ data2 = data;
+
+ for (i = 0; i < image->height; ++i)
+ {
+ for (x = 0; x < image->width; ++x)
+ /* Some bits in data1 might be initialized at this point,
+ but they will all be set properly later. */
+ data1[x / 8] = (data2[x]
+ ? (data1[x / 8] | (1 << (x % 8)))
+ : (data1[x / 8] & ~(1 << (x % 8))));
+
+ data1 += image->bytes_per_line;
+ data2 += bitmap_info.stride;
+ }
+ }
+
+ /* Unlock the bitmap pixels. */
+ AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+ /* Delete the bitmap reference. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ return image;
+}
+
+void
+android_put_image (android_pixmap handle, struct android_image *image)
+{
+ jobject drawable, bitmap;
+ AndroidBitmapInfo bitmap_info;
+ void *data;
+ unsigned char *data_1, *data_2;
+ int i, x;
+
+ drawable = android_resolve_handle (handle, ANDROID_HANDLE_PIXMAP);
+
+ /* Look up the drawable and get the bitmap corresponding to it.
+ Then, lock the bitmap's bits. */
+ bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+ drawable,
+ drawable_class.get_bitmap);
+ android_exception_check ();
+
+ /* Clear the bitmap info structure. */
+ memset (&bitmap_info, 0, sizeof bitmap_info);
+
+ /* The NDK doc seems to imply this function can fail but doesn't say
+ what value it gives when it does! */
+ AndroidBitmap_getInfo (android_java_env, bitmap, &bitmap_info);
+
+ if (!bitmap_info.stride)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ if (bitmap_info.width != image->width
+ || bitmap_info.height != image->height)
+ /* This is not yet supported. */
+ emacs_abort ();
+
+ /* Make sure the bitmap formats are compatible with each other. */
+
+ if ((image->depth == 24
+ && bitmap_info.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
+ || (image->depth == 1
+ && bitmap_info.format != ANDROID_BITMAP_FORMAT_A_8))
+ emacs_abort ();
+
+ /* Lock the image data. Once again, the NDK documentation says the
+ call can fail, but does not say how to determine whether or not
+ it has failed, nor how the address is aligned. */
+ data = NULL;
+ AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+ if (!data)
+ {
+ /* Take a NULL pointer to mean that AndroidBitmap_lockPixels
+ failed. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ memory_full (0);
+ }
+
+ data_1 = data;
+ data_2 = (unsigned char *) image->data;
+
+ /* Copy the bitmap data over scanline-by-scanline. */
+ for (i = 0; i < image->height; ++i)
+ {
+ if (image->depth != 1)
+ memcpy (data_1, data_2,
+ image->width * (image->bits_per_pixel / 8));
+ else
+ {
+ /* Android internally uses a 1 byte-per-pixel format for
+ ALPHA_8 images. Expand the image from the 1
+ bit-per-pixel X format correctly. */
+
+ for (x = 0; x < image->width; ++x)
+ data_1[x] = (data_2[x / 8] & (1 << x % 8)) ? 0xff : 0;
+ }
+
+ data_1 += bitmap_info.stride;
+ data_2 += image->bytes_per_line;
+ }
+
+ /* Unlock the bitmap pixels. */
+ AndroidBitmap_unlockPixels (android_java_env, bitmap);
+
+ /* Delete the bitmap reference. */
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+}
+
+void
+android_bell (void)
+{
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.ring_bell);
+ android_exception_check ();
+}
+
+void
+android_set_input_focus (android_window handle, unsigned long time)
+{
+ jobject window;
+ jmethodID make_input_focus;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ make_input_focus = window_class.make_input_focus;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ make_input_focus,
+ (jlong) time);
+ android_exception_check ();
+}
+
+void
+android_raise_window (android_window handle)
+{
+ jobject window;
+ jmethodID raise;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ raise = window_class.raise;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ raise);
+ android_exception_check ();
+}
+
+void
+android_lower_window (android_window handle)
+{
+ jobject window;
+ jmethodID lower;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ lower = window_class.lower;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window,
+ window_class.class,
+ lower);
+ android_exception_check ();
+}
+
+int
+android_query_tree (android_window handle, android_window *root_return,
+ android_window *parent_return,
+ android_window **children_return,
+ unsigned int *nchildren_return)
+{
+ jobject window, array;
+ jsize nelements, i;
+ android_window *children;
+ jshort *shorts;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ /* window can be NULL, so this is a service method. */
+ array
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.query_tree,
+ window);
+ android_exception_check ();
+
+ /* The first element of the array is the parent window. The rest
+ are the children. */
+ nelements = (*android_java_env)->GetArrayLength (android_java_env,
+ array);
+ eassert (nelements);
+
+ /* Now fill in the children. */
+ children = xnmalloc (nelements - 1, sizeof *children);
+
+ shorts
+ = (*android_java_env)->GetShortArrayElements (android_java_env, array,
+ NULL);
+ android_exception_check_nonnull (shorts, array);
+
+ for (i = 1; i < nelements; ++i)
+ children[i] = shorts[i];
+
+ /* Finally, return the parent and other values. */
+ *root_return = 0;
+ *parent_return = shorts[0];
+ *children_return = children;
+ *nchildren_return = nelements - 1;
+
+ /* Release the array contents. */
+ (*android_java_env)->ReleaseShortArrayElements (android_java_env, array,
+ shorts, JNI_ABORT);
+
+ ANDROID_DELETE_LOCAL_REF (array);
+ return 1;
+}
+
+void
+android_get_geometry (android_window handle,
+ android_window *root_return,
+ int *x_return, int *y_return,
+ unsigned int *width_return,
+ unsigned int *height_return,
+ unsigned int *border_width_return)
+{
+ jobject window;
+ jarray window_geometry;
+ jmethodID get_geometry;
+ jint *ints;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ get_geometry = window_class.get_window_geometry;
+
+ window_geometry
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ window,
+ get_geometry);
+ android_exception_check ();
+
+ /* window_geometry is an array containing x, y, width and
+ height. border_width is always 0 on Android. */
+ eassert ((*android_java_env)->GetArrayLength (android_java_env,
+ window_geometry)
+ == 4);
+
+ *root_return = 0;
+ *border_width_return = 0;
+
+ ints
+ = (*android_java_env)->GetIntArrayElements (android_java_env,
+ window_geometry,
+ NULL);
+ android_exception_check_nonnull (ints, window_geometry);
+
+ *x_return = ints[0];
+ *y_return = ints[1];
+ *width_return = ints[2];
+ *height_return = ints[3];
+
+ (*android_java_env)->ReleaseIntArrayElements (android_java_env,
+ window_geometry,
+ ints, JNI_ABORT);
+
+ /* Now free the local reference. */
+ ANDROID_DELETE_LOCAL_REF (window_geometry);
+}
+
+void
+android_move_resize_window (android_window window, int x, int y,
+ unsigned int width, unsigned int height)
+{
+ android_move_window (window, x, y);
+ android_resize_window (window, width, height);
+}
+
+void
+android_map_raised (android_window window)
+{
+ android_raise_window (window);
+ android_map_window (window);
+}
+
+void
+android_translate_coordinates (android_window src, int x,
+ int y, int *root_x, int *root_y)
+{
+ jobject window;
+ jarray coordinates;
+ jmethodID method;
+ jint *ints;
+
+ window = android_resolve_handle (src, ANDROID_HANDLE_WINDOW);
+ method = window_class.translate_coordinates;
+ coordinates
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ window, method,
+ (jint) x, (jint) y);
+ android_exception_check ();
+
+ /* The array must contain two elements: X, Y translated to the root
+ window. */
+ eassert ((*android_java_env)->GetArrayLength (android_java_env,
+ coordinates)
+ == 2);
+
+ /* Obtain the coordinates from the array. */
+ ints = (*android_java_env)->GetIntArrayElements (android_java_env,
+ coordinates, NULL);
+ android_exception_check_nonnull (ints, coordinates);
+
+ *root_x = ints[0];
+ *root_y = ints[1];
+
+ /* Release the coordinates. */
+ (*android_java_env)->ReleaseIntArrayElements (android_java_env,
+ coordinates, ints,
+ JNI_ABORT);
+
+ /* And free the local reference. */
+ ANDROID_DELETE_LOCAL_REF (coordinates);
+}
+
+int
+android_wc_lookup_string (android_key_pressed_event *event,
+ wchar_t *buffer_return, int wchars_buffer,
+ int *keysym_return,
+ enum android_lookup_status *status_return)
+{
+ enum android_lookup_status status;
+ int rc;
+ jobject window, string;
+ const jchar *characters;
+ jsize size;
+ size_t i;
+
+ status = ANDROID_LOOKUP_NONE;
+ rc = 0;
+
+ /* See if an actual lookup has to be made. Note that while
+ BUFFER_RETURN is wchar_t, the returned characters are always in
+ UCS. */
+
+ if (event->unicode_char != (uint32_t) -1)
+ {
+ if (event->unicode_char)
+ {
+ if (wchars_buffer < 1)
+ {
+ *status_return = ANDROID_BUFFER_OVERFLOW;
+ return 0;
+ }
+ else
+ {
+ buffer_return[0] = event->unicode_char;
+ status = ANDROID_LOOKUP_CHARS;
+ rc = 1;
+ }
+ }
+
+ *keysym_return = event->keycode;
+
+ if (status == ANDROID_LOOKUP_CHARS)
+ status = ANDROID_LOOKUP_BOTH;
+ else
+ {
+ status = ANDROID_LOOKUP_KEYSYM;
+ rc = 0;
+ }
+
+ *status_return = status;
+
+ return rc;
+ }
+
+ /* Now look up the window. */
+ rc = 0;
+
+ if (!android_handles[event->window].handle
+ || (android_handles[event->window].type
+ != ANDROID_HANDLE_WINDOW))
+ status = ANDROID_LOOKUP_NONE;
+ else
+ {
+ window = android_handles[event->window].handle;
+ string
+ = (*android_java_env)->CallObjectMethod (android_java_env, window,
+ window_class.lookup_string,
+ (jint) event->serial);
+ android_exception_check ();
+
+ if (!string)
+ status = ANDROID_LOOKUP_NONE;
+ else
+ {
+ /* Now return this input method string. */
+ characters = (*android_java_env)->GetStringChars (android_java_env,
+ string, NULL);
+ android_exception_check_1 (string);
+
+ /* Figure out how big the string is. */
+ size = (*android_java_env)->GetStringLength (android_java_env,
+ string);
+
+ /* Copy over the string data. */
+ for (i = 0; i < MIN ((unsigned int) wchars_buffer, size); ++i)
+ buffer_return[i] = characters[i];
+
+ if (i < size)
+ status = ANDROID_BUFFER_OVERFLOW;
+ else
+ status = ANDROID_LOOKUP_CHARS;
+
+ /* Return the number of characters that should have been
+ written. */
+
+ if (size > INT_MAX)
+ rc = INT_MAX;
+ else
+ rc = size;
+
+ (*android_java_env)->ReleaseStringChars (android_java_env, string,
+ characters);
+ ANDROID_DELETE_LOCAL_REF (string);
+ }
+ }
+
+ *status_return = status;
+ return rc;
+}
+
+
+
+/* Low level drawing primitives. */
+
+/* Lock the bitmap corresponding to the window WINDOW. Return the
+ bitmap data upon success, and store the bitmap object in
+ BITMAP_RETURN. Value is NULL upon failure.
+
+ The caller must take care to unlock the bitmap data afterwards. */
+
+unsigned char *
+android_lock_bitmap (android_window window,
+ AndroidBitmapInfo *bitmap_info,
+ jobject *bitmap_return)
+{
+ jobject drawable, bitmap;
+ void *data;
+
+ drawable = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+ /* Look up the drawable and get the bitmap corresponding to it.
+ Then, lock the bitmap's bits. */
+ bitmap = (*android_java_env)->CallObjectMethod (android_java_env,
+ drawable,
+ drawable_class.get_bitmap);
+ if (!bitmap)
+ /* NULL is returned when the bitmap does not currently exist due
+ to ongoing reconfiguration on the main thread. */
+ return NULL;
+
+ memset (bitmap_info, 0, sizeof *bitmap_info);
+
+ /* Get the bitmap info. */
+ AndroidBitmap_getInfo (android_java_env, bitmap, bitmap_info);
+
+ if (!bitmap_info->stride)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ return NULL;
+ }
+
+ /* Now lock the image data. */
+ data = NULL;
+ AndroidBitmap_lockPixels (android_java_env, bitmap, &data);
+
+ if (!data)
+ {
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+ return NULL;
+ }
+
+ /* Give the bitmap to the caller. */
+ *bitmap_return = bitmap;
+
+ /* The bitmap data is now locked. */
+ return data;
+}
+
+/* Damage the window HANDLE by the given damage rectangle. */
+
+void
+android_damage_window (android_drawable handle,
+ struct android_rectangle *damage)
+{
+ jobject drawable, rect;
+
+ drawable = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+
+ /* Now turn DAMAGE into a Java rectangle. */
+ rect = (*android_java_env)->NewObject (android_java_env,
+ android_rect_class,
+ android_rect_constructor,
+ (jint) damage->x,
+ (jint) damage->y,
+ (jint) (damage->x
+ + damage->width),
+ (jint) (damage->y
+ + damage->height));
+ android_exception_check ();
+
+ /* Post the damage to the drawable. */
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ drawable,
+ drawable_class.damage_rect,
+ rect);
+ android_exception_check_1 (rect);
+ ANDROID_DELETE_LOCAL_REF (rect);
+}
+
+
+
+/* Other misc system routines. */
+
+int
+android_get_screen_width (void)
+{
+ int rc;
+ jmethodID method;
+
+ method = service_class.get_screen_width;
+ rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ (jboolean) false);
+ android_exception_check ();
+ return rc;
+}
+
+int
+android_get_screen_height (void)
+{
+ int rc;
+ jmethodID method;
+
+ method = service_class.get_screen_height;
+ rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ (jboolean) false);
+ android_exception_check ();
+ return rc;
+}
+
+int
+android_get_mm_width (void)
+{
+ int rc;
+ jmethodID method;
+
+ method = service_class.get_screen_width;
+ rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ (jboolean) true);
+ android_exception_check ();
+ return rc;
+}
+
+int
+android_get_mm_height (void)
+{
+ int rc;
+ jmethodID method;
+
+ method = service_class.get_screen_height;
+ rc = (*android_java_env)->CallNonvirtualIntMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ (jboolean) true);
+ android_exception_check ();
+ return rc;
+}
+
+bool
+android_detect_mouse (void)
+{
+ bool rc;
+ jmethodID method;
+
+ method = service_class.detect_mouse;
+ rc = (*android_java_env)->CallNonvirtualBooleanMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method);
+ android_exception_check ();
+ return rc;
+}
+
+void
+android_set_dont_focus_on_map (android_window handle,
+ bool no_focus_on_map)
+{
+ jmethodID method;
+ jobject window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ method = window_class.set_dont_focus_on_map;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window,
+ window_class.class,
+ method,
+ (jboolean) no_focus_on_map);
+ android_exception_check ();
+}
+
+void
+android_set_dont_accept_focus (android_window handle,
+ bool no_accept_focus)
+{
+ jmethodID method;
+ jobject window;
+
+ window = android_resolve_handle (handle, ANDROID_HANDLE_WINDOW);
+ method = window_class.set_dont_accept_focus;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, window,
+ window_class.class,
+ method,
+ (jboolean) no_accept_focus);
+ android_exception_check ();
+}
+
+void
+android_get_keysym_name (int keysym, char *name_return, size_t size)
+{
+ jobject string;
+ const char *buffer;
+
+ string = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.name_keysym,
+ (jint) keysym);
+ android_exception_check ();
+
+ buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
+ (jstring) string,
+ NULL);
+ android_exception_check_nonnull ((void *) buffer, string);
+ strncpy (name_return, buffer, size - 1);
+
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ (jstring) string,
+ buffer);
+ ANDROID_DELETE_LOCAL_REF (string);
+}
+
+/* Display the on screen keyboard on window WINDOW, or hide it if SHOW
+ is false. Ask the system to bring up or hide the on-screen
+ keyboard on behalf of WINDOW. The request may be rejected by the
+ system, especially when the window does not have the input
+ focus. */
+
+void
+android_toggle_on_screen_keyboard (android_window window, bool show)
+{
+ jobject object;
+ jmethodID method;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+ method = window_class.toggle_on_screen_keyboard;
+
+ /* Now display the on screen keyboard. */
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env, object,
+ window_class.class,
+ method, (jboolean) show);
+
+ /* Check for out of memory errors. */
+ android_exception_check ();
+}
+
+
+
+/* When calling the system's faccessat, make sure to clear the flag
+ AT_EACCESS.
+
+ Android's faccessat simply fails upon using AT_EACCESS, so replace
+ it with zero here. This isn't caught during configuration as Emacs
+ is being cross compiled.
+
+ This replacement is only done when building for Android 16 or
+ later, because earlier versions use the gnulib replacement that
+ lacks these issues.
+
+ This is unnecessary on earlier API versions, as gnulib's
+ rpl_faccessat will be used instead, which lacks these problems. */
+
+/* Like faccessat, except it also understands DIRFD opened using
+ android_dirfd. */
+
+int
+android_faccessat (int dirfd, const char *pathname, int mode, int flags)
+{
+ const char *asset;
+
+ if (dirfd != AT_FDCWD)
+ dirfd
+ = android_lookup_asset_directory_fd (dirfd, &pathname,
+ pathname);
+
+ /* Check if pathname is actually an asset. If that is the case,
+ simply fall back to android_file_access_p. */
+
+ if (dirfd == AT_FDCWD
+ && asset_manager
+ && (asset = android_get_asset_name (pathname)))
+ {
+ if (android_file_access_p (asset, mode))
+ return 0;
+
+ /* Set errno to an appropriate value. */
+ errno = ENOENT;
+ return 1;
+ }
+
+ /* Check if pathname is actually a content resolver URI. */
+
+ if (dirfd == AT_FDCWD
+ && android_init_gui
+ && android_content_name_p (pathname))
+ {
+ if (android_check_content_access (pathname, mode))
+ return 0;
+
+ /* Set errno to an appropriate value. */
+ errno = ENOENT;
+ return 1;
+ }
+
+#if __ANDROID_API__ >= 16
+ return faccessat (dirfd, pathname, mode, flags & ~AT_EACCESS);
+#else
+ return faccessat (dirfd, pathname, mode, flags);
+#endif
+}
+
+
+
+/* Directory listing emulation. */
+
+struct android_dir
+{
+ /* The real DIR *, if it exists. */
+ DIR *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;
+
+ /* The next struct android_dir. */
+ struct android_dir *next;
+
+ /* Path to the directory relative to /. */
+ char *asset_file;
+
+ /* File descriptor used when asset_dir is set. */
+ int fd;
+};
+
+/* List of all struct android_dir's corresponding to an asset
+ directory that are currently open. */
+static struct android_dir *android_dirs;
+
+/* Like opendir. However, return an asset directory if NAME points to
+ an asset. */
+
+struct android_dir *
+android_opendir (const char *name)
+{
+ struct android_dir *dir;
+ char *asset_dir;
+ const char *asset_name;
+ size_t limit, length;
+
+ asset_name = android_get_asset_name (name);
+
+ /* If the asset manager exists and NAME is an asset, return an asset
+ directory. */
+ if (asset_manager && asset_name)
+ {
+ asset_dir
+ = (char *) android_scan_directory_tree ((char *) asset_name,
+ &limit);
+
+ if (!asset_dir)
+ {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ length = strlen (name);
+
+ dir = xmalloc (sizeof *dir);
+ dir->dir = NULL;
+ dir->asset_dir = asset_dir;
+ dir->asset_limit = (char *) directory_tree + limit;
+ dir->fd = -1;
+ dir->asset_file = xzalloc (length + 2);
+
+ /* Make sure dir->asset_file is terminated with /. */
+ strcpy (dir->asset_file, name);
+ if (dir->asset_file[length - 1] != '/')
+ dir->asset_file[length] = '/';
+
+ /* 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;
+ }
+
+ dir->next = android_dirs;
+ android_dirs = dir;
+
+ return dir;
+ }
+
+ /* Otherwise, open the directory normally. */
+ dir = xmalloc (sizeof *dir);
+ dir->asset_dir = NULL;
+ dir->dir = opendir (name);
+
+ if (!dir->dir)
+ {
+ xfree (dir);
+ return NULL;
+ }
+
+ return dir;
+}
+
+/* Like dirfd. However, value is not a real directory file descriptor
+ if DIR is an asset directory. */
+
+int
+android_dirfd (struct android_dir *dirp)
+{
+ int fd;
+
+ if (dirp->dir)
+ return dirfd (dirp->dir);
+ else if (dirp->fd != -1)
+ return dirp->fd;
+
+ fd = open ("/dev/null", O_RDONLY | O_CLOEXEC);
+
+ /* Record this file descriptor in dirp. */
+ dirp->fd = fd;
+ return fd;
+}
+
+/* Like readdir, except it understands asset directories. */
+
+struct dirent *
+android_readdir (struct android_dir *dir)
+{
+ static struct dirent dirent;
+ const char *last;
+
+ if (dir->asset_dir)
+ {
+ /* 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++;
+
+ /* 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;
+
+ /* If this is not a directory, return DT_UNKNOWN. Otherwise,
+ return DT_DIR. */
+
+ if (android_is_directory (dir->asset_dir))
+ dirent.d_type = DT_DIR;
+ else
+ dirent.d_type = DT_UNKNOWN;
+
+ /* 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;
+ }
+
+ return readdir (dir->dir);
+}
+
+/* Like closedir, but it also closes asset manager directories. */
+
+void
+android_closedir (struct android_dir *dir)
+{
+ struct android_dir **next, *tem;
+
+ if (dir->dir)
+ closedir (dir->dir);
+ else
+ {
+ if (dir->fd != -1)
+ close (dir->fd);
+
+ /* Unlink this directory from the list of all asset manager
+ directories. */
+
+ for (next = &android_dirs; (tem = *next);)
+ {
+ if (tem == dir)
+ *next = dir->next;
+ else
+ next = &(*next)->next;
+ }
+
+ /* Free the asset file name. */
+ xfree (dir->asset_file);
+ }
+
+ /* There is no need to close anything else, as the directory tree
+ lies in statically allocated memory. */
+
+ xfree (dir);
+}
+
+/* Subroutine used by android_fstatat and android_faccessat. If DIRFD
+ belongs to an open asset directory and FILE is a relative file
+ name, then return AT_FDCWD and the absolute file name of the
+ directory prepended to FILE in *PATHNAME. Else, return DIRFD. */
+
+int
+android_lookup_asset_directory_fd (int dirfd,
+ const char *restrict *pathname,
+ const char *restrict file)
+{
+ struct android_dir *dir;
+ static char *name;
+
+ if (file[0] == '/')
+ return dirfd;
+
+ for (dir = android_dirs; dir; dir = dir->next)
+ {
+ if (dir->fd == dirfd && dirfd != -1)
+ {
+ if (name)
+ xfree (name);
+
+ /* dir->asset_file is always separator terminated. */
+ name = xzalloc (strlen (dir->asset_file)
+ + strlen (file) + 1);
+ strcpy (name, dir->asset_file);
+ strcpy (name + strlen (dir->asset_file),
+ file);
+ *pathname = name;
+ return AT_FDCWD;
+ }
+ }
+
+ return dirfd;
+}
+
+
+
+/* emacs_abort implementation for Android. This logs a stack
+ trace. */
+
+void
+emacs_abort (void)
+{
+ volatile char *foo;
+
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "emacs_abort called, please review the ensuing"
+ " stack trace");
+
+ /* Cause a NULL pointer dereference to make debuggerd generate a
+ tombstone. */
+ foo = NULL;
+ *foo = '\0';
+
+ abort ();
+}
+
+
+
+/* Return whether or not TEXT, a string without multibyte
+ characters, has no bytes with the 8th bit set. */
+
+static bool
+android_check_string (Lisp_Object text)
+{
+ ptrdiff_t i;
+
+ for (i = 0; i < SBYTES (text); ++i)
+ {
+ if (SREF (text, i) & 128)
+ return false;
+ }
+
+ return true;
+}
+
+/* Given a Lisp string TEXT, return a local reference to an equivalent
+ Java string. */
+
+jstring
+android_build_string (Lisp_Object text)
+{
+ Lisp_Object encoded;
+ jstring string;
+ size_t nchars;
+ jchar *characters;
+ USE_SAFE_ALLOCA;
+
+ /* Directly encode TEXT if it contains no multibyte
+ characters. This is okay because the Java extended UTF
+ format is compatible with ASCII. */
+
+ if (SBYTES (text) == SCHARS (text)
+ && android_check_string (text))
+ {
+ string = (*android_java_env)->NewStringUTF (android_java_env,
+ SSDATA (text));
+ android_exception_check ();
+ SAFE_FREE ();
+
+ return string;
+ }
+
+ encoded = code_convert_string_norecord (text, Qutf_16le,
+ true);
+ nchars = (SBYTES (encoded) / sizeof (jchar));
+
+ /* Encode the string as UTF-16 prior to creating the string.
+ Copy the string to a separate buffer in order to preserve
+ alignment. */
+
+ characters = SAFE_ALLOCA (SBYTES (encoded));
+ memcpy (characters, SDATA (encoded), SBYTES (encoded));
+
+ /* Create the string. */
+ string
+ = (*android_java_env)->NewString (android_java_env,
+ characters, nchars);
+ android_exception_check ();
+
+ SAFE_FREE ();
+ return string;
+}
+
+/* Do the same, except TEXT is constant string data in ASCII or
+ UTF-8 containing no characters outside the Basic Multilingual
+ Plane. */
+
+jstring
+android_build_jstring (const char *text)
+{
+ jstring string;
+
+ /* Note that Java expects this string to be in ``modified UTF
+ encoding'', which is actually UTF-8, except with NUL
+ encoded as a two-byte sequence, and surrogate pairs encoded
+ in the three-byte extended encoding. The only consequence
+ of passing an actual UTF-8 string is that NUL bytes and
+ characters requiring surrogate pairs cannot be represented,
+ which is not really of consequence. */
+
+ string = (*android_java_env)->NewStringUTF (android_java_env,
+ text);
+ android_exception_check ();
+
+ return string;
+}
+
+
+
+/* Exception checking functions. Most JNI functions which allocate
+ memory return NULL upon failure; they also set the JNI
+ environment's pending exception to an OutOfMemoryError.
+
+ These functions check for such errors and call memory_full wherever
+ appropriate. Three variants are provided: one which releases no
+ local references, one which releases a single local reference
+ before calling memory_full, and one which releases two local
+ references.
+
+ Typically, you use these functions by calling them immediately
+ after a JNI function which allocates memory, passing it any local
+ references that are already valid but are not used after leaving
+ the current scope. For example, to allocate foo and then make
+ global_foo its global reference, and then release foo, you write:
+
+ jobject foo, global_foo;
+
+ foo = (*android_java_env)->New...;
+ android_exception_check ();
+
+ global_foo = (*android_java_env)->NewGlobalRef (..., foo);
+ android_exception_check_1 (foo);
+ ANDROID_DELETE_LOCAL_REF (foo);
+
+ where the first android_exception_check ensures that foo has been
+ allocated correctly, while the call to android_exception_check_1,
+ and the call to ANDROID_DELETE_LOCAL_REF afterwards, together
+ ensure the same of global_foo, and also that foo is released both
+ if global_foo cannot be allocated, and after the global reference
+ is created. */
+
+/* Check for JNI exceptions and call memory_full in that
+ situation. */
+
+void
+android_exception_check (void)
+{
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "Possible out of memory error. "
+ " The Java exception follows: ");
+ /* Describe exactly what went wrong. */
+ (*android_java_env)->ExceptionDescribe (android_java_env);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+}
+
+/* Check for JNI exceptions. If there is one such exception, clear
+ it, then delete the local reference to OBJECT and call
+ memory_full. */
+
+void
+android_exception_check_1 (jobject object)
+{
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "Possible out of memory error. "
+ " The Java exception follows: ");
+ /* Describe exactly what went wrong. */
+ (*android_java_env)->ExceptionDescribe (android_java_env);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+ memory_full (0);
+ }
+}
+
+/* Like android_exception_check_one, except it takes more than one
+ local reference argument. */
+
+void
+android_exception_check_2 (jobject object, jobject object1)
+{
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "Possible out of memory error. "
+ " The Java exception follows: ");
+ /* Describe exactly what went wrong. */
+ (*android_java_env)->ExceptionDescribe (android_java_env);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+ ANDROID_DELETE_LOCAL_REF (object1);
+ memory_full (0);
+ }
+}
+
+/* Check for JNI problems based on the value of OBJECT.
+
+ Signal out of memory if OBJECT is NULL. OBJECT1 means the
+ same as in `android_exception_check_1'.
+
+ This function is useful when checking for errors from JNI
+ functions that do not set exceptions on failure, such as
+ `GetIntArrayElements'. */
+
+void
+android_exception_check_nonnull (void *object, jobject object1)
+{
+ if (object)
+ return;
+
+ if (object1)
+ ANDROID_DELETE_LOCAL_REF (object1);
+
+ memory_full (0);
+}
+
+/* Check for JNI problems based on the value of OBJECT.
+
+ Signal out of memory if OBJECT is NULL. OBJECT1 and OBJECT2 mean
+ the same as in `android_exception_check_2'. */
+
+void
+android_exception_check_nonnull_1 (void *object, jobject object1,
+ jobject object2)
+{
+ if (object)
+ return;
+
+ if (object1)
+ ANDROID_DELETE_LOCAL_REF (object1);
+
+ if (object2)
+ ANDROID_DELETE_LOCAL_REF (object2);
+
+ memory_full (0);
+}
+
+
+
+/* Native image transforms. */
+
+/* Transform the coordinates X and Y by the specified affine
+ transformation MATRIX. Place the result in *XOUT and *YOUT. */
+
+static void
+android_transform_coordinates (int x, int y,
+ struct android_transform *transform,
+ float *xout, float *yout)
+{
+ /* Apply the specified affine transformation.
+ A transform looks like:
+
+ M1 M2 M3 X
+ M4 M5 M6 * Y
+
+ =
+
+ M1*X + M2*Y + M3*1 = X1
+ M4*X + M5*Y + M6*1 = Y1
+
+ (In most transforms, there is another row at the bottom for
+ mathematical reasons. Since Z1 is always 1.0, the row is simply
+ implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 = 1.0. See
+ the definition of matrix3x3 in image.c for some more explanations
+ about this.) */
+
+ *xout = transform->m1 * x + transform->m2 * y + transform->m3;
+ *yout = transform->m4 * x + transform->m5 * y + transform->m6;
+}
+
+/* Return the interpolation of the four pixels TL, TR, BL, and BR,
+ according to the weights DISTX and DISTY. */
+
+static unsigned int
+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;
+
+ return r;
+}
+
+/* Return the interpolation of the four pixels closest to at X, Y in
+ IMAGE, according to weights in both axes computed from X and Y.
+ IMAGE must be depth 24, or the behavior is undefined. */
+
+static unsigned int
+android_fetch_pixel_bilinear (struct android_image *image,
+ float x, float y)
+{
+ int x1, y1, x2, y2;
+ float distx, disty;
+ unsigned int top_left, top_right;
+ unsigned int bottom_left, bottom_right;
+ char *word;
+
+ /* Compute the four closest corners to X and Y. */
+ x1 = (int) x;
+ x2 = x1 + 1;
+ y1 = (int) y;
+ y2 = y1 + 1;
+
+ /* Make sure all four corners are within range. */
+ x1 = MAX (0, MIN (image->width - 1, x1));
+ y1 = MAX (0, MIN (image->height - 1, y1));
+ x2 = MAX (0, MIN (image->width - 1, x2));
+ y2 = MAX (0, MIN (image->height - 1, y2));
+
+ /* Compute the X and Y biases. These are numbers between 0f and
+ 1f. */
+ distx = x - x1;
+ disty = y - y1;
+
+ /* Fetch the four closest pixels. */
+ word = image->data + y1 * image->bytes_per_line + x1 * 4;
+ memcpy (&top_left, word, sizeof top_left);
+ word = image->data + y1 * image->bytes_per_line + x2 * 4;
+ memcpy (&top_right, word, sizeof top_right);
+ word = image->data + y2 * image->bytes_per_line + x1 * 4;
+ memcpy (&bottom_left, word, sizeof bottom_left);
+ word = image->data + y2 * image->bytes_per_line + x2 * 4;
+ memcpy (&bottom_right, word, sizeof bottom_right);
+
+ /* Do the interpolation. */
+ return android_four_corners_bilinear (top_left, top_right, bottom_left,
+ bottom_right, distx * 256,
+ disty * 256);
+}
+
+/* Transform the depth 24 image IMAGE by the 3x2 affine transformation
+ matrix MATRIX utilizing a bilinear filter. Place the result in
+ OUT. The matrix maps from the coordinate space of OUT to
+ IMAGE. */
+
+void
+android_project_image_bilinear (struct android_image *image,
+ struct android_image *out,
+ struct android_transform *transform)
+{
+ int x, y;
+ unsigned int pixel;
+ float xout, yout;
+ char *word;
+
+ /* Loop through each pixel in OUT. Transform it by TRANSFORM, then
+ interpolate it to IMAGE, and place the result back in OUT. */
+
+ for (y = 0; y < out->height; ++y)
+ {
+ for (x = 0; x < out->width; ++x)
+ {
+ /* Transform the coordinates by TRANSFORM. */
+ android_transform_coordinates (x, y, transform,
+ &xout, &yout);
+
+ /* Interpolate back to IMAGE. */
+ pixel = android_fetch_pixel_bilinear (image, xout, yout);
+
+ /* Put the pixel back in OUT. */
+ word = out->data + y * out->bytes_per_line + x * 4;
+ memcpy (word, &pixel, sizeof pixel);
+ }
+ }
+}
+
+/* Return the interpolation of X, Y to IMAGE, a depth 24 image. */
+
+static unsigned int
+android_fetch_pixel_nearest_24 (struct android_image *image, float x,
+ float y)
+{
+ int x1, y1;
+ char *word;
+ unsigned int pixel;
+
+ x1 = MAX (0, MIN (image->width - 1, (int) roundf (x)));
+ y1 = MAX (0, MIN (image->height - 1, (int) roundf (y)));
+
+ word = image->data + y1 * image->bytes_per_line + x1 * 4;
+ memcpy (&pixel, word, sizeof pixel);
+
+ return pixel;
+}
+
+/* Return the interpolation of X, Y to IMAGE, a depth 1 image. */
+
+static unsigned int
+android_fetch_pixel_nearest_1 (struct android_image *image, float x,
+ float y)
+{
+ int x1, y1;
+ char *byte;
+
+ x1 = MAX (0, MIN (image->width - 1, (int) roundf (x)));
+ y1 = MAX (0, MIN (image->height - 1, (int) roundf (y)));
+
+ byte = image->data + y1 * image->bytes_per_line;
+ return (byte[x1 / 8] & (1 << x1 % 8)) ? 1 : 0;
+}
+
+/* Transform the depth 24 or 1 image IMAGE by the 3x2 affine
+ transformation matrix MATRIX. Place the result in OUT. The matrix
+ maps from the coordinate space of OUT to IMAGE. Use a
+ nearest-neighbor filter. */
+
+void
+android_project_image_nearest (struct android_image *image,
+ struct android_image *out,
+ struct android_transform *transform)
+{
+ int x, y;
+ unsigned int pixel;
+ float xout, yout;
+ char *word, *byte;
+
+ if (image->depth == 1)
+ {
+ for (y = 0; y < out->height; ++y)
+ {
+ for (x = 0; x < out->width; ++x)
+ {
+ /* Transform the coordinates by TRANSFORM. */
+ android_transform_coordinates (x, y, transform,
+ &xout, &yout);
+
+ /* Interpolate back to IMAGE. */
+ pixel = android_fetch_pixel_nearest_1 (image, xout, yout);
+
+ /* Put the pixel back in OUT. */
+ byte = out->data + y * out->bytes_per_line + x / 8;
+
+ if (pixel)
+ *byte |= (1 << x % 8);
+ else
+ *byte &= ~(1 << x % 8);
+ }
+ }
+
+ return;
+ }
+
+ for (y = 0; y < out->height; ++y)
+ {
+ for (x = 0; x < out->width; ++x)
+ {
+ /* Transform the coordinates by TRANSFORM. */
+ android_transform_coordinates (x, y, transform,
+ &xout, &yout);
+
+ /* Interpolate back to IMAGE. */
+ pixel = android_fetch_pixel_nearest_24 (image, xout, yout);
+
+ /* Put the pixel back in OUT. */
+ word = out->data + y * out->bytes_per_line + x * 4;
+ memcpy (word, &pixel, sizeof pixel);
+ }
+ }
+}
+
+
+
+/* Other miscellaneous functions. */
+
+/* Ask the system to start browsing the specified encoded URL. Upon
+ failure, return a string describing the error. Else, value is
+ nil. */
+
+Lisp_Object
+android_browse_url (Lisp_Object url)
+{
+ jobject value, string;
+ Lisp_Object tem;
+ const char *buffer;
+
+ string = android_build_string (url);
+ value = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.browse_url,
+ string);
+ android_exception_check ();
+
+ ANDROID_DELETE_LOCAL_REF (string);
+
+ /* If no string was returned, return Qnil. */
+ if (!value)
+ return Qnil;
+
+ buffer = (*android_java_env)->GetStringUTFChars (android_java_env,
+ (jstring) value,
+ NULL);
+ android_exception_check_1 (string);
+
+ /* Otherwise, build the string describing the error. */
+ tem = build_string_from_utf8 (buffer);
+
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ (jstring) value,
+ buffer);
+
+ /* And return it. */
+ ANDROID_DELETE_LOCAL_REF (value);
+ return tem;
+}
+
+/* Tell the system to restart Emacs in a short amount of time, and
+ then kill Emacs. Never return. This is used to implement
+ `restart-emacs'. */
+
+_Noreturn void
+android_restart_emacs (void)
+{
+ /* Try to call the Java side function. Normally, this should call
+ System.exit to terminate this process. */
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.restart_emacs);
+
+ /* Exit anyway, in case EmacsService did not do so. */
+ exit (0);
+}
+
+/* Return a number from 1 to 33 describing the version of Android
+ Emacs is running on.
+
+ This is different from __ANDROID_API__, as that describes the
+ minimum version of Android this build of Emacs will run on, and in
+ turn which APIs Emacs can safely use. */
+
+int
+android_get_current_api_level (void)
+{
+ return android_api_level;
+}
+
+/* Query the status of the battery, and place it in *STATUS.
+ Value is 1 upon failure, else 0. */
+
+int
+android_query_battery (struct android_battery_state *status)
+{
+ jlongArray array;
+ jlong *longs;
+
+ array = (*android_java_env)->CallObjectMethod (android_java_env,
+ emacs_service,
+ service_class.query_battery);
+ android_exception_check ();
+
+ /* A NULL return with no exception means that battery information
+ could not be obtained. */
+
+ if (!array)
+ return 1;
+
+ longs = (*android_java_env)->GetLongArrayElements (android_java_env,
+ array, NULL);
+ android_exception_check_nonnull (longs, array);
+
+ status->capacity = longs[0];
+ status->charge_counter = longs[1];
+ status->current_average = longs[2];
+ status->current_now = longs[3];
+ status->remaining = longs[4];
+ status->status = longs[5];
+ status->plugged = longs[6];
+ status->temperature = longs[7];
+
+ (*android_java_env)->ReleaseLongArrayElements (android_java_env,
+ array, longs,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (array);
+
+ return 0;
+}
+
+/* Display a small momentary notification on screen containing
+ TEXT, which must be in the modified UTF encoding used by the
+ JVM. */
+
+void
+android_display_toast (const char *text)
+{
+ jstring string;
+
+ /* Make the string. */
+ string = (*android_java_env)->NewStringUTF (android_java_env,
+ text);
+ android_exception_check ();
+
+ /* Display the toast. */
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.display_toast,
+ string);
+ android_exception_check_1 (string);
+
+ /* Delete the local reference to the string. */
+ ANDROID_DELETE_LOCAL_REF (string);
+}
+
+
+
+/* Whether or not a query is currently being made. */
+static bool android_servicing_query;
+
+/* Function that is waiting to be run in the Emacs thread. */
+static void (*android_query_function) (void *);
+
+/* Context for that function. */
+static void *android_query_context;
+
+/* Deadlock protection. The UI thread and the Emacs thread must
+ sometimes make synchronous queries to each other, which are
+ normally answered inside each thread's respective event loop.
+ Deadlocks can happen when both threads simultaneously make such
+ synchronous queries and block waiting for each others responses.
+
+ The Emacs thread can be interrupted to service any queries made by
+ the UI thread, but is not possible the other way around.
+
+ To avoid such deadlocks, an atomic counter is provided. This
+ counter is incremented every time a query starts, and is set to
+ zerp every time one ends. If the UI thread tries to make a query
+ and sees that the counter is non-zero, it simply returns so that
+ its event loop can proceed to perform and respond to the query. If
+ the Emacs thread sees the same thing, then it stops to service all
+ queries being made by the input method, then proceeds to make its
+ query. */
+
+/* Run any function that the UI thread has asked to run, and then
+ signal its completion. */
+
+void
+android_check_query (void)
+{
+ void (*proc) (void *);
+ void *closure;
+
+ if (!__atomic_load_n (&android_servicing_query, __ATOMIC_SEQ_CST))
+ return;
+
+ /* First, load the procedure and closure. */
+ __atomic_load (&android_query_context, &closure, __ATOMIC_SEQ_CST);
+ __atomic_load (&android_query_function, &proc, __ATOMIC_SEQ_CST);
+
+ if (!proc)
+ return;
+
+ proc (closure);
+
+ /* Finish the query. */
+ __atomic_store_n (&android_query_context, NULL, __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_query_function, NULL, __ATOMIC_SEQ_CST);
+ __atomic_clear (&android_servicing_query, __ATOMIC_SEQ_CST);
+
+ /* Signal completion. */
+ sem_post (&android_query_sem);
+}
+
+/* Notice that the Emacs thread will start blocking waiting for a
+ response from the UI thread. Process any pending queries from the
+ UI thread.
+
+ This function may be called from Java. */
+
+static void
+android_begin_query (void)
+{
+ if (__atomic_test_and_set (&android_servicing_query,
+ __ATOMIC_SEQ_CST))
+ {
+ /* Answer the query that is currently being made. */
+ assert (android_query_function != NULL);
+ android_check_query ();
+
+ /* Wait for that query to complete. */
+ while (__atomic_load_n (&android_servicing_query,
+ __ATOMIC_SEQ_CST))
+ ;;
+ }
+}
+
+/* Notice that a query has stopped. This function may be called from
+ Java. */
+
+static void
+android_end_query (void)
+{
+ __atomic_clear (&android_servicing_query, __ATOMIC_SEQ_CST);
+}
+
+/* Synchronously ask the Emacs thread to run the specified PROC with
+ the given CLOSURE. Return if this fails, or once PROC is run.
+
+ PROC may be run from inside maybe_quit.
+
+ It is not okay to run Lisp code which signals or performs non
+ trivial tasks inside PROC.
+
+ Return 1 if the Emacs thread is currently waiting for the UI thread
+ to respond and PROC could not be run, or 0 otherwise. */
+
+int
+android_run_in_emacs_thread (void (*proc) (void *), void *closure)
+{
+ union android_event event;
+
+ event.xaction.type = ANDROID_WINDOW_ACTION;
+ event.xaction.serial = ++event_serial;
+ event.xaction.window = 0;
+ event.xaction.action = 0;
+
+ /* Set android_query_function and android_query_context. */
+ __atomic_store_n (&android_query_context, closure, __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_query_function, proc, __ATOMIC_SEQ_CST);
+
+ /* Don't allow deadlocks to happen; make sure the Emacs thread is
+ not waiting for something to be done. */
+
+ if (__atomic_test_and_set (&android_servicing_query,
+ __ATOMIC_SEQ_CST))
+ {
+ __atomic_store_n (&android_query_context, NULL,
+ __ATOMIC_SEQ_CST);
+ __atomic_store_n (&android_query_function, NULL,
+ __ATOMIC_SEQ_CST);
+
+ return 1;
+ }
+
+ /* Send a dummy event. `android_check_query' will be called inside
+ wait_reading_process_output after the event arrives.
+
+ Otherwise, android_select will call android_check_thread the next
+ time it is entered. */
+ android_write_event (&event);
+
+ /* Start waiting for the function to be executed. */
+ while (sem_wait (&android_query_sem) < 0)
+ ;;
+
+ return 0;
+}
+
+
+
+/* Input method related functions. */
+
+/* Change WINDOW's active selection to the characters between
+ SELECTION_START and SELECTION_END.
+
+ Also, update the composing region to COMPOSING_REGION_START and
+ COMPOSING_REGION_END.
+
+ If any value cannot fit in jint, then the behavior of the input
+ method is undefined. */
+
+void
+android_update_ic (android_window window, ptrdiff_t selection_start,
+ ptrdiff_t selection_end, ptrdiff_t composing_region_start,
+ ptrdiff_t composing_region_end)
+{
+ jobject object;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.update_ic,
+ object,
+ (jint) selection_start,
+ (jint) selection_end,
+ (jint) composing_region_start,
+ (jint) composing_region_end);
+ android_exception_check ();
+}
+
+/* Reinitialize any ongoing input method connection on WINDOW.
+
+ Any input method that is connected to WINDOW will invalidate its
+ cache of the buffer contents.
+
+ MODE controls certain aspects of the input method's behavior:
+
+ - If MODE is ANDROID_IC_MODE_NULL, the input method will be
+ deactivated, and an ASCII only keyboard will be displayed
+ instead.
+
+ - If MODE is ANDROID_IC_MODE_ACTION, the input method will
+ edit text normally, but send ``return'' as a key event.
+ This is useful inside the mini buffer.
+
+ - If MODE is ANDROID_IC_MODE_TEXT, the input method is free
+ to behave however it wants. */
+
+void
+android_reset_ic (android_window window, enum android_ic_mode mode)
+{
+ jobject object;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ service_class.reset_ic,
+ object, (jint) mode);
+ android_exception_check ();
+}
+
+/* Make updates to extracted text known to the input method on
+ WINDOW. TEXT should be a local reference to the new
+ extracted text. TOKEN should be the token specified by the
+ input method. */
+
+void
+android_update_extracted_text (android_window window, void *text,
+ int token)
+{
+ jobject object;
+ jmethodID method;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+ method = service_class.update_extracted_text;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method, object,
+ /* N.B. that
+ text is not
+ jobject,
+ because that
+ type is not
+ available in
+ androidgui.h. */
+ (jobject) text,
+ (jint) token);
+ android_exception_check_1 (text);
+}
+
+/* Report the position of the cursor to the input method connection on
+ WINDOW.
+
+ X is the horizontal position of the end of the insertion marker. Y
+ is the top of the insertion marker. Y_BASELINE is the baseline of
+ the row containing the insertion marker, and Y_BOTTOM is the bottom
+ of the insertion marker. */
+
+void
+android_update_cursor_anchor_info (android_window window, float x,
+ float y, float y_baseline,
+ float y_bottom)
+{
+ jobject object;
+ jmethodID method;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+ method = service_class.update_cursor_anchor_info;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ emacs_service,
+ service_class.class,
+ method,
+ object,
+ (jfloat) x,
+ (jfloat) y,
+ (jfloat) y_baseline,
+ (jfloat) y_bottom);
+ android_exception_check ();
+}
+
+
+
+/* Window decoration management functions. */
+
+/* Make the specified WINDOW fullscreen, i.e. obscure all of the
+ system navigation and status bars. If not FULLSCREEN, make it
+ maximized instead.
+
+ Value is 1 if the system does not support this, else 0. */
+
+int
+android_set_fullscreen (android_window window, bool fullscreen)
+{
+ jobject object;
+
+ /* Android 4.0 and earlier don't support fullscreen windows. */
+
+ if (android_api_level < 16)
+ return 1;
+
+ object = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ object,
+ window_class.class,
+ window_class.set_fullscreen,
+ (jboolean) fullscreen);
+ android_exception_check ();
+ return 0;
+}
+
+
+
+/* External asset management interface. By using functions here
+ to read and write from files, Emacs can avoid opening a
+ shared memory file descriptor for each ``asset'' file. */
+
+/* Like android_open. However, return a structure that can
+ either directly hold an AAsset or a file descriptor.
+
+ Value is the structure upon success. Upon failure, value
+ consists of an uninitialized file descriptor, but its asset
+ field is set to -1, and errno is set accordingly. */
+
+struct android_fd_or_asset
+android_open_asset (const char *filename, int oflag, mode_t mode)
+{
+ const char *name;
+ struct android_fd_or_asset fd;
+ AAsset *asset;
+
+ /* Initialize FD by setting its asset to an invalid
+ pointer. */
+ fd.asset = (void *) -1;
+
+ /* See if this is an asset. */
+
+ if (asset_manager && (name = android_get_asset_name (filename)))
+ {
+ /* Return failure for unsupported flags. */
+
+ if (oflag & O_WRONLY || oflag & O_RDWR)
+ {
+ errno = EROFS;
+ return fd;
+ }
+
+ if (oflag & O_DIRECTORY)
+ {
+ errno = ENOTSUP;
+ return fd;
+ }
+
+ /* Now try to open the asset. */
+ asset = AAssetManager_open (asset_manager, name,
+ AASSET_MODE_STREAMING);
+
+ if (!asset)
+ {
+ errno = ENOENT;
+ return fd;
+ }
+
+ /* Return the asset. */
+ fd.asset = asset;
+ return fd;
+ }
+
+ /* If the file is not an asset, fall back to android_open and
+ get a regular file descriptor. */
+
+ fd.fd = android_open (filename, oflag, mode);
+ if (fd.fd < 0)
+ return fd;
+
+ /* Set fd.asset to NULL, signifying that it is a file
+ descriptor. */
+ fd.asset = NULL;
+ return fd;
+}
+
+/* Like android_close. However, it takes a ``file descriptor''
+ opened using android_open_asset. */
+
+int
+android_close_asset (struct android_fd_or_asset asset)
+{
+ if (!asset.asset)
+ return android_close (asset.fd);
+
+ AAsset_close (asset.asset);
+ return 0;
+}
+
+/* Like `emacs_read_quit'. However, it handles file descriptors
+ opened using `android_open_asset' as well. */
+
+ssize_t
+android_asset_read_quit (struct android_fd_or_asset asset,
+ void *buffer, size_t size)
+{
+ if (!asset.asset)
+ return emacs_read_quit (asset.fd, buffer, size);
+
+ /* It doesn't seem possible to quit from inside AAsset_read,
+ sadly. */
+ return AAsset_read (asset.asset, buffer, size);
+}
+
+/* Like `read'. However, it handles file descriptors opened
+ using `android_open_asset' as well. */
+
+ssize_t
+android_asset_read (struct android_fd_or_asset asset,
+ void *buffer, size_t size)
+{
+ if (!asset.asset)
+ return read (asset.fd, buffer, size);
+
+ /* It doesn't seem possible to quit from inside AAsset_read,
+ sadly. */
+ return AAsset_read (asset.asset, buffer, size);
+}
+
+/* Like `lseek', but it handles ``file descriptors'' opened with
+ android_open_asset. */
+
+off_t
+android_asset_lseek (struct android_fd_or_asset asset, off_t off,
+ int whence)
+{
+ if (!asset.asset)
+ return lseek (asset.fd, off, whence);
+
+ return AAsset_seek (asset.asset, off, whence);
+}
+
+/* Like `fstat'. */
+
+int
+android_asset_fstat (struct android_fd_or_asset asset,
+ struct stat *statb)
+{
+ if (!asset.asset)
+ return fstat (asset.fd, statb);
+
+ /* Clear statb. */
+ memset (statb, 0, sizeof *statb);
+
+ /* Set the mode. */
+ statb->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+
+ /* Owned by root. */
+ statb->st_uid = 0;
+ statb->st_gid = 0;
+
+ /* Size of the file. */
+ statb->st_size = AAsset_getLength (asset.asset);
+ return 0;
+}
+
+
+
+/* Window cursor support. */
+
+android_cursor
+android_create_font_cursor (enum android_cursor_shape shape)
+{
+ android_cursor id;
+ short prev_max_handle;
+ jobject object;
+
+ /* First, allocate the cursor handle. */
+ prev_max_handle = max_handle;
+ id = android_alloc_id ();
+
+ if (!id)
+ error ("Out of cursor handles!");
+
+ /* Next, create the cursor. */
+ object = (*android_java_env)->NewObject (android_java_env,
+ cursor_class.class,
+ cursor_class.constructor,
+ (jshort) id,
+ (jint) shape);
+ if (!object)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ max_handle = prev_max_handle;
+ memory_full (0);
+ }
+
+ android_handles[id].type = ANDROID_HANDLE_CURSOR;
+ android_handles[id].handle
+ = (*android_java_env)->NewGlobalRef (android_java_env, object);
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (object);
+
+ if (!android_handles[id].handle)
+ memory_full (0);
+
+ return id;
+}
+
+void
+android_define_cursor (android_window window, android_cursor cursor)
+{
+ jobject window1, cursor1;
+ jmethodID method;
+
+ window1 = android_resolve_handle (window, ANDROID_HANDLE_WINDOW);
+ cursor1 = android_resolve_handle (cursor, ANDROID_HANDLE_CURSOR);
+ method = window_class.define_cursor;
+
+ (*android_java_env)->CallNonvirtualVoidMethod (android_java_env,
+ window1,
+ window_class.class,
+ method, cursor1);
+ android_exception_check ();
+}
+
+void
+android_free_cursor (android_cursor cursor)
+{
+ if (android_handles[cursor].type != ANDROID_HANDLE_CURSOR)
+ {
+ __android_log_print (ANDROID_LOG_ERROR, __func__,
+ "Trying to destroy something not a CURSOR!");
+ emacs_abort ();
+ }
+
+ android_destroy_handle (cursor);
+}
+
+
+
+/* Process execution.
+
+ Newer Android systems use SELinux to restrict user programs from
+ executing programs installed in the application data directory for
+ security reasons. Emacs uses a `loader' binary installed in the
+ application data directory to manually load executables and replace
+ the `execve' system call. */
+
+enum
+ {
+ /* Maximum number of arguments available. */
+ MAXARGS = 1024,
+ };
+
+/* Rewrite the command line given in *ARGV to utilize the `exec1'
+ bootstrap binary if necessary.
+
+ Value is 0 upon success, else 1. Set errno upon failure.
+
+ ARGV holds a pointer to a NULL-terminated array of arguments given
+ to `emacs_spawn'. */
+
+int
+android_rewrite_spawn_argv (const char ***argv)
+{
+ static const char *new_args[MAXARGS];
+ static char exec1_name[PATH_MAX], loader_name[PATH_MAX];
+ size_t i, nargs;
+
+ /* This isn't required on Android 9 or earlier. */
+
+ if (android_api_level < 29 || !android_use_exec_loader)
+ return 0;
+
+ /* Get argv[0]; this should never be NULL.
+ Then, verify that it exists and is executable. */
+
+ eassert (**argv);
+ if (access (**argv, R_OK | X_OK))
+ return 1;
+
+ /* Count the number of arguments in *argv. */
+
+ nargs = 0;
+ while ((*argv)[nargs])
+ ++nargs;
+
+ /* nargs now holds the number of arguments in argv. If it's larger
+ than MAXARGS, return failure. */
+
+ if (nargs + 2 > MAXARGS)
+ {
+ errno = E2BIG;
+ return 1;
+ }
+
+ /* Fill in the name of `libexec1.so'. */
+ snprintf (exec1_name, PATH_MAX, "%s/libexec1.so",
+ android_lib_dir);
+
+ /* And libloader.so. */
+ snprintf (loader_name, PATH_MAX, "%s/libloader.so",
+ android_lib_dir);
+
+ /* Now fill in the first two arguments. */
+ new_args[0] = exec1_name;
+ new_args[1] = loader_name;
+
+ /* And insert the rest, including the trailing NULL. */
+ for (i = 0; i < nargs + 1; ++i)
+ new_args[i + 2] = (*argv)[i];
+
+ /* Replace argv. */
+ *argv = new_args;
+
+ /* Return success. */
+ return 0;
+}
+
+
+
+#else /* ANDROID_STUBIFY */
+
+/* X emulation functions for Android. */
+
+struct android_gc *
+android_create_gc (enum android_gc_value_mask mask,
+ struct android_gc_values *values)
+{
+ /* This function should never be called when building stubs. */
+ emacs_abort ();
+}
+
+void
+android_free_gc (struct android_gc *gc)
+{
+ /* This function should never be called when building stubs. */
+ emacs_abort ();
+}
+
+struct android_image *
+android_create_image (unsigned int depth, enum android_image_format format,
+ char *data, unsigned int width, unsigned int height)
+{
+ emacs_abort ();
+}
+
+void
+android_destroy_image (struct android_image *ximg)
+{
+ emacs_abort ();
+}
+
+void
+android_put_pixel (struct android_image *ximg, int x, int y,
+ unsigned long pixel)
+{
+ emacs_abort ();
+}
+
+unsigned long
+android_get_pixel (struct android_image *ximg, int x, int y)
+{
+ emacs_abort ();
+}
+
+struct android_image *
+android_get_image (android_drawable drawable,
+ enum android_image_format format)
+{
+ emacs_abort ();
+}
+
+void
+android_put_image (android_pixmap pixmap,
+ struct android_image *image)
+{
+ emacs_abort ();
+}
+
+void
+android_project_image_bilinear (struct android_image *image,
+ struct android_image *out,
+ struct android_transform *transform)
+{
+ emacs_abort ();
+}
+
+void
+android_project_image_nearest (struct android_image *image,
+ struct android_image *out,
+ struct android_transform *transform)
+{
+ emacs_abort ();
+}
+
+#endif
diff --git a/src/android.h b/src/android.h
new file mode 100644
index 00000000000..62d420d4cce
--- /dev/null
+++ b/src/android.h
@@ -0,0 +1,231 @@
+/* Android initialization for GNU Emacs.
+
+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/>. */
+
+/* On Android, Emacs is built as a shared library loaded from Java
+ using the Java Native Interface. Emacs's `main' function is
+ renamed `android_emacs_init', and runs with some modifications
+ inside a separate thread, communicating with the Java code through
+ a table of function pointers. */
+
+#ifndef _ANDROID_H_
+#ifndef ANDROID_STUBIFY
+#include <jni.h>
+#include <pwd.h>
+
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdio.h>
+
+#include <android/bitmap.h>
+
+#include "androidgui.h"
+#include "lisp.h"
+#endif
+
+extern bool android_init_gui;
+
+#ifndef ANDROID_STUBIFY
+
+extern int android_emacs_init (int, char **, char *);
+extern int android_select (int, fd_set *, fd_set *, fd_set *,
+ struct timespec *);
+
+extern int android_open (const char *, int, mode_t);
+extern char *android_user_full_name (struct passwd *);
+extern int android_fstat (int, struct stat *);
+extern int android_fstatat (int, const char *restrict,
+ struct stat *restrict, int);
+extern int android_faccessat (int, const char *, int, int);
+extern int android_close (int);
+extern int android_fclose (FILE *);
+extern const char *android_get_home_directory (void);
+
+extern double android_pixel_density_x, android_pixel_density_y;
+
+enum android_handle_type
+ {
+ ANDROID_HANDLE_WINDOW,
+ ANDROID_HANDLE_GCONTEXT,
+ ANDROID_HANDLE_PIXMAP,
+ ANDROID_HANDLE_CURSOR,
+ };
+
+extern jobject android_resolve_handle (android_handle,
+ enum android_handle_type);
+extern unsigned char *android_lock_bitmap (android_window,
+ AndroidBitmapInfo *,
+ jobject *);
+extern void android_damage_window (android_window,
+ struct android_rectangle *);
+extern int android_get_screen_width (void);
+extern int android_get_screen_height (void);
+extern int android_get_mm_width (void);
+extern int android_get_mm_height (void);
+extern bool android_detect_mouse (void);
+
+extern void android_set_dont_focus_on_map (android_window, bool);
+extern void android_set_dont_accept_focus (android_window, bool);
+
+extern jstring android_build_string (Lisp_Object);
+extern jstring android_build_jstring (const char *);
+extern void android_exception_check (void);
+extern void android_exception_check_1 (jobject);
+extern void android_exception_check_2 (jobject, jobject);
+extern void android_exception_check_nonnull (void *, jobject);
+extern void android_exception_check_nonnull_1 (void *, jobject, jobject);
+
+extern void android_get_keysym_name (int, char *, size_t);
+extern void android_wait_event (void);
+extern void android_toggle_on_screen_keyboard (android_window, bool);
+extern _Noreturn void android_restart_emacs (void);
+extern int android_get_current_api_level (void);
+
+
+
+/* Directory listing emulation. */
+
+struct android_dir;
+
+extern struct android_dir *android_opendir (const char *);
+extern int android_dirfd (struct android_dir *);
+extern struct dirent *android_readdir (struct android_dir *);
+extern void android_closedir (struct android_dir *);
+
+
+
+/* External asset manager interface. */
+
+struct android_fd_or_asset
+{
+ /* The file descriptor. */
+ int fd;
+
+ /* The asset. If set, FD is not a real file descriptor. */
+ void *asset;
+};
+
+extern struct android_fd_or_asset android_open_asset (const char *,
+ int, mode_t);
+extern int android_close_asset (struct android_fd_or_asset);
+extern ssize_t android_asset_read_quit (struct android_fd_or_asset,
+ void *, size_t);
+extern ssize_t android_asset_read (struct android_fd_or_asset,
+ void *, size_t);
+extern off_t android_asset_lseek (struct android_fd_or_asset, off_t, int);
+extern int android_asset_fstat (struct android_fd_or_asset,
+ struct stat *);
+
+
+
+/* Very miscellaneous functions. */
+
+struct android_battery_state
+{
+ /* Battery charge level in integer percentage. */
+ intmax_t capacity;
+
+ /* Battery charge level in microampere-hours. */
+ intmax_t charge_counter;
+
+ /* Battery current in microampere-hours. */
+ intmax_t current_average;
+
+ /* Instantaneous battery current in microampere-hours. */
+ intmax_t current_now;
+
+ /* Estimate as to the amount of time remaining until the battery is
+ charged, in milliseconds. */
+ intmax_t remaining;
+
+ /* Battery status. The value is either:
+
+ 2, if the battery is charging.
+ 3, if the battery is discharging.
+ 5, if the battery is full.
+ 4, if the battery is not full or discharging,
+ but is not charging either.
+ 1, if the battery state is unknown. */
+ int status;
+
+ /* The power source of the battery. Value is:
+
+ 0, if on battery power.
+ 1, for line power.
+ 8, for dock power.
+ 2, for USB power.
+ 4, for wireless power. */
+ int plugged;
+
+ /* The temperature of the battery in 10 * degrees centigrade. */
+ int temperature;
+};
+
+extern Lisp_Object android_browse_url (Lisp_Object);
+extern int android_query_battery (struct android_battery_state *);
+extern void android_display_toast (const char *);
+
+
+
+/* Event loop functions. */
+
+extern void android_check_query (void);
+extern int android_run_in_emacs_thread (void (*) (void *), void *);
+extern void android_write_event (union android_event *);
+
+extern unsigned int event_serial;
+
+
+
+/* Process related functions. */
+extern int android_rewrite_spawn_argv (const char ***);
+
+#endif
+
+/* JNI functions should not be built when Emacs is stubbed out for the
+ build. These should be documented in EmacsNative.java. */
+
+#ifndef ANDROID_STUBIFY
+#include <jni.h>
+
+extern JNIEnv *android_java_env;
+
+#define ANDROID_DELETE_LOCAL_REF(ref) \
+ ((*android_java_env)->DeleteLocalRef (android_java_env, \
+ (ref)))
+
+#define NATIVE_NAME(name) Java_org_gnu_emacs_EmacsNative_##name
+
+/* Prologue which must be inserted before each JNI function.
+ See initEmacs for why. */
+
+#if defined __i386__
+extern void *unused_pointer;
+
+#define JNI_STACK_ALIGNMENT_PROLOGUE \
+ __attribute__ ((aligned (32))) char stack_align_buffer[32]; \
+ \
+ /* Trick GCC into not optimizing this variable away. */ \
+ unused_pointer = stack_align_buffer;
+
+#else /* !__i386__ */
+#define JNI_STACK_ALIGNMENT_PROLOGUE ((void) 0)
+#endif /* __i386__ */
+
+#endif
+#endif /* _ANDROID_H_ */
diff --git a/src/androidfns.c b/src/androidfns.c
new file mode 100644
index 00000000000..60b0549e7d1
--- /dev/null
+++ b/src/androidfns.c
@@ -0,0 +1,3205 @@
+/* Communication module for Android terminals.
+
+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 <math.h>
+
+#include "lisp.h"
+#include "android.h"
+#include "androidterm.h"
+#include "blockinput.h"
+#include "keyboard.h"
+#include "buffer.h"
+#include "androidgui.h"
+
+#ifndef ANDROID_STUBIFY
+
+/* Some kind of reference count for the image cache. */
+static ptrdiff_t image_cache_refcount;
+
+/* The frame of the currently visible tooltip, or nil if none. */
+static Lisp_Object tip_frame;
+
+/* The window-system window corresponding to the frame of the
+ currently visible tooltip. */
+static android_window tip_window;
+
+/* The X and Y deltas of the last call to `x-show-tip'. */
+static Lisp_Object tip_dx, tip_dy;
+
+/* A timer that hides or deletes the currently visible tooltip when it
+ fires. */
+static Lisp_Object tip_timer;
+
+/* STRING argument of last `x-show-tip' call. */
+static Lisp_Object tip_last_string;
+
+/* Normalized FRAME argument of last `x-show-tip' call. */
+static Lisp_Object tip_last_frame;
+
+/* PARMS argument of last `x-show-tip' call. */
+static Lisp_Object tip_last_parms;
+
+#endif
+
+static struct android_display_info *
+android_display_info_for_name (Lisp_Object name)
+{
+ struct android_display_info *dpyinfo;
+
+ CHECK_STRING (name);
+
+ for (dpyinfo = x_display_list; dpyinfo; dpyinfo = dpyinfo->next)
+ {
+ if (!NILP (Fstring_equal (XCAR (dpyinfo->name_list_element),
+ name)))
+ return dpyinfo;
+ }
+
+ error ("Cannot connect to Android if it was not initialized"
+ " at startup");
+}
+
+static struct android_display_info *
+check_android_display_info (Lisp_Object object)
+{
+ struct android_display_info *dpyinfo;
+ struct frame *sf, *f;
+ struct terminal *t;
+
+ if (NILP (object))
+ {
+ sf = XFRAME (selected_frame);
+
+ if (FRAME_ANDROID_P (sf) && FRAME_LIVE_P (sf))
+ dpyinfo = FRAME_DISPLAY_INFO (sf);
+ else if (x_display_list)
+ dpyinfo = x_display_list;
+ else
+ error ("Android windows are not in use or not initialized");
+ }
+ else if (TERMINALP (object))
+ {
+ t = decode_live_terminal (object);
+
+ if (t->type != output_android)
+ error ("Terminal %d is not an Android display", t->id);
+
+ dpyinfo = t->display_info.android;
+ }
+ else if (STRINGP (object))
+ dpyinfo = android_display_info_for_name (object);
+ else
+ {
+ f = decode_window_system_frame (object);
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+ }
+
+ return dpyinfo;
+}
+
+Display_Info *
+check_x_display_info (Lisp_Object object)
+{
+ return check_android_display_info (object);
+}
+
+
+
+#ifndef ANDROID_STUBIFY
+
+void
+gamma_correct (struct frame *f, Emacs_Color *color)
+{
+ if (f->gamma)
+ {
+ color->red = pow (color->red / 65535.0, f->gamma) * 65535.0 + 0.5;
+ color->green = pow (color->green / 65535.0, f->gamma) * 65535.0 + 0.5;
+ color->blue = pow (color->blue / 65535.0, f->gamma) * 65535.0 + 0.5;
+ }
+}
+
+/* Decide if color named COLOR_NAME is valid for use on frame F. If
+ so, return the RGB values in COLOR. If ALLOC_P, allocate the
+ color. Value is false if COLOR_NAME is invalid, or no color could
+ be allocated. MAKE_INDEX is some mysterious argument used on
+ NS. */
+
+bool
+android_defined_color (struct frame *f, const char *color_name,
+ Emacs_Color *color, bool alloc_p,
+ bool make_index)
+{
+ bool success_p;
+
+ success_p = false;
+
+ block_input ();
+ success_p = android_parse_color (f, color_name, color);
+ if (success_p && alloc_p)
+ success_p = android_alloc_nearest_color (f, color);
+ unblock_input ();
+
+ return success_p;
+}
+
+/* Return the pixel color value for color COLOR_NAME on frame F. If F
+ is a monochrome frame, return MONO_COLOR regardless of what ARG
+ says. Signal an error if color can't be allocated. */
+
+static unsigned long
+android_decode_color (struct frame *f, Lisp_Object color_name, int mono_color)
+{
+ Emacs_Color cdef;
+
+ CHECK_STRING (color_name);
+
+ if (android_defined_color (f, SSDATA (color_name), &cdef,
+ true, false))
+ return cdef.pixel;
+
+ signal_error ("Undefined color", color_name);
+}
+
+static void
+android_set_parent_frame (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value)
+{
+ struct frame *p;
+
+ p = NULL;
+
+ if (!NILP (new_value)
+ && (!FRAMEP (new_value)
+ || !FRAME_LIVE_P (p = XFRAME (new_value))
+ || !FRAME_ANDROID_P (p)))
+ {
+ store_frame_param (f, Qparent_frame, old_value);
+ error ("Invalid specification of `parent-frame'");
+ }
+
+ if (p != FRAME_PARENT_FRAME (f))
+ {
+ block_input ();
+ android_reparent_window (FRAME_ANDROID_WINDOW (f),
+ (p ? FRAME_ANDROID_WINDOW (p)
+ : FRAME_DISPLAY_INFO (f)->root_window),
+ f->left_pos, f->top_pos);
+ unblock_input ();
+
+ fset_parent_frame (f, new_value);
+ }
+
+ /* Update the fullscreen frame parameter as well. */
+ FRAME_TERMINAL (f)->fullscreen_hook (f);
+}
+
+void
+android_implicitly_set_name (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+
+}
+
+void
+android_explicitly_set_name (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+
+}
+
+/* Set the number of lines used for the tool bar of frame F to VALUE.
+ VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
+ is the old number of tool bar lines. This function changes the
+ height of all windows on frame F to match the new tool bar height.
+ The frame's height doesn't change. */
+
+static void
+android_set_tool_bar_lines (struct frame *f, Lisp_Object value,
+ Lisp_Object oldval)
+{
+ int nlines;
+
+ /* Treat tool bars like menu bars. */
+ if (FRAME_MINIBUF_ONLY_P (f))
+ return;
+
+ /* Use VALUE only if an int >= 0. */
+ if (RANGED_FIXNUMP (0, value, INT_MAX))
+ nlines = XFIXNAT (value);
+ else
+ nlines = 0;
+
+ android_change_tool_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+void
+android_change_tool_bar_height (struct frame *f, int height)
+{
+ int unit = FRAME_LINE_HEIGHT (f);
+ int old_height = FRAME_TOOL_BAR_HEIGHT (f);
+ int lines = (height + unit - 1) / unit;
+ Lisp_Object fullscreen = get_frame_param (f, Qfullscreen);
+
+ /* Make sure we redisplay all windows in this frame. */
+ fset_redisplay (f);
+
+ FRAME_TOOL_BAR_HEIGHT (f) = height;
+ FRAME_TOOL_BAR_LINES (f) = lines;
+ store_frame_param (f, Qtool_bar_lines, make_fixnum (lines));
+
+ if (FRAME_ANDROID_WINDOW (f) && FRAME_TOOL_BAR_HEIGHT (f) == 0)
+ {
+ clear_frame (f);
+ clear_current_matrices (f);
+ }
+
+ if ((height < old_height) && WINDOWP (f->tool_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tool_bar_window)->current_matrix);
+
+ if (!f->tool_bar_resized)
+ {
+ /* As long as tool_bar_resized is false, effectively try to change
+ F's native height. */
+ if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+ adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+ 1, false, Qtool_bar_lines);
+ else
+ adjust_frame_size (f, -1, -1, 4, false, Qtool_bar_lines);
+
+ f->tool_bar_resized = f->tool_bar_redisplayed;
+ }
+ else
+ /* Any other change may leave the native size of F alone. */
+ adjust_frame_size (f, -1, -1, 3, false, Qtool_bar_lines);
+
+ /* adjust_frame_size might not have done anything, garbage frame
+ here. */
+ adjust_frame_glyphs (f);
+ SET_FRAME_GARBAGED (f);
+}
+
+/* Set the number of lines used for the tab bar of frame F to VALUE.
+ VALUE not an integer, or < 0 means set the lines to zero. OLDVAL
+ is the old number of tab bar lines. This function may change the
+ height of all windows on frame F to match the new tab bar height.
+ The frame's height may change if frame_inhibit_implied_resize was
+ set accordingly. */
+
+static void
+android_set_tab_bar_lines (struct frame *f, Lisp_Object value,
+ Lisp_Object oldval)
+{
+ int olines;
+ int nlines;
+
+ olines = FRAME_TAB_BAR_LINES (f);
+
+ /* Treat tab bars like menu bars. */
+ if (FRAME_MINIBUF_ONLY_P (f))
+ return;
+
+ /* Use VALUE only if an int >= 0. */
+ if (RANGED_FIXNUMP (0, value, INT_MAX))
+ nlines = XFIXNAT (value);
+ else
+ nlines = 0;
+
+ if (nlines != olines && (olines == 0 || nlines == 0))
+ android_change_tab_bar_height (f, nlines * FRAME_LINE_HEIGHT (f));
+}
+
+void
+android_change_tab_bar_height (struct frame *f, int height)
+{
+ int unit, old_height, lines;
+ Lisp_Object fullscreen;
+
+ unit = FRAME_LINE_HEIGHT (f);
+ old_height = FRAME_TAB_BAR_HEIGHT (f);
+ fullscreen = get_frame_param (f, Qfullscreen);
+
+ /* This differs from the tool bar code in that the tab bar height is
+ not rounded up. Otherwise, if redisplay_tab_bar decides to grow
+ the tab bar by even 1 pixel, FRAME_TAB_BAR_LINES will be changed,
+ leading to the tab bar height being incorrectly set upon the next
+ call to android_set_font. (bug#59285) */
+ lines = height / unit;
+
+ /* Make sure we redisplay all windows in this frame. */
+ fset_redisplay (f);
+
+ /* Recalculate tab bar and frame text sizes. */
+ FRAME_TAB_BAR_HEIGHT (f) = height;
+ FRAME_TAB_BAR_LINES (f) = lines;
+ store_frame_param (f, Qtab_bar_lines, make_fixnum (lines));
+
+ if (FRAME_ANDROID_WINDOW (f) && FRAME_TAB_BAR_HEIGHT (f) == 0)
+ {
+ clear_frame (f);
+ clear_current_matrices (f);
+ }
+
+ if ((height < old_height) && WINDOWP (f->tab_bar_window))
+ clear_glyph_matrix (XWINDOW (f->tab_bar_window)->current_matrix);
+
+ if (!f->tab_bar_resized)
+ {
+ /* As long as tab_bar_resized is false, effectively try to change
+ F's native height. */
+ if (NILP (fullscreen) || EQ (fullscreen, Qfullwidth))
+ adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+ 1, false, Qtab_bar_lines);
+ else
+ adjust_frame_size (f, -1, -1, 4, false, Qtab_bar_lines);
+
+ f->tab_bar_resized = f->tab_bar_redisplayed;
+ }
+ else
+ /* Any other change may leave the native size of F alone. */
+ adjust_frame_size (f, -1, -1, 3, false, Qtab_bar_lines);
+
+ /* adjust_frame_size might not have done anything, garbage frame
+ here. */
+ adjust_frame_glyphs (f);
+ SET_FRAME_GARBAGED (f);
+}
+
+void
+android_set_scroll_bar_default_height (struct frame *f)
+{
+ int height;
+
+ height = FRAME_LINE_HEIGHT (f);
+
+ /* The height of a non-toolkit scrollbar is 14 pixels. */
+ FRAME_CONFIG_SCROLL_BAR_LINES (f) = (14 + height - 1) / height;
+
+ /* Use all of that space (aside from required margins) for the
+ scroll bar. */
+ FRAME_CONFIG_SCROLL_BAR_HEIGHT (f) = 14;
+}
+
+void
+android_set_scroll_bar_default_width (struct frame *f)
+{
+ int unit;
+
+ unit = FRAME_COLUMN_WIDTH (f);
+
+ FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + unit - 1) / unit;
+ FRAME_CONFIG_SCROLL_BAR_WIDTH (f)
+ = FRAME_CONFIG_SCROLL_BAR_COLS (f) * unit;
+}
+
+
+/* Verify that the icon position args for this window are valid. */
+
+static void
+android_icon_verify (struct frame *f, Lisp_Object parms)
+{
+ Lisp_Object icon_x, icon_y;
+
+ /* Set the position of the icon. Note that twm groups all
+ icons in an icon window. */
+ icon_x = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0,
+ RES_TYPE_NUMBER);
+ icon_y = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0,
+ RES_TYPE_NUMBER);
+
+ if (!BASE_EQ (icon_x, Qunbound) && !BASE_EQ (icon_y, Qunbound))
+ {
+ CHECK_FIXNUM (icon_x);
+ CHECK_FIXNUM (icon_y);
+ }
+ else if (!BASE_EQ (icon_x, Qunbound) || !BASE_EQ (icon_y, Qunbound))
+ error ("Both left and top icon corners of icon must be specified");
+}
+
+/* Handle the icon stuff for this window. Perhaps later we might
+ want an x_set_icon_position which can be called interactively as
+ well. */
+
+static void
+android_icon (struct frame *f, Lisp_Object parms)
+{
+ /* Set the position of the icon. Note that twm groups all
+ icons in an icon window. */
+ Lisp_Object icon_x
+ = gui_frame_get_and_record_arg (f, parms, Qicon_left, 0, 0,
+ RES_TYPE_NUMBER);
+ Lisp_Object icon_y
+ = gui_frame_get_and_record_arg (f, parms, Qicon_top, 0, 0,
+ RES_TYPE_NUMBER);
+
+ bool xgiven = !BASE_EQ (icon_x, Qunbound);
+ bool ygiven = !BASE_EQ (icon_y, Qunbound);
+
+ if (xgiven != ygiven)
+ error ("Both left and top icon corners of icon must be specified");
+
+ if (xgiven)
+ {
+ check_integer_range (icon_x, INT_MIN, INT_MAX);
+ check_integer_range (icon_y, INT_MIN, INT_MAX);
+ }
+
+ /* Now return as this is not supported on Android. */
+}
+
+/* Make the GCs needed for this window, setting the background
+ color. */
+
+static void
+android_make_gc (struct frame *f)
+{
+ struct android_gc_values gc_values;
+
+ block_input ();
+
+ /* Create the GCs of this frame.
+ Note that many default values are used. */
+
+ gc_values.foreground = FRAME_FOREGROUND_PIXEL (f);
+ gc_values.background = FRAME_BACKGROUND_PIXEL (f);
+ f->output_data.android->normal_gc
+ = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND,
+ &gc_values);
+
+ /* Reverse video style. */
+ gc_values.foreground = FRAME_BACKGROUND_PIXEL (f);
+ gc_values.background = FRAME_FOREGROUND_PIXEL (f);
+ f->output_data.android->reverse_gc
+ = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND,
+ &gc_values);
+
+ /* Cursor has cursor-color background, background-color foreground. */
+ gc_values.foreground = FRAME_BACKGROUND_PIXEL (f);
+ gc_values.background = f->output_data.android->cursor_pixel;
+ f->output_data.android->cursor_gc
+ = android_create_gc (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND,
+ &gc_values);
+ unblock_input ();
+}
+
+
+/* Free what was allocated in android_make_gc. */
+
+void
+android_free_gcs (struct frame *f)
+{
+ block_input ();
+
+ if (f->output_data.android->normal_gc)
+ {
+ android_free_gc (f->output_data.android->normal_gc);
+ f->output_data.android->normal_gc = 0;
+ }
+
+ if (f->output_data.android->reverse_gc)
+ {
+ android_free_gc (f->output_data.android->reverse_gc);
+ f->output_data.android->reverse_gc = 0;
+ }
+
+ if (f->output_data.android->cursor_gc)
+ {
+ android_free_gc (f->output_data.android->cursor_gc);
+ f->output_data.android->cursor_gc = 0;
+ }
+
+ unblock_input ();
+}
+
+/* Handler for signals raised during x_create_frame and
+ Fx_create_tip_frame. FRAME is the frame which is partially
+ constructed. */
+
+static Lisp_Object
+unwind_create_frame (Lisp_Object frame)
+{
+ struct frame *f = XFRAME (frame);
+
+ /* If frame is already dead, nothing to do. This can happen if the
+ display is disconnected after the frame has become official, but
+ before Fx_create_frame removes the unwind protect. */
+ if (!FRAME_LIVE_P (f))
+ return Qnil;
+
+ /* If frame is ``official'', nothing to do. */
+ if (NILP (Fmemq (frame, Vframe_list)))
+ {
+ /* If the frame's image cache refcount is still the same as our
+ private shadow variable, it means we are unwinding a frame
+ for which we didn't yet call init_frame_faces, where the
+ refcount is incremented. Therefore, we increment it here, so
+ that free_frame_faces, called in x_free_frame_resources
+ below, will not mistakenly decrement the counter that was not
+ incremented yet to account for this new frame. */
+ if (FRAME_IMAGE_CACHE (f) != NULL
+ && FRAME_IMAGE_CACHE (f)->refcount == image_cache_refcount)
+ FRAME_IMAGE_CACHE (f)->refcount++;
+
+ android_free_frame_resources (f);
+ free_glyphs (f);
+ return Qt;
+ }
+
+ return Qnil;
+}
+
+static void
+do_unwind_create_frame (Lisp_Object frame)
+{
+ unwind_create_frame (frame);
+}
+
+void
+android_default_font_parameter (struct frame *f, Lisp_Object parms)
+{
+ struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ Lisp_Object font_param = gui_display_get_arg (dpyinfo, parms, Qfont, NULL, NULL,
+ RES_TYPE_STRING);
+ Lisp_Object font = Qnil;
+ if (BASE_EQ (font_param, Qunbound))
+ font_param = Qnil;
+
+ if (NILP (font))
+ font = (!NILP (font_param)
+ ? font_param
+ : gui_display_get_arg (dpyinfo, parms,
+ Qfont, "font", "Font",
+ RES_TYPE_STRING));
+
+ if (! FONTP (font) && ! STRINGP (font))
+ {
+ const char *names[] = {
+ "Droid Sans Mono-12",
+ "Monospace-12",
+ "DroidSansMono-12",
+ NULL
+ };
+ int i;
+
+ for (i = 0; names[i]; i++)
+ {
+ font = font_open_by_name (f, build_unibyte_string (names[i]));
+ if (! NILP (font))
+ break;
+ }
+
+ if (NILP (font))
+ error ("No suitable font was found");
+ }
+
+ gui_default_parameter (f, parms, Qfont, font, "font", "Font", RES_TYPE_STRING);
+}
+
+static void
+android_create_frame_window (struct frame *f)
+{
+ struct android_set_window_attributes attributes;
+ enum android_window_value_mask attribute_mask;
+
+ attributes.background_pixel = FRAME_BACKGROUND_PIXEL (f);
+ attribute_mask = ANDROID_CW_BACK_PIXEL;
+
+ block_input ();
+ FRAME_ANDROID_WINDOW (f)
+ = android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
+ f->left_pos,
+ f->top_pos,
+ FRAME_PIXEL_WIDTH (f),
+ FRAME_PIXEL_HEIGHT (f),
+ attribute_mask, &attributes);
+ unblock_input ();
+}
+
+#endif /* ANDROID_STUBIFY */
+
+
+
+DEFUN ("x-create-frame", Fx_create_frame, Sx_create_frame,
+ 1, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object parms)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ struct frame *f;
+ Lisp_Object frame, tem;
+ Lisp_Object name;
+ bool minibuffer_only;
+ bool undecorated, override_redirect;
+ long window_prompting;
+ specpdl_ref count;
+ Lisp_Object display;
+ struct android_display_info *dpyinfo;
+ Lisp_Object parent, parent_frame;
+ struct kboard *kb;
+
+ minibuffer_only = false;
+ undecorated = false;
+ override_redirect = false;
+ window_prompting = 0;
+ count = SPECPDL_INDEX ();
+ dpyinfo = NULL;
+
+ /* Not actually used, but be consistent with X. */
+ ((void) window_prompting);
+
+ parms = Fcopy_alist (parms);
+
+ /* Use this general default value to start with
+ until we know if this frame has a specified name. */
+ Vx_resource_name = Vinvocation_name;
+
+ display = gui_display_get_arg (dpyinfo, parms, Qterminal, 0, 0,
+ RES_TYPE_NUMBER);
+ if (BASE_EQ (display, Qunbound))
+ display = gui_display_get_arg (dpyinfo, parms, Qdisplay, 0, 0,
+ RES_TYPE_STRING);
+ if (BASE_EQ (display, Qunbound))
+ display = Qnil;
+ dpyinfo = check_android_display_info (display);
+ kb = dpyinfo->terminal->kboard;
+
+ if (!dpyinfo->terminal->name)
+ error ("Terminal is not live, can't create new frames on it");
+
+ name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name",
+ RES_TYPE_STRING);
+ if (!STRINGP (name)
+ && ! BASE_EQ (name, Qunbound)
+ && ! NILP (name))
+ error ("Invalid frame name--not a string or nil");
+
+ if (STRINGP (name))
+ Vx_resource_name = name;
+
+ /* See if parent window is specified. */
+ parent = gui_display_get_arg (dpyinfo, parms, Qparent_id, NULL, NULL,
+ RES_TYPE_NUMBER);
+ if (BASE_EQ (parent, Qunbound))
+ parent = Qnil;
+ if (! NILP (parent))
+ CHECK_FIXNUM (parent);
+
+ frame = Qnil;
+ tem = gui_display_get_arg (dpyinfo,
+ parms, Qminibuffer, "minibuffer", "Minibuffer",
+ RES_TYPE_SYMBOL);
+ if (EQ (tem, Qnone) || NILP (tem))
+ f = make_frame_without_minibuffer (Qnil, kb, display);
+ else if (EQ (tem, Qonly))
+ {
+ f = make_minibuffer_frame ();
+ minibuffer_only = true;
+ }
+ else if (WINDOWP (tem))
+ f = make_frame_without_minibuffer (tem, kb, display);
+ else
+ f = make_frame (true);
+
+ parent_frame = gui_display_get_arg (dpyinfo,
+ parms,
+ Qparent_frame,
+ NULL,
+ NULL,
+ RES_TYPE_SYMBOL);
+ /* Accept parent-frame iff parent-id was not specified. */
+ if (!NILP (parent)
+ || BASE_EQ (parent_frame, Qunbound)
+ || NILP (parent_frame)
+ || !FRAMEP (parent_frame)
+ || !FRAME_LIVE_P (XFRAME (parent_frame))
+ || !FRAME_ANDROID_P (XFRAME (parent_frame)))
+ parent_frame = Qnil;
+
+ fset_parent_frame (f, parent_frame);
+ store_frame_param (f, Qparent_frame, parent_frame);
+
+ if (!NILP (tem = (gui_display_get_arg (dpyinfo,
+ parms,
+ Qundecorated,
+ NULL,
+ NULL,
+ RES_TYPE_BOOLEAN)))
+ && !(BASE_EQ (tem, Qunbound)))
+ undecorated = true;
+
+ FRAME_UNDECORATED (f) = undecorated;
+ store_frame_param (f, Qundecorated, undecorated ? Qt : Qnil);
+
+ if (!NILP (tem = (gui_display_get_arg (dpyinfo,
+ parms,
+ Qoverride_redirect,
+ NULL,
+ NULL,
+ RES_TYPE_BOOLEAN)))
+ && !(BASE_EQ (tem, Qunbound)))
+ override_redirect = true;
+
+ FRAME_OVERRIDE_REDIRECT (f) = override_redirect;
+ store_frame_param (f, Qoverride_redirect, override_redirect ? Qt : Qnil);
+
+ XSETFRAME (frame, f);
+
+ f->terminal = dpyinfo->terminal;
+
+ f->output_method = output_android;
+ f->output_data.android = xzalloc (sizeof *f->output_data.android);
+ FRAME_FONTSET (f) = -1;
+ f->output_data.android->scroll_bar_foreground_pixel = -1;
+ f->output_data.android->scroll_bar_background_pixel = -1;
+ f->output_data.android->white_relief.pixel = -1;
+ f->output_data.android->black_relief.pixel = -1;
+
+ fset_icon_name (f, gui_display_get_arg (dpyinfo,
+ parms,
+ Qicon_name,
+ "iconName",
+ "Title",
+ RES_TYPE_STRING));
+ if (! STRINGP (f->icon_name))
+ fset_icon_name (f, Qnil);
+
+ FRAME_DISPLAY_INFO (f) = dpyinfo;
+
+ /* With FRAME_DISPLAY_INFO set up, this unwind-protect is safe. */
+ record_unwind_protect (do_unwind_create_frame, frame);
+
+ /* These colors will be set anyway later, but it's important
+ to get the color reference counts right, so initialize them!
+
+ (Not really on Android, but it's best to be consistent with
+ X.) */
+ {
+ Lisp_Object black;
+
+ /* Function x_decode_color can signal an error. Make
+ sure to initialize color slots so that we won't try
+ to free colors we haven't allocated. */
+ FRAME_FOREGROUND_PIXEL (f) = -1;
+ FRAME_BACKGROUND_PIXEL (f) = -1;
+ f->output_data.android->cursor_pixel = -1;
+ f->output_data.android->cursor_foreground_pixel = -1;
+ f->output_data.android->mouse_pixel = -1;
+
+ black = build_string ("black");
+ FRAME_FOREGROUND_PIXEL (f)
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ FRAME_BACKGROUND_PIXEL (f)
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->cursor_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->cursor_foreground_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->mouse_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ }
+
+ /* Set the name; the functions to which we pass f expect the name to
+ be set. */
+ if (BASE_EQ (name, Qunbound) || NILP (name))
+ {
+ fset_name (f, build_string ("GNU Emacs"));
+ f->explicit_name = false;
+ }
+ else
+ {
+ fset_name (f, name);
+ f->explicit_name = true;
+ /* Use the frame's title when getting resources for this frame. */
+ specbind (Qx_resource_name, name);
+ }
+
+ register_font_driver (&androidfont_driver, f);
+ register_font_driver (&android_sfntfont_driver, f);
+
+ image_cache_refcount = (FRAME_IMAGE_CACHE (f)
+ ? FRAME_IMAGE_CACHE (f)->refcount
+ : 0);
+
+ gui_default_parameter (f, parms, Qfont_backend, Qnil,
+ "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+ /* Extract the window parameters from the supplied values
+ that are needed to determine window geometry. */
+ android_default_font_parameter (f, parms);
+ if (!FRAME_FONT (f))
+ {
+ delete_frame (frame, Qnoelisp);
+ error ("Invalid frame font");
+ }
+
+ if (NILP (Fassq (Qinternal_border_width, parms)))
+ {
+ Lisp_Object value;
+
+ value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
+ "internalBorder", "internalBorder",
+ RES_TYPE_NUMBER);
+ if (! BASE_EQ (value, Qunbound))
+ parms = Fcons (Fcons (Qinternal_border_width, value),
+ parms);
+ }
+
+ gui_default_parameter (f, parms, Qinternal_border_width,
+ make_fixnum (0),
+ "internalBorderWidth", "internalBorderWidth",
+ RES_TYPE_NUMBER);
+
+ /* Same for child frames. */
+ if (NILP (Fassq (Qchild_frame_border_width, parms)))
+ {
+ Lisp_Object value;
+
+ value = gui_display_get_arg (dpyinfo, parms, Qchild_frame_border_width,
+ "childFrameBorder", "childFrameBorder",
+ RES_TYPE_NUMBER);
+ if (! BASE_EQ (value, Qunbound))
+ parms = Fcons (Fcons (Qchild_frame_border_width, value),
+ parms);
+ }
+
+ gui_default_parameter (f, parms, Qchild_frame_border_width, Qnil,
+ "childFrameBorderWidth", "childFrameBorderWidth",
+ RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+
+ gui_default_parameter (f, parms, Qvertical_scroll_bars,
+ Qleft,
+ "verticalScrollBars", "ScrollBars",
+ RES_TYPE_SYMBOL);
+ gui_default_parameter (f, parms, Qhorizontal_scroll_bars, Qnil,
+ "horizontalScrollBars", "ScrollBars",
+ RES_TYPE_SYMBOL);
+
+ /* Also do the stuff which must be set before the window exists. */
+ gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+ "foreground", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+ "background", "Background", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
+ "pointerColor", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
+ "borderColor", "BorderColor", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qscreen_gamma, Qnil,
+ "screenGamma", "ScreenGamma", RES_TYPE_FLOAT);
+ gui_default_parameter (f, parms, Qline_spacing, Qnil,
+ "lineSpacing", "LineSpacing", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qleft_fringe, Qnil,
+ "leftFringe", "LeftFringe", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qright_fringe, Qnil,
+ "rightFringe", "RightFringe", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+
+#if 0
+ android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_foreground,
+ "scrollBarForeground",
+ "ScrollBarForeground", true);
+ android_default_scroll_bar_color_parameter (f, parms, Qscroll_bar_background,
+ "scrollBarBackground",
+ "ScrollBarBackground", false);
+#endif
+
+ /* Init faces before gui_default_parameter is called for the
+ scroll-bar-width parameter because otherwise we end up in
+ init_iterator with a null face cache, which should not
+ happen. */
+
+ init_frame_faces (f);
+
+ tem = gui_display_get_arg (dpyinfo, parms, Qmin_width, NULL, NULL,
+ RES_TYPE_NUMBER);
+ if (FIXNUMP (tem))
+ store_frame_param (f, Qmin_width, tem);
+ tem = gui_display_get_arg (dpyinfo, parms, Qmin_height, NULL, NULL,
+ RES_TYPE_NUMBER);
+ if (FIXNUMP (tem))
+ store_frame_param (f, Qmin_height, tem);
+
+ adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+ FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 5, true,
+ Qx_create_frame_1);
+
+ /* Set the menu-bar-lines and tool-bar-lines parameters. We don't
+ look up the X resources controlling the menu-bar and tool-bar
+ here; they are processed specially at startup, and reflected in
+ the values of the mode variables. */
+
+ gui_default_parameter (f, parms, Qmenu_bar_lines,
+ NILP (Vmenu_bar_mode)
+ ? make_fixnum (0) : make_fixnum (1),
+ NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qtab_bar_lines,
+ NILP (Vtab_bar_mode)
+ ? make_fixnum (0) : make_fixnum (1),
+ NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qtool_bar_lines,
+ NILP (Vtool_bar_mode)
+ ? make_fixnum (0) : make_fixnum (1),
+ NULL, NULL, RES_TYPE_NUMBER);
+
+ gui_default_parameter (f, parms, Qbuffer_predicate, Qnil,
+ "bufferPredicate", "BufferPredicate",
+ RES_TYPE_SYMBOL);
+ gui_default_parameter (f, parms, Qtitle, Qnil,
+ "title", "Title", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qwait_for_wm, Qt,
+ "waitForWM", "WaitForWM", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qtool_bar_position,
+ FRAME_TOOL_BAR_POSITION (f), 0, 0, RES_TYPE_SYMBOL);
+ gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+ "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+ RES_TYPE_BOOLEAN);
+
+ /* Compute the size of the X window. */
+ window_prompting = gui_figure_window_size (f, parms, true, true);
+
+ tem = gui_display_get_arg (dpyinfo, parms, Qunsplittable, 0, 0,
+ RES_TYPE_BOOLEAN);
+ f->no_split = minibuffer_only || EQ (tem, Qt);
+
+ android_icon_verify (f, parms);
+ android_create_frame_window (f);
+ android_icon (f, parms);
+ android_make_gc (f);
+
+ /* Now consider the frame official. */
+ f->terminal->reference_count++;
+ Vframe_list = Fcons (frame, Vframe_list);
+
+ /* We need to do this after creating the window, so that the
+ icon-creation functions can say whose icon they're
+ describing. */
+ gui_default_parameter (f, parms, Qicon_type, Qt,
+ "bitmapIcon", "BitmapIcon", RES_TYPE_BOOLEAN);
+
+ gui_default_parameter (f, parms, Qauto_raise, Qnil,
+ "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qauto_lower, Qnil,
+ "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qcursor_type, Qbox,
+ "cursorType", "CursorType", RES_TYPE_SYMBOL);
+ /* Scroll bars are not supported on Android, as they are near
+ useless. */
+#if 0
+ gui_default_parameter (f, parms, Qscroll_bar_width, Qnil,
+ "scrollBarWidth", "ScrollBarWidth",
+ RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qscroll_bar_height, Qnil,
+ "scrollBarHeight", "ScrollBarHeight",
+ RES_TYPE_NUMBER);
+#endif
+ gui_default_parameter (f, parms, Qalpha, Qnil,
+ "alpha", "Alpha", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qalpha_background, Qnil,
+ "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER);
+
+ if (!NILP (parent_frame))
+ {
+ struct frame *p = XFRAME (parent_frame);
+
+ block_input ();
+ android_reparent_window (FRAME_ANDROID_WINDOW (f),
+ FRAME_ANDROID_WINDOW (p),
+ f->left_pos, f->top_pos);
+ unblock_input ();
+ }
+
+ gui_default_parameter (f, parms, Qno_focus_on_map, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qno_accept_focus, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+
+ /* Consider frame official, now. */
+ f->can_set_window_size = true;
+
+ adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+ 0, true, Qx_create_frame_2);
+
+ /* Process fullscreen parameter here in the hope that normalizing a
+ fullheight/fullwidth frame will produce the size set by the last
+ adjust_frame_size call. Note that Android only supports the
+ `maximized' state. */
+ gui_default_parameter (f, parms, Qfullscreen, Qmaximized,
+ "fullscreen", "Fullscreen", RES_TYPE_SYMBOL);
+
+ /* When called from `x-create-frame-with-faces' visibility is
+ always explicitly nil. */
+ Lisp_Object visibility
+ = gui_display_get_arg (dpyinfo, parms, Qvisibility, 0, 0,
+ RES_TYPE_SYMBOL);
+ Lisp_Object height
+ = gui_display_get_arg (dpyinfo, parms, Qheight, 0, 0, RES_TYPE_NUMBER);
+ Lisp_Object width
+ = gui_display_get_arg (dpyinfo, parms, Qwidth, 0, 0, RES_TYPE_NUMBER);
+
+ if (EQ (visibility, Qicon))
+ {
+ f->was_invisible = true;
+ android_iconify_frame (f);
+ }
+ else
+ {
+ if (BASE_EQ (visibility, Qunbound))
+ visibility = Qt;
+
+ if (!NILP (visibility))
+ android_make_frame_visible (f);
+ else
+ f->was_invisible = true;
+ }
+
+ /* Leave f->was_invisible true only if height or width were
+ specified too. This takes effect only when we are not called
+ from `x-create-frame-with-faces' (see above comment). */
+ f->was_invisible
+ = (f->was_invisible
+ && (!BASE_EQ (height, Qunbound) || !BASE_EQ (width, Qunbound)));
+
+ store_frame_param (f, Qvisibility, visibility);
+
+ /* Set whether or not frame synchronization is enabled. */
+ gui_default_parameter (f, parms, Quse_frame_synchronization, Qt,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+
+ /* Works iff frame has been already mapped. */
+ gui_default_parameter (f, parms, Qskip_taskbar, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+ /* The `z-group' parameter works only for visible frames. */
+ gui_default_parameter (f, parms, Qz_group, Qnil,
+ NULL, NULL, RES_TYPE_SYMBOL);
+
+ /* Initialize `default-minibuffer-frame' in case this is the first
+ frame on this terminal. */
+ if (FRAME_HAS_MINIBUF_P (f)
+ && (!FRAMEP (KVAR (kb, Vdefault_minibuffer_frame))
+ || !FRAME_LIVE_P (XFRAME (KVAR (kb, Vdefault_minibuffer_frame)))))
+ kset_default_minibuffer_frame (kb, frame);
+
+ /* All remaining specified parameters, which have not been "used" by
+ gui_display_get_arg and friends, now go in the misc. alist of the
+ frame. */
+ for (tem = parms; CONSP (tem); tem = XCDR (tem))
+ if (CONSP (XCAR (tem)) && !NILP (XCAR (XCAR (tem))))
+ fset_param_alist (f, Fcons (XCAR (tem), f->param_alist));
+
+ /* Make sure windows on this frame appear in calls to next-window
+ and similar functions. */
+ Vwindow_list = Qnil;
+
+ return unbind_to (count, frame);
+#endif
+}
+
+DEFUN ("xw-color-defined-p", Fxw_color_defined_p, Sxw_color_defined_p,
+ 1, 2, 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object color, Lisp_Object frame)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ Emacs_Color foo;
+ struct frame *f;
+
+ f = decode_window_system_frame (frame);
+
+ CHECK_STRING (color);
+
+ if (android_defined_color (f, SSDATA (color), &foo, false, false))
+ return Qt;
+ else
+ return Qnil;
+#endif
+}
+
+DEFUN ("xw-color-values", Fxw_color_values, Sxw_color_values, 1, 2,
+ 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object color, Lisp_Object frame)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ Emacs_Color foo;
+ struct frame *f;
+
+ f = decode_window_system_frame (frame);
+
+ CHECK_STRING (color);
+
+ if (android_defined_color (f, SSDATA (color), &foo, false, false))
+ return list3i (foo.red, foo.green, foo.blue);
+ else
+ return Qnil;
+#endif
+}
+
+DEFUN ("xw-display-color-p", Fxw_display_color_p,
+ Sxw_display_color_p, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ return Qt;
+}
+
+DEFUN ("x-display-grayscale-p", Fx_display_grayscale_p,
+ Sx_display_grayscale_p, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ return Qnil;
+}
+
+DEFUN ("x-display-pixel-width", Fx_display_pixel_width,
+ Sx_display_pixel_width, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return make_fixnum (android_get_screen_width ());
+#endif
+}
+
+DEFUN ("x-display-pixel-height", Fx_display_pixel_height,
+ Sx_display_pixel_height, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return make_fixnum (android_get_screen_height ());
+#endif
+}
+
+DEFUN ("x-display-planes", Fx_display_planes, Sx_display_planes,
+ 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = check_android_display_info (terminal);
+
+ return make_fixnum (dpyinfo->n_planes);
+}
+
+DEFUN ("x-display-color-cells", Fx_display_color_cells, Sx_display_color_cells,
+ 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ struct android_display_info *dpyinfo;
+ int nr_planes;
+
+ dpyinfo = check_android_display_info (terminal);
+ nr_planes = dpyinfo->n_planes;
+
+ /* Truncate nr_planes to 24 to avoid integer overflow. */
+
+ if (nr_planes > 24)
+ nr_planes = 24;
+
+ return make_fixnum (1 << nr_planes);
+}
+
+DEFUN ("x-server-vendor", Fx_server_vendor, Sx_server_vendor, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ check_android_display_info (terminal);
+ return Vandroid_build_manufacturer;
+#endif
+}
+
+DEFUN ("x-server-version", Fx_server_version, Sx_server_version, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ check_android_display_info (terminal);
+ return list3i (android_get_current_api_level (), 0, 0);
+#endif
+}
+
+DEFUN ("x-display-screens", Fx_display_screens, Sx_display_screens,
+ 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ check_android_display_info (terminal);
+ return make_fixnum (1);
+}
+
+DEFUN ("x-display-mm-width", Fx_display_mm_width, Sx_display_mm_width,
+ 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return make_fixnum (android_get_mm_width ());
+#endif
+}
+
+DEFUN ("x-display-mm-height", Fx_display_mm_height, Sx_display_mm_height,
+ 0, 1, 0, doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return make_fixnum (android_get_mm_height ());
+#endif
+}
+
+DEFUN ("x-display-backing-store", Fx_display_backing_store,
+ Sx_display_backing_store, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ check_android_display_info (terminal);
+
+ /* The Java part is implemented in a way that it always does the
+ equivalent of backing store. */
+ return Qalways;
+}
+
+DEFUN ("x-display-visual-class", Fx_display_visual_class,
+ Sx_display_visual_class, 0, 1, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object terminal)
+{
+ check_android_display_info (terminal);
+
+ return Qtrue_color;
+}
+
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+android_make_monitor_attribute_list (struct MonitorInfo *monitors,
+ int n_monitors,
+ int primary_monitor)
+{
+ Lisp_Object monitor_frames;
+ Lisp_Object frame, rest;
+ struct frame *f;
+
+ monitor_frames = make_nil_vector (n_monitors);
+
+ FOR_EACH_FRAME (rest, frame)
+ {
+ f = XFRAME (frame);
+
+ /* Associate all frames with the primary monitor. */
+
+ if (FRAME_WINDOW_P (f)
+ && !FRAME_TOOLTIP_P (f))
+ ASET (monitor_frames, primary_monitor,
+ Fcons (frame, AREF (monitor_frames,
+ primary_monitor)));
+ }
+
+ return make_monitor_attribute_list (monitors, n_monitors,
+ primary_monitor,
+ monitor_frames, NULL);
+}
+
+#endif
+
+DEFUN ("android-display-monitor-attributes-list",
+ Fandroid_display_monitor_attributes_list,
+ Sandroid_display_monitor_attributes_list,
+ 0, 1, 0,
+ doc: /* Return a list of physical monitor attributes on the X display TERMINAL.
+
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be a terminal object, a frame or a display name (a string).
+If omitted or nil, that stands for the selected frame's display.
+
+Internal use only, use `display-monitor-attributes-list' instead. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ struct MonitorInfo monitor;
+
+ memset (&monitor, 0, sizeof monitor);
+ monitor.geom.width = android_get_screen_width ();
+ monitor.geom.height = android_get_screen_height ();
+ monitor.mm_width = android_get_mm_width ();
+ monitor.mm_height = android_get_mm_height ();
+ monitor.work = monitor.geom;
+ monitor.name = (char *) "Android device monitor";
+
+ return android_make_monitor_attribute_list (&monitor, 1, 0);
+#endif
+}
+
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+frame_geometry (Lisp_Object frame, Lisp_Object attribute)
+{
+ struct frame *f = decode_live_frame (frame);
+ android_window rootw;
+ unsigned int native_width, native_height, x_border_width = 0;
+ int x_native = 0, y_native = 0, xptr = 0, yptr = 0;
+ int left_off = 0, right_off = 0, top_off = 0, bottom_off = 0;
+ int outer_left, outer_top, outer_right, outer_bottom;
+ int native_left, native_top, native_right, native_bottom;
+ int inner_left, inner_top, inner_right, inner_bottom;
+ int internal_border_width;
+ bool menu_bar_external = false, tool_bar_external = false;
+ int menu_bar_height = 0, menu_bar_width = 0;
+ int tab_bar_height = 0, tab_bar_width = 0;
+ int tool_bar_height = 0, tool_bar_width = 0;
+
+ if (FRAME_INITIAL_P (f) || !FRAME_ANDROID_P (f)
+ || !FRAME_ANDROID_WINDOW (f))
+ return Qnil;
+
+ block_input ();
+ android_get_geometry (FRAME_ANDROID_WINDOW (f),
+ &rootw, &x_native, &y_native,
+ &native_width, &native_height, &x_border_width);
+ unblock_input ();
+
+ if (FRAME_PARENT_FRAME (f))
+ {
+ Lisp_Object parent, edges;
+
+ XSETFRAME (parent, FRAME_PARENT_FRAME (f));
+ edges = Fandroid_frame_edges (parent, Qnative_edges);
+ if (!NILP (edges))
+ {
+ x_native += XFIXNUM (Fnth (make_fixnum (0), edges));
+ y_native += XFIXNUM (Fnth (make_fixnum (1), edges));
+ }
+
+ outer_left = x_native;
+ outer_top = y_native;
+ outer_right = outer_left + native_width + 2 * x_border_width;
+ outer_bottom = outer_top + native_height + 2 * x_border_width;
+
+ native_left = x_native + x_border_width;
+ native_top = y_native + x_border_width;
+ native_right = native_left + native_width;
+ native_bottom = native_top + native_height;
+ }
+ else
+ {
+ outer_left = xptr;
+ outer_top = yptr;
+ outer_right = outer_left + left_off + native_width + right_off;
+ outer_bottom = outer_top + top_off + native_height + bottom_off;
+
+ native_left = outer_left + left_off;
+ native_top = outer_top + top_off;
+ native_right = native_left + native_width;
+ native_bottom = native_top + native_height;
+ }
+
+ internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f);
+ inner_left = native_left + internal_border_width;
+ inner_top = native_top + internal_border_width;
+ inner_right = native_right - internal_border_width;
+ inner_bottom = native_bottom - internal_border_width;
+
+ menu_bar_height = FRAME_MENU_BAR_HEIGHT (f);
+ inner_top += menu_bar_height;
+ menu_bar_width = menu_bar_height ? native_width : 0;
+
+ tab_bar_height = FRAME_TAB_BAR_HEIGHT (f);
+ tab_bar_width = (tab_bar_height
+ ? native_width - 2 * internal_border_width
+ : 0);
+ inner_top += tab_bar_height;
+
+ tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f);
+ tool_bar_width = (tool_bar_height
+ ? native_width - 2 * internal_border_width
+ : 0);
+ inner_top += tool_bar_height;
+
+ /* Construct list. */
+ if (EQ (attribute, Qouter_edges))
+ return list4i (outer_left, outer_top, outer_right, outer_bottom);
+ else if (EQ (attribute, Qnative_edges))
+ return list4i (native_left, native_top, native_right, native_bottom);
+ else if (EQ (attribute, Qinner_edges))
+ return list4i (inner_left, inner_top, inner_right, inner_bottom);
+ else
+ return
+ list (Fcons (Qouter_position,
+ Fcons (make_fixnum (outer_left),
+ make_fixnum (outer_top))),
+ Fcons (Qouter_size,
+ Fcons (make_fixnum (outer_right - outer_left),
+ make_fixnum (outer_bottom - outer_top))),
+ /* Approximate. */
+ Fcons (Qexternal_border_size,
+ Fcons (make_fixnum (right_off),
+ make_fixnum (bottom_off))),
+ Fcons (Qouter_border_width, make_fixnum (x_border_width)),
+ /* Approximate. */
+ Fcons (Qtitle_bar_size,
+ Fcons (make_fixnum (0),
+ make_fixnum (top_off - bottom_off))),
+ Fcons (Qmenu_bar_external, menu_bar_external ? Qt : Qnil),
+ Fcons (Qmenu_bar_size,
+ Fcons (make_fixnum (menu_bar_width),
+ make_fixnum (menu_bar_height))),
+ Fcons (Qtab_bar_size,
+ Fcons (make_fixnum (tab_bar_width),
+ make_fixnum (tab_bar_height))),
+ Fcons (Qtool_bar_external, tool_bar_external ? Qt : Qnil),
+ Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)),
+ Fcons (Qtool_bar_size,
+ Fcons (make_fixnum (tool_bar_width),
+ make_fixnum (tool_bar_height))),
+ Fcons (Qinternal_border_width,
+ make_fixnum (internal_border_width)));
+}
+
+#endif
+
+DEFUN ("android-frame-geometry", Fandroid_frame_geometry,
+ Sandroid_frame_geometry,
+ 0, 1, 0,
+ doc: /* Return geometric attributes of FRAME.
+FRAME must be a live frame and defaults to the selected one. The return
+value is an association list of the attributes listed below. All height
+and width values are in pixels.
+
+`outer-position' is a cons of the outer left and top edges of FRAME
+ relative to the origin - the position (0, 0) - of FRAME's display.
+
+`outer-size' is a cons of the outer width and height of FRAME. The
+ outer size includes the title bar and the external borders as well as
+ any menu and/or tool bar of frame.
+
+`external-border-size' is a cons of the horizontal and vertical width of
+ FRAME's external borders as supplied by the window manager.
+
+`title-bar-size' is a cons of the width and height of the title bar of
+ FRAME as supplied by the window manager. If both of them are zero,
+ FRAME has no title bar. If only the width is zero, Emacs was not
+ able to retrieve the width information.
+
+`menu-bar-external', if non-nil, means the menu bar is external (never
+ included in the inner edges of FRAME).
+
+`menu-bar-size' is a cons of the width and height of the menu bar of
+ FRAME.
+
+`tool-bar-external', if non-nil, means the tool bar is external (never
+ included in the inner edges of FRAME).
+
+`tool-bar-position' tells on which side the tool bar on FRAME is and can
+ be one of `left', `top', `right' or `bottom'. If this is nil, FRAME
+ has no tool bar.
+
+`tool-bar-size' is a cons of the width and height of the tool bar of
+ FRAME.
+
+`internal-border-width' is the width of the internal border of
+ FRAME. */)
+ (Lisp_Object frame)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return frame_geometry (frame, Qnil);
+#endif
+}
+
+DEFUN ("android-frame-edges", Fandroid_frame_edges, Sandroid_frame_edges, 0, 2, 0,
+ doc: /* Return edge coordinates of FRAME.
+FRAME must be a live frame and defaults to the selected one. The return
+value is a list of the form (LEFT, TOP, RIGHT, BOTTOM). All values are
+in pixels relative to the origin - the position (0, 0) - of FRAME's
+display.
+
+If optional argument TYPE is the symbol `outer-edges', return the outer
+edges of FRAME. The outer edges comprise the decorations of the window
+manager (like the title bar or external borders) as well as any external
+menu or tool bar of FRAME. If optional argument TYPE is the symbol
+`native-edges' or nil, return the native edges of FRAME. The native
+edges exclude the decorations of the window manager and any external
+menu or tool bar of FRAME. If TYPE is the symbol `inner-edges', return
+the inner edges of FRAME. These edges exclude title bar, any borders,
+menu bar or tool bar of FRAME. */)
+ (Lisp_Object frame, Lisp_Object type)
+{
+#ifndef ANDROID_STUBIFY
+ return frame_geometry (frame, ((EQ (type, Qouter_edges)
+ || EQ (type, Qinner_edges))
+ ? type
+ : Qnative_edges));
+#else
+ return Qnil;
+#endif
+}
+
+#ifndef ANDROID_STUBIFY
+
+static Lisp_Object
+android_frame_list_z_order (struct android_display_info *dpyinfo,
+ android_window window)
+{
+ android_window root, parent, *children;
+ unsigned int nchildren;
+ unsigned long i;
+ Lisp_Object frames;
+
+ frames = Qnil;
+
+ if (android_query_tree (window, &root, &parent,
+ &children, &nchildren))
+ {
+ for (i = 0; i < nchildren; i++)
+ {
+ Lisp_Object frame, tail;
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ struct frame *cf = XFRAME (frame);
+
+ if (FRAME_ANDROID_P (cf)
+ && (FRAME_ANDROID_WINDOW (cf) == children[i]))
+ frames = Fcons (frame, frames);
+ }
+ }
+
+ if (children)
+ xfree (children);
+ }
+
+ return frames;
+}
+
+#endif
+
+DEFUN ("android-frame-list-z-order", Fandroid_frame_list_z_order,
+ Sandroid_frame_list_z_order, 0, 1, 0,
+ doc: /* Return list of Emacs' frames, in Z (stacking) order.
+The optional argument TERMINAL specifies which display to ask about.
+TERMINAL should be either a frame or a display name (a string). If
+omitted or nil, that stands for the selected frame's display. Return
+nil if TERMINAL contains no Emacs frame.
+
+As a special case, if TERMINAL is non-nil and specifies a live frame,
+return the child frames of that frame in Z (stacking) order.
+
+Frames are listed from topmost (first) to bottommost (last).
+
+On Android, the order of the frames returned is undefined unless
+TERMINAL is a frame. */)
+ (Lisp_Object terminal)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ struct android_display_info *dpyinfo;
+ android_window window;
+
+ dpyinfo = check_android_display_info (terminal);
+
+ if (FRAMEP (terminal) && FRAME_LIVE_P (XFRAME (terminal)))
+ window = FRAME_ANDROID_WINDOW (XFRAME (terminal));
+ else
+ window = dpyinfo->root_window;
+
+ return android_frame_list_z_order (dpyinfo, window);
+#endif
+}
+
+DEFUN ("android-frame-restack", Fandroid_frame_restack,
+ Sandroid_frame_restack, 2, 3, 0,
+ doc: /* Restack FRAME1 below FRAME2.
+This means that if both frames are visible and the display areas of
+these frames overlap, FRAME2 (partially) obscures FRAME1. If optional
+third argument ABOVE is non-nil, restack FRAME1 above FRAME2. This
+means that if both frames are visible and the display areas of these
+frames overlap, FRAME1 (partially) obscures FRAME2.
+
+This may be thought of as an atomic action performed in two steps: The
+first step removes FRAME1's window-step window from the display. The
+second step reinserts FRAME1's window below (above if ABOVE is true)
+that of FRAME2. Hence the position of FRAME2 in its display's Z
+\(stacking) order relative to all other frames excluding FRAME1 remains
+unaltered.
+
+The Android system refuses to restack windows, so this does not
+work. */)
+ (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object frame3)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ /* This is not supported on Android because of limitations in the
+ platform that prevent ViewGroups from restacking
+ SurfaceViews. */
+ return Qnil;
+#endif
+}
+
+DEFUN ("android-mouse-absolute-pixel-position",
+ Fandroid_mouse_absolute_pixel_position,
+ Sandroid_mouse_absolute_pixel_position, 0, 0, 0,
+ doc: /* Return absolute position of mouse cursor in pixels.
+The position is returned as a cons cell (X . Y) of the coordinates of
+the mouse cursor position in pixels relative to a position (0, 0) of the
+selected frame's display. This does not work on Android. */)
+ (void)
+{
+ /* This cannot be implemented on Android. */
+ return Qnil;
+}
+
+DEFUN ("android-set-mouse-absolute-pixel-position",
+ Fandroid_set_mouse_absolute_pixel_position,
+ Sandroid_set_mouse_absolute_pixel_position, 2, 2, 0,
+ doc: /* Move mouse pointer to a pixel position at (X, Y). The
+coordinates X and Y are interpreted to start from the top-left corner
+of the screen. This does not work on Android. */)
+ (Lisp_Object x, Lisp_Object y)
+{
+ /* This cannot be implemented on Android. */
+ return Qnil;
+}
+
+DEFUN ("android-get-connection", Fandroid_get_connection,
+ Sandroid_get_connection, 0, 0, 0,
+ doc: /* Get the connection to the display server.
+Return the terminal if it exists, else nil.
+
+Emacs cannot open a connection to the display server itself under
+Android, so there is no equivalent of `x-open-connection'. */)
+ (void)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ Lisp_Object terminal;
+
+ terminal = Qnil;
+
+ if (x_display_list)
+ XSETTERMINAL (terminal, x_display_list->terminal);
+
+ return terminal;
+#endif
+}
+
+DEFUN ("x-display-list", Fx_display_list, Sx_display_list, 0, 0, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (void)
+{
+ Lisp_Object result;
+
+ result = Qnil;
+
+ if (x_display_list)
+ result = Fcons (XCAR (x_display_list->name_list_element),
+ result);
+
+ return result;
+}
+
+#ifndef ANDROID_STUBIFY
+
+static void
+unwind_create_tip_frame (Lisp_Object frame)
+{
+ Lisp_Object deleted;
+
+ deleted = unwind_create_frame (frame);
+ if (EQ (deleted, Qt))
+ {
+ tip_window = ANDROID_NONE;
+ tip_frame = Qnil;
+ }
+}
+
+static Lisp_Object
+android_create_tip_frame (struct android_display_info *dpyinfo,
+ Lisp_Object parms)
+{
+ struct frame *f;
+ Lisp_Object frame;
+ Lisp_Object name;
+ specpdl_ref count = SPECPDL_INDEX ();
+ bool face_change_before = face_change;
+
+ if (!dpyinfo->terminal->name)
+ error ("Terminal is not live, can't create new frames on it");
+
+ parms = Fcopy_alist (parms);
+
+ /* Get the name of the frame to use for resource lookup. */
+ name = gui_display_get_arg (dpyinfo, parms, Qname, "name", "Name",
+ RES_TYPE_STRING);
+ if (!STRINGP (name)
+ && !BASE_EQ (name, Qunbound)
+ && !NILP (name))
+ error ("Invalid frame name--not a string or nil");
+
+ frame = Qnil;
+ f = make_frame (false);
+ f->wants_modeline = false;
+ XSETFRAME (frame, f);
+ record_unwind_protect (unwind_create_tip_frame, frame);
+
+ f->terminal = dpyinfo->terminal;
+
+ /* By setting the output method, we're essentially saying that
+ the frame is live, as per FRAME_LIVE_P. If we get a signal
+ from this point on, x_destroy_window might screw up reference
+ counts etc. */
+ f->output_method = output_android;
+ f->output_data.android = xzalloc (sizeof *f->output_data.android);
+ FRAME_FONTSET (f) = -1;
+ f->output_data.android->white_relief.pixel = -1;
+ f->output_data.android->black_relief.pixel = -1;
+
+ f->tooltip = true;
+ fset_icon_name (f, Qnil);
+ FRAME_DISPLAY_INFO (f) = dpyinfo;
+ f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
+
+ /* These colors will be set anyway later, but it's important
+ to get the color reference counts right, so initialize them! */
+ {
+ Lisp_Object black;
+
+ /* Function android_decode_color can signal an error. Make sure
+ to initialize color slots so that we won't try to free colors
+ we haven't allocated. */
+ FRAME_FOREGROUND_PIXEL (f) = -1;
+ FRAME_BACKGROUND_PIXEL (f) = -1;
+ f->output_data.android->cursor_pixel = -1;
+ f->output_data.android->cursor_foreground_pixel = -1;
+ f->output_data.android->mouse_pixel = -1;
+
+ black = build_string ("black");
+ FRAME_FOREGROUND_PIXEL (f)
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ FRAME_BACKGROUND_PIXEL (f)
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->cursor_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->cursor_foreground_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ f->output_data.android->mouse_pixel
+ = android_decode_color (f, black, BLACK_PIX_DEFAULT (f));
+ }
+
+ /* Set the name; the functions to which we pass f expect the name to
+ be set. */
+ if (BASE_EQ (name, Qunbound) || NILP (name))
+ f->explicit_name = false;
+ else
+ {
+ fset_name (f, name);
+ f->explicit_name = true;
+ /* use the frame's title when getting resources for this frame. */
+ specbind (Qx_resource_name, name);
+ }
+
+ register_font_driver (&androidfont_driver, f);
+ register_font_driver (&android_sfntfont_driver, f);
+
+ image_cache_refcount
+ = FRAME_IMAGE_CACHE (f) ? FRAME_IMAGE_CACHE (f)->refcount : 0;
+#ifdef GLYPH_DEBUG
+ dpyinfo_refcount = dpyinfo->reference_count;
+#endif /* GLYPH_DEBUG */
+
+ gui_default_parameter (f, parms, Qfont_backend, Qnil,
+ "fontBackend", "FontBackend", RES_TYPE_STRING);
+
+ /* Extract the window parameters from the supplied values that are
+ needed to determine window geometry. */
+ android_default_font_parameter (f, parms);
+
+ gui_default_parameter (f, parms, Qborder_width, make_fixnum (0),
+ "borderWidth", "BorderWidth", RES_TYPE_NUMBER);
+
+ /* This defaults to 1 in order to match xterm. We recognize either
+ internalBorderWidth or internalBorder (which is what xterm calls
+ it). */
+ if (NILP (Fassq (Qinternal_border_width, parms)))
+ {
+ Lisp_Object value;
+
+ value = gui_display_get_arg (dpyinfo, parms, Qinternal_border_width,
+ "internalBorder", "internalBorder",
+ RES_TYPE_NUMBER);
+ if (! BASE_EQ (value, Qunbound))
+ parms = Fcons (Fcons (Qinternal_border_width, value),
+ parms);
+ }
+
+ gui_default_parameter (f, parms, Qinternal_border_width, make_fixnum (1),
+ "internalBorderWidth", "internalBorderWidth",
+ RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qright_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qbottom_divider_width, make_fixnum (0),
+ NULL, NULL, RES_TYPE_NUMBER);
+
+ /* Also do the stuff which must be set before the window exists. */
+ gui_default_parameter (f, parms, Qforeground_color, build_string ("black"),
+ "foreground", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qbackground_color, build_string ("white"),
+ "background", "Background", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qmouse_color, build_string ("black"),
+ "pointerColor", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qcursor_color, build_string ("black"),
+ "cursorColor", "Foreground", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qborder_color, build_string ("black"),
+ "borderColor", "BorderColor", RES_TYPE_STRING);
+ gui_default_parameter (f, parms, Qno_special_glyphs, Qnil,
+ NULL, NULL, RES_TYPE_BOOLEAN);
+
+ {
+ struct android_set_window_attributes attrs;
+ unsigned long mask;
+
+ block_input ();
+ mask = ANDROID_CW_OVERRIDE_REDIRECT | ANDROID_CW_BACK_PIXEL;
+
+ attrs.override_redirect = true;
+ attrs.background_pixel = FRAME_BACKGROUND_PIXEL (f);
+ tip_window
+ = FRAME_ANDROID_WINDOW (f)
+ = android_create_window (FRAME_DISPLAY_INFO (f)->root_window,
+ /* x, y, width, height, value-mask,
+ attrs. */
+ 0, 0, 1, 1, mask, &attrs);
+ unblock_input ();
+ }
+
+ /* Init faces before gui_default_parameter is called for the
+ scroll-bar-width parameter because otherwise we end up in
+ init_iterator with a null face cache, which should not happen. */
+ init_frame_faces (f);
+
+ gui_default_parameter (f, parms, Qinhibit_double_buffering, Qnil,
+ "inhibitDoubleBuffering", "InhibitDoubleBuffering",
+ RES_TYPE_BOOLEAN);
+
+ gui_figure_window_size (f, parms, false, false);
+
+ f->output_data.android->parent_desc = FRAME_DISPLAY_INFO (f)->root_window;
+
+ android_make_gc (f);
+
+ gui_default_parameter (f, parms, Qauto_raise, Qnil,
+ "autoRaise", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qauto_lower, Qnil,
+ "autoLower", "AutoRaiseLower", RES_TYPE_BOOLEAN);
+ gui_default_parameter (f, parms, Qcursor_type, Qbox,
+ "cursorType", "CursorType", RES_TYPE_SYMBOL);
+ gui_default_parameter (f, parms, Qalpha, Qnil,
+ "alpha", "Alpha", RES_TYPE_NUMBER);
+ gui_default_parameter (f, parms, Qalpha_background, Qnil,
+ "alphaBackground", "AlphaBackground", RES_TYPE_NUMBER);
+
+ /* Add `tooltip' frame parameter's default value. */
+ if (NILP (Fframe_parameter (frame, Qtooltip)))
+ {
+ AUTO_FRAME_ARG (arg, Qtooltip, Qt);
+ Fmodify_frame_parameters (frame, arg);
+ }
+
+ /* FIXME - can this be done in a similar way to normal frames?
+ https://lists.gnu.org/r/emacs-devel/2007-10/msg00641.html */
+
+ /* Set the `display-type' frame parameter before setting up faces. */
+ {
+ Lisp_Object disptype;
+
+ disptype = Qcolor;
+
+ if (NILP (Fframe_parameter (frame, Qdisplay_type)))
+ {
+ AUTO_FRAME_ARG (arg, Qdisplay_type, disptype);
+ Fmodify_frame_parameters (frame, arg);
+ }
+ }
+
+ /* Set up faces after all frame parameters are known. This call
+ also merges in face attributes specified for new frames. */
+ {
+ Lisp_Object bg = Fframe_parameter (frame, Qbackground_color);
+
+ call2 (Qface_set_after_frame_default, frame, Qnil);
+
+ if (!EQ (bg, Fframe_parameter (frame, Qbackground_color)))
+ {
+ AUTO_FRAME_ARG (arg, Qbackground_color, bg);
+ Fmodify_frame_parameters (frame, arg);
+ }
+ }
+
+ f->no_split = true;
+
+ /* Now that the frame will be official, it counts as a reference to
+ its display and terminal. */
+ f->terminal->reference_count++;
+
+ /* It is now ok to make the frame official even if we get an error
+ below. And the frame needs to be on Vframe_list or making it
+ visible won't work. */
+ Vframe_list = Fcons (frame, Vframe_list);
+ f->can_set_window_size = true;
+ adjust_frame_size (f, FRAME_TEXT_WIDTH (f), FRAME_TEXT_HEIGHT (f),
+ 0, true, Qtip_frame);
+
+ /* Setting attributes of faces of the tooltip frame from resources
+ and similar will set face_change, which leads to the clearing of
+ all current matrices. Since this isn't necessary here, avoid it
+ by resetting face_change to the value it had before we created
+ the tip frame. */
+ face_change = face_change_before;
+
+ /* Discard the unwind_protect. */
+ return unbind_to (count, frame);
+}
+
+static Lisp_Object
+android_hide_tip (bool delete)
+{
+ if (!NILP (tip_timer))
+ {
+ call1 (Qcancel_timer, tip_timer);
+ tip_timer = Qnil;
+ }
+
+ if (NILP (tip_frame)
+ || (!delete
+ && !NILP (tip_frame)
+ && FRAME_LIVE_P (XFRAME (tip_frame))
+ && !FRAME_VISIBLE_P (XFRAME (tip_frame))))
+ return Qnil;
+ else
+ {
+ Lisp_Object was_open = Qnil;
+
+ specpdl_ref count = SPECPDL_INDEX ();
+ specbind (Qinhibit_redisplay, Qt);
+ specbind (Qinhibit_quit, Qt);
+
+ if (!NILP (tip_frame))
+ {
+ struct frame *f = XFRAME (tip_frame);
+
+ if (FRAME_LIVE_P (f))
+ {
+ if (delete)
+ {
+ delete_frame (tip_frame, Qnil);
+ tip_frame = Qnil;
+ }
+ else
+ android_make_frame_invisible (XFRAME (tip_frame));
+
+ was_open = Qt;
+ }
+ else
+ tip_frame = Qnil;
+ }
+ else
+ tip_frame = Qnil;
+
+ return unbind_to (count, was_open);
+ }
+}
+
+static void
+compute_tip_xy (struct frame *f, Lisp_Object parms, Lisp_Object dx,
+ Lisp_Object dy, int width, int height, int *root_x,
+ int *root_y)
+{
+ Lisp_Object left, top, right, bottom;
+ int min_x, min_y, max_x, max_y = -1;
+ android_window window;
+ struct frame *mouse_frame;
+
+ /* Initialize these values in case there is no mouse frame. */
+ *root_x = 0;
+ *root_y = 0;
+
+ /* User-specified position? */
+ left = CDR (Fassq (Qleft, parms));
+ top = CDR (Fassq (Qtop, parms));
+ right = CDR (Fassq (Qright, parms));
+ bottom = CDR (Fassq (Qbottom, parms));
+
+ /* Move the tooltip window where the mouse pointer was last seen.
+ Resize and show it. */
+ if ((!FIXNUMP (left) && !FIXNUMP (right))
+ || (!FIXNUMP (top) && !FIXNUMP (bottom)))
+ {
+ if (x_display_list->last_mouse_motion_frame)
+ {
+ *root_x = x_display_list->last_mouse_motion_x;
+ *root_y = x_display_list->last_mouse_motion_y;
+ mouse_frame = x_display_list->last_mouse_motion_frame;
+ window = FRAME_ANDROID_WINDOW (mouse_frame);
+
+ /* Translate the coordinates to the screen. */
+ android_translate_coordinates (window, *root_x, *root_y,
+ root_x, root_y);
+ }
+ }
+
+ min_x = 0;
+ min_y = 0;
+ max_x = android_get_screen_width ();
+ max_y = android_get_screen_height ();
+
+ if (FIXNUMP (top))
+ *root_y = XFIXNUM (top);
+ else if (FIXNUMP (bottom))
+ *root_y = XFIXNUM (bottom) - height;
+ else if (*root_y + XFIXNUM (dy) <= min_y)
+ *root_y = min_y; /* Can happen for negative dy */
+ else if (*root_y + XFIXNUM (dy) + height <= max_y)
+ /* It fits below the pointer */
+ *root_y += XFIXNUM (dy);
+ else if (height + XFIXNUM (dy) + min_y <= *root_y)
+ /* It fits above the pointer. */
+ *root_y -= height + XFIXNUM (dy);
+ else
+ /* Put it on the top. */
+ *root_y = min_y;
+
+ if (FIXNUMP (left))
+ *root_x = XFIXNUM (left);
+ else if (FIXNUMP (right))
+ *root_x = XFIXNUM (right) - width;
+ else if (*root_x + XFIXNUM (dx) <= min_x)
+ *root_x = 0; /* Can happen for negative dx */
+ else if (*root_x + XFIXNUM (dx) + width <= max_x)
+ /* It fits to the right of the pointer. */
+ *root_x += XFIXNUM (dx);
+ else if (width + XFIXNUM (dx) + min_x <= *root_x)
+ /* It fits to the left of the pointer. */
+ *root_x -= width + XFIXNUM (dx);
+ else
+ /* Put it left justified on the screen -- it ought to fit that way. */
+ *root_x = min_x;
+}
+
+#endif
+
+DEFUN ("x-show-tip", Fx_show_tip, Sx_show_tip, 1, 6, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (Lisp_Object string, Lisp_Object frame, Lisp_Object parms,
+ Lisp_Object timeout, Lisp_Object dx, Lisp_Object dy)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ struct frame *f, *tip_f;
+ struct window *w;
+ int root_x, root_y;
+ struct buffer *old_buffer;
+ struct text_pos pos;
+ int width, height;
+ int old_windows_or_buffers_changed = windows_or_buffers_changed;
+ specpdl_ref count = SPECPDL_INDEX ();
+ Lisp_Object window, size, tip_buf;
+ bool displayed;
+#ifdef ENABLE_CHECKING
+ struct glyph_row *row, *end;
+#endif
+ AUTO_STRING (tip, " *tip*");
+
+ specbind (Qinhibit_redisplay, Qt);
+
+ CHECK_STRING (string);
+ if (SCHARS (string) == 0)
+ string = make_unibyte_string (" ", 1);
+
+ if (NILP (frame))
+ frame = selected_frame;
+ f = decode_window_system_frame (frame);
+
+ if (NILP (timeout))
+ timeout = Vx_show_tooltip_timeout;
+ CHECK_FIXNAT (timeout);
+
+ if (NILP (dx))
+ dx = make_fixnum (5);
+ else
+ CHECK_FIXNUM (dx);
+
+ if (NILP (dy))
+ dy = make_fixnum (-10);
+ else
+ CHECK_FIXNUM (dy);
+
+ tip_dx = dx;
+ tip_dy = dy;
+
+ if (!NILP (tip_frame) && FRAME_LIVE_P (XFRAME (tip_frame)))
+ {
+ if (FRAME_VISIBLE_P (XFRAME (tip_frame))
+ && !NILP (Fequal_including_properties (tip_last_string,
+ string))
+ && !NILP (Fequal (tip_last_parms, parms)))
+ {
+ /* Only DX and DY have changed. */
+ tip_f = XFRAME (tip_frame);
+ if (!NILP (tip_timer))
+ {
+ call1 (Qcancel_timer, tip_timer);
+ tip_timer = Qnil;
+ }
+
+ block_input ();
+ compute_tip_xy (tip_f, parms, dx, dy, FRAME_PIXEL_WIDTH (tip_f),
+ FRAME_PIXEL_HEIGHT (tip_f), &root_x, &root_y);
+ android_move_window (FRAME_ANDROID_WINDOW (tip_f),
+ root_x, root_y);
+ unblock_input ();
+
+ goto start_timer;
+ }
+ else
+ android_hide_tip (true);
+ }
+ else
+ android_hide_tip (true);
+
+ tip_last_frame = frame;
+ tip_last_string = string;
+ tip_last_parms = parms;
+
+ if (NILP (tip_frame) || !FRAME_LIVE_P (XFRAME (tip_frame)))
+ {
+ /* Add default values to frame parameters. */
+ if (NILP (Fassq (Qname, parms)))
+ parms = Fcons (Fcons (Qname, build_string ("tooltip")), parms);
+ if (NILP (Fassq (Qinternal_border_width, parms)))
+ parms = Fcons (Fcons (Qinternal_border_width, make_fixnum (3)),
+ parms);
+ if (NILP (Fassq (Qborder_width, parms)))
+ parms = Fcons (Fcons (Qborder_width, make_fixnum (1)), parms);
+ if (NILP (Fassq (Qborder_color, parms)))
+ parms = Fcons (Fcons (Qborder_color, build_string ("lightyellow")),
+ parms);
+ if (NILP (Fassq (Qbackground_color, parms)))
+ parms = Fcons (Fcons (Qbackground_color,
+ build_string ("lightyellow")),
+ parms);
+
+ /* Create a frame for the tooltip, and record it in the global
+ variable tip_frame. */
+ if (NILP (tip_frame = android_create_tip_frame (FRAME_DISPLAY_INFO (f),
+ parms)))
+ /* Creating the tip frame failed. */
+ return unbind_to (count, Qnil);
+ }
+
+ tip_f = XFRAME (tip_frame);
+ window = FRAME_ROOT_WINDOW (tip_f);
+ tip_buf = Fget_buffer_create (tip, Qnil);
+ /* We will mark the tip window a "pseudo-window" below, and such
+ windows cannot have display margins. */
+ bset_left_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+ bset_right_margin_cols (XBUFFER (tip_buf), make_fixnum (0));
+ set_window_buffer (window, tip_buf, false, false);
+ w = XWINDOW (window);
+ w->pseudo_window_p = true;
+ /* Try to avoid that `other-window' select us (Bug#47207). */
+ Fset_window_parameter (window, Qno_other_window, Qt);
+
+ /* Set up the frame's root window. Note: The following code does not
+ try to size the window or its frame correctly. Its only purpose is
+ to make the subsequent text size calculations work. The right
+ sizes should get installed when the toolkit gets back to us. */
+ w->left_col = 0;
+ w->top_line = 0;
+ w->pixel_left = 0;
+ w->pixel_top = 0;
+
+ if (CONSP (Vx_max_tooltip_size)
+ && RANGED_FIXNUMP (1, XCAR (Vx_max_tooltip_size), INT_MAX)
+ && RANGED_FIXNUMP (1, XCDR (Vx_max_tooltip_size), INT_MAX))
+ {
+ w->total_cols = XFIXNAT (XCAR (Vx_max_tooltip_size));
+ w->total_lines = XFIXNAT (XCDR (Vx_max_tooltip_size));
+ }
+ else
+ {
+ w->total_cols = 80;
+ w->total_lines = 40;
+ }
+
+ w->pixel_width = w->total_cols * FRAME_COLUMN_WIDTH (tip_f);
+ w->pixel_height = w->total_lines * FRAME_LINE_HEIGHT (tip_f);
+ FRAME_TOTAL_COLS (tip_f) = w->total_cols;
+ adjust_frame_glyphs (tip_f);
+
+ /* Insert STRING into root window's buffer and fit the frame to the
+ buffer. */
+ specpdl_ref count_1 = SPECPDL_INDEX ();
+ old_buffer = current_buffer;
+ set_buffer_internal_1 (XBUFFER (w->contents));
+ bset_truncate_lines (current_buffer, Qnil);
+ specbind (Qinhibit_read_only, Qt);
+ specbind (Qinhibit_modification_hooks, Qt);
+ specbind (Qinhibit_point_motion_hooks, Qt);
+ Ferase_buffer ();
+ Finsert (1, &string);
+ clear_glyph_matrix (w->desired_matrix);
+ clear_glyph_matrix (w->current_matrix);
+ SET_TEXT_POS (pos, BEGV, BEGV_BYTE);
+ displayed = try_window (window, pos, TRY_WINDOW_IGNORE_FONTS_CHANGE);
+
+ if (!displayed && NILP (Vx_max_tooltip_size))
+ {
+#ifdef ENABLE_CHECKING
+ row = w->desired_matrix->rows;
+ end = w->desired_matrix->rows + w->desired_matrix->nrows;
+
+ while (row < end)
+ {
+ if (!row->displays_text_p
+ || row->ends_at_zv_p)
+ break;
+ ++row;
+ }
+
+ eassert (row < end && row->ends_at_zv_p);
+#endif
+ }
+
+ /* Calculate size of tooltip window. */
+ size = Fwindow_text_pixel_size (window, Qnil, Qnil, Qnil,
+ make_fixnum (w->pixel_height), Qnil,
+ Qnil);
+ /* Add the frame's internal border to calculated size. */
+ width = XFIXNUM (CAR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+ height = XFIXNUM (CDR (size)) + 2 * FRAME_INTERNAL_BORDER_WIDTH (tip_f);
+
+ /* Calculate position of tooltip frame. */
+ compute_tip_xy (tip_f, parms, dx, dy, width, height, &root_x, &root_y);
+
+ /* Show tooltip frame. */
+ block_input ();
+ android_move_resize_window (FRAME_ANDROID_WINDOW (tip_f),
+ root_x, root_y, width,
+ height);
+ android_map_raised (FRAME_ANDROID_WINDOW (tip_f));
+ unblock_input ();
+
+ /* Garbage the tip frame too. */
+ SET_FRAME_GARBAGED (tip_f);
+
+ w->must_be_updated_p = true;
+ update_single_window (w);
+ flush_frame (tip_f);
+ set_buffer_internal_1 (old_buffer);
+ unbind_to (count_1, Qnil);
+ windows_or_buffers_changed = old_windows_or_buffers_changed;
+
+ /* MapNotify events are not sent on Android, so make the frame
+ visible. */
+
+ SET_FRAME_VISIBLE (tip_f, true);
+
+ start_timer:
+ /* Let the tip disappear after timeout seconds. */
+ tip_timer = call3 (Qrun_at_time, timeout, Qnil,
+ Qx_hide_tip);
+
+ return unbind_to (count, Qnil);
+#endif
+}
+
+DEFUN ("x-hide-tip", Fx_hide_tip, Sx_hide_tip, 0, 0, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (void)
+{
+#ifdef ANDROID_STUBIFY
+ error ("Android cross-compilation stub called!");
+ return Qnil;
+#else
+ return android_hide_tip (true);
+#endif
+}
+
+DEFUN ("android-detect-mouse", Fandroid_detect_mouse,
+ Sandroid_detect_mouse, 0, 0, 0,
+ doc: /* Figure out whether or not there is a mouse.
+Return non-nil if a mouse is connected to this computer, and nil if
+there is no mouse. */)
+ (void)
+{
+#ifndef ANDROID_STUBIFY
+ /* If no display connection is present, just return nil. */
+
+ if (!android_init_gui)
+ return Qnil;
+
+ return android_detect_mouse () ? Qt : Qnil;
+#else
+ return Qnil;
+#endif
+}
+
+DEFUN ("android-toggle-on-screen-keyboard",
+ Fandroid_toggle_on_screen_keyboard,
+ Sandroid_toggle_on_screen_keyboard, 2, 2, 0,
+ doc: /* Display or hide the on-screen keyboard.
+If HIDE is non-nil, hide the on screen keyboard if it is currently
+being displayed. Else, request that the system display it on behalf
+of FRAME. This request may be rejected if FRAME does not have the
+input focus. */)
+ (Lisp_Object frame, Lisp_Object hide)
+{
+#ifndef ANDROID_STUBIFY
+ struct frame *f;
+
+ f = decode_window_system_frame (frame);
+
+ block_input ();
+ android_toggle_on_screen_keyboard (FRAME_ANDROID_WINDOW (f),
+ NILP (hide));
+ unblock_input ();
+#endif
+
+ return Qnil;
+}
+
+
+
+#ifndef ANDROID_STUBIFY
+
+static void
+android_set_background_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ struct android_output *x;
+ unsigned long bg;
+
+ x = f->output_data.android;
+ bg = android_decode_color (f, arg, WHITE_PIX_DEFAULT (f));
+ FRAME_BACKGROUND_PIXEL (f) = bg;
+
+ if (FRAME_ANDROID_WINDOW (f) != 0)
+ {
+ block_input ();
+ android_set_background (x->normal_gc, bg);
+ android_set_foreground (x->reverse_gc, bg);
+ android_set_window_background (FRAME_ANDROID_WINDOW (f), bg);
+ android_set_foreground (x->cursor_gc, bg);
+ unblock_input ();
+
+ update_face_from_frame_parameter (f, Qbackground_color, arg);
+
+ if (FRAME_VISIBLE_P (f))
+ redraw_frame (f);
+ }
+}
+
+static void
+android_set_border_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ /* Left unimplemented because Android has no window borders. */
+ CHECK_STRING (arg);
+ android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+ update_face_from_frame_parameter (f, Qborder_color, arg);
+}
+
+static void
+android_set_cursor_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ unsigned long fore_pixel, pixel;
+ struct android_output *x;
+
+ x = f->output_data.android;
+
+ if (!NILP (Vx_cursor_fore_pixel))
+ fore_pixel = android_decode_color (f, Vx_cursor_fore_pixel,
+ WHITE_PIX_DEFAULT (f));
+ else
+ fore_pixel = FRAME_BACKGROUND_PIXEL (f);
+
+ pixel = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+
+ /* Make sure that the cursor color differs from the background color. */
+ if (pixel == FRAME_BACKGROUND_PIXEL (f))
+ {
+ pixel = FRAME_FOREGROUND_PIXEL (f);
+ if (pixel == fore_pixel)
+ fore_pixel = FRAME_BACKGROUND_PIXEL (f);
+ }
+
+ x->cursor_foreground_pixel = fore_pixel;
+ x->cursor_pixel = pixel;
+
+ if (FRAME_ANDROID_WINDOW (f) != 0)
+ {
+ block_input ();
+ android_set_background (x->cursor_gc, x->cursor_pixel);
+ android_set_foreground (x->cursor_gc, fore_pixel);
+ unblock_input ();
+
+ if (FRAME_VISIBLE_P (f))
+ {
+ gui_update_cursor (f, false);
+ gui_update_cursor (f, true);
+ }
+ }
+
+ update_face_from_frame_parameter (f, Qcursor_color, arg);
+}
+
+static void
+android_set_cursor_type (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ set_frame_cursor_types (f, arg);
+}
+
+static void
+android_set_foreground_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ struct android_output *x;
+ unsigned long fg, old_fg;
+
+ x = f->output_data.android;
+
+ fg = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+ old_fg = FRAME_FOREGROUND_PIXEL (f);
+ FRAME_FOREGROUND_PIXEL (f) = fg;
+
+ if (FRAME_ANDROID_WINDOW (f) != 0)
+ {
+ block_input ();
+ android_set_foreground (x->normal_gc, fg);
+ android_set_background (x->reverse_gc, fg);
+
+ if (x->cursor_pixel == old_fg)
+ {
+ x->cursor_pixel = fg;
+ android_set_background (x->cursor_gc, x->cursor_pixel);
+ }
+
+ unblock_input ();
+
+ update_face_from_frame_parameter (f, Qforeground_color, arg);
+
+ if (FRAME_VISIBLE_P (f))
+ redraw_frame (f);
+ }
+}
+
+static void
+android_set_child_frame_border_width (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ int border;
+
+ if (NILP (arg))
+ border = -1;
+ else if (RANGED_FIXNUMP (0, arg, INT_MAX))
+ border = XFIXNAT (arg);
+ else
+ signal_error ("Invalid child frame border width", arg);
+
+ if (border != FRAME_CHILD_FRAME_BORDER_WIDTH (f))
+ {
+ f->child_frame_border_width = border;
+
+ if (FRAME_ANDROID_WINDOW (f))
+ {
+ adjust_frame_size (f, -1, -1, 3, false, Qchild_frame_border_width);
+ android_clear_under_internal_border (f);
+ }
+ }
+}
+
+static void
+android_set_internal_border_width (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ int border = check_int_nonnegative (arg);
+
+ if (border != FRAME_INTERNAL_BORDER_WIDTH (f))
+ {
+ f->internal_border_width = border;
+
+ if (FRAME_ANDROID_WINDOW (f))
+ {
+ adjust_frame_size (f, -1, -1, 3, false, Qinternal_border_width);
+ android_clear_under_internal_border (f);
+ }
+ }
+}
+
+static void
+android_set_menu_bar_lines (struct frame *f, Lisp_Object value,
+ Lisp_Object oldval)
+{
+ int nlines;
+ int olines = FRAME_MENU_BAR_LINES (f);
+
+ /* Right now, menu bars don't work properly in minibuf-only frames;
+ most of the commands try to apply themselves to the minibuffer
+ frame itself, and get an error because you can't switch buffers
+ in or split the minibuffer window. */
+ if (FRAME_MINIBUF_ONLY_P (f) || FRAME_PARENT_FRAME (f))
+ return;
+
+ if (TYPE_RANGED_FIXNUMP (int, value))
+ nlines = XFIXNUM (value);
+ else
+ nlines = 0;
+
+ /* Make sure we redisplay all windows in this frame. */
+ fset_redisplay (f);
+
+ FRAME_MENU_BAR_LINES (f) = nlines;
+ FRAME_MENU_BAR_HEIGHT (f) = nlines * FRAME_LINE_HEIGHT (f);
+ if (FRAME_ANDROID_WINDOW (f))
+ android_clear_under_internal_border (f);
+
+ /* If the menu bar height gets changed, the internal border below
+ the top margin has to be cleared. Also, if the menu bar gets
+ larger, the area for the added lines has to be cleared except for
+ the first menu bar line that is to be drawn later. */
+ if (nlines != olines)
+ {
+ int height = FRAME_INTERNAL_BORDER_WIDTH (f);
+ int width = FRAME_PIXEL_WIDTH (f);
+ int y;
+
+ adjust_frame_size (f, -1, -1, 3, true, Qmenu_bar_lines);
+
+ /* height can be zero here. */
+ if (FRAME_ANDROID_WINDOW (f) && height > 0 && width > 0)
+ {
+ y = FRAME_TOP_MARGIN_HEIGHT (f);
+
+ block_input ();
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f),
+ 0, y, width, height);
+ unblock_input ();
+ }
+
+ if (nlines > 1 && nlines > olines)
+ {
+ y = (olines == 0 ? 1 : olines) * FRAME_LINE_HEIGHT (f);
+ height = nlines * FRAME_LINE_HEIGHT (f) - y;
+
+ block_input ();
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, y,
+ width, height);
+ unblock_input ();
+ }
+
+ if (nlines == 0 && WINDOWP (f->menu_bar_window))
+ clear_glyph_matrix (XWINDOW (f->menu_bar_window)->current_matrix);
+ }
+
+ adjust_frame_glyphs (f);
+}
+
+
+
+/* These enums must stay in sync with the mouse_cursor_types array
+ below! */
+
+enum mouse_cursor
+ {
+ mouse_cursor_text,
+ mouse_cursor_nontext,
+ mouse_cursor_hourglass,
+ mouse_cursor_mode,
+ mouse_cursor_hand,
+ mouse_cursor_horizontal_drag,
+ mouse_cursor_vertical_drag,
+ mouse_cursor_left_edge,
+ mouse_cursor_top_left_corner,
+ mouse_cursor_top_edge,
+ mouse_cursor_top_right_corner,
+ mouse_cursor_right_edge,
+ mouse_cursor_bottom_right_corner,
+ mouse_cursor_bottom_edge,
+ mouse_cursor_bottom_left_corner,
+ mouse_cursor_max
+ };
+
+struct mouse_cursor_types
+{
+ /* Printable name for error messages (optional). */
+ const char *name;
+
+ /* Lisp variable controlling the cursor shape. */
+ /* FIXME: A couple of these variables are defined in the C code but
+ are not actually accessible from Lisp. They should probably be
+ made accessible or removed. */
+ Lisp_Object *shape_var_ptr;
+
+ /* The default shape. */
+ int default_shape;
+};
+
+/* This array must stay in sync with enum mouse_cursor above! */
+
+static const struct mouse_cursor_types mouse_cursor_types[] =
+ {
+ {"text", &Vx_pointer_shape, ANDROID_XC_XTERM, },
+ {"nontext", &Vx_nontext_pointer_shape, ANDROID_XC_LEFT_PTR, },
+ {"hourglass", &Vx_hourglass_pointer_shape, ANDROID_XC_WATCH, },
+ {"modeline", &Vx_mode_pointer_shape, ANDROID_XC_XTERM, },
+ {NULL, &Vx_sensitive_text_pointer_shape, ANDROID_XC_HAND2, },
+ {NULL, &Vx_window_horizontal_drag_shape, ANDROID_XC_SB_H_DOUBLE_ARROW, },
+ {NULL, &Vx_window_vertical_drag_shape, ANDROID_XC_SB_V_DOUBLE_ARROW, },
+ {NULL, &Vx_window_left_edge_shape, ANDROID_XC_LEFT_SIDE, },
+ {NULL, &Vx_window_top_left_corner_shape, ANDROID_XC_TOP_LEFT_CORNER, },
+ {NULL, &Vx_window_top_edge_shape, ANDROID_XC_TOP_SIDE, },
+ {NULL, &Vx_window_top_right_corner_shape, ANDROID_XC_TOP_RIGHT_CORNER, },
+ {NULL, &Vx_window_right_edge_shape, ANDROID_XC_RIGHT_SIDE, },
+ {NULL, &Vx_window_bottom_right_corner_shape,
+ ANDROID_XC_BOTTOM_RIGHT_CORNER, },
+ {NULL, &Vx_window_bottom_edge_shape, ANDROID_XC_BOTTOM_SIDE, },
+ {NULL, &Vx_window_bottom_left_corner_shape,
+ ANDROID_XC_BOTTOM_LEFT_CORNER, },
+ };
+
+struct mouse_cursor_data
+{
+ /* Cursor numbers chosen. */
+ unsigned int cursor_num[mouse_cursor_max];
+
+ /* Allocated Cursor values, or zero for failed attempts. */
+ android_cursor cursor[mouse_cursor_max];
+};
+
+
+
+static void
+android_set_mouse_color (struct frame *f, Lisp_Object arg,
+ Lisp_Object oldval)
+{
+ struct android_output *x = f->output_data.android;
+ struct mouse_cursor_data cursor_data = { -1, -1 };
+ unsigned long pixel = android_decode_color (f, arg, BLACK_PIX_DEFAULT (f));
+ unsigned long mask_color = FRAME_BACKGROUND_PIXEL (f);
+ int i;
+
+ /* Don't let pointers be invisible. */
+ if (mask_color == pixel)
+ pixel = FRAME_FOREGROUND_PIXEL (f);
+
+ x->mouse_pixel = pixel;
+
+ for (i = 0; i < mouse_cursor_max; i++)
+ {
+ Lisp_Object shape_var = *mouse_cursor_types[i].shape_var_ptr;
+ cursor_data.cursor_num[i]
+ = (!NILP (shape_var)
+ ? check_uinteger_max (shape_var, UINT_MAX)
+ : mouse_cursor_types[i].default_shape);
+ }
+
+ block_input ();
+
+ for (i = 0; i < mouse_cursor_max; i++)
+ cursor_data.cursor[i]
+ = android_create_font_cursor (cursor_data.cursor_num[i]);
+
+ if (FRAME_ANDROID_WINDOW (f))
+ {
+ f->output_data.android->current_cursor
+ = cursor_data.cursor[mouse_cursor_text];
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ f->output_data.android->current_cursor);
+ }
+
+#define INSTALL_CURSOR(FIELD, SHORT_INDEX) \
+ eassert (x->FIELD \
+ != cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX]); \
+ if (x->FIELD != 0) \
+ android_free_cursor (x->FIELD); \
+ x->FIELD = cursor_data.cursor[mouse_cursor_ ## SHORT_INDEX];
+
+ INSTALL_CURSOR (text_cursor, text);
+ INSTALL_CURSOR (nontext_cursor, nontext);
+ INSTALL_CURSOR (hourglass_cursor, hourglass);
+ INSTALL_CURSOR (modeline_cursor, mode);
+ INSTALL_CURSOR (hand_cursor, hand);
+ INSTALL_CURSOR (horizontal_drag_cursor, horizontal_drag);
+ INSTALL_CURSOR (vertical_drag_cursor, vertical_drag);
+ INSTALL_CURSOR (left_edge_cursor, left_edge);
+ INSTALL_CURSOR (top_left_corner_cursor, top_left_corner);
+ INSTALL_CURSOR (top_edge_cursor, top_edge);
+ INSTALL_CURSOR (top_right_corner_cursor, top_right_corner);
+ INSTALL_CURSOR (right_edge_cursor, right_edge);
+ INSTALL_CURSOR (bottom_right_corner_cursor, bottom_right_corner);
+ INSTALL_CURSOR (bottom_edge_cursor, bottom_edge);
+ INSTALL_CURSOR (bottom_left_corner_cursor, bottom_left_corner);
+
+#undef INSTALL_CURSOR
+
+ unblock_input ();
+
+ update_face_from_frame_parameter (f, Qmouse_color, arg);
+}
+
+static void
+android_set_title (struct frame *f, Lisp_Object name,
+ Lisp_Object old_name)
+{
+ /* Don't change the title if it's already NAME. */
+ if (EQ (name, f->title))
+ return;
+
+ update_mode_lines = 38;
+
+ fset_title (f, name);
+
+ if (NILP (name))
+ name = f->name;
+ else
+ CHECK_STRING (name);
+}
+
+static void
+android_set_alpha (struct frame *f, Lisp_Object arg, Lisp_Object oldval)
+{
+ double alpha = 1.0;
+ double newval[2];
+ int i;
+ Lisp_Object item;
+
+ /* N.B. that setting the window alpha is actually unsupported under
+ Android. */
+
+ for (i = 0; i < 2; i++)
+ {
+ newval[i] = 1.0;
+ if (CONSP (arg))
+ {
+ item = CAR (arg);
+ arg = CDR (arg);
+ }
+ else
+ item = arg;
+
+ if (NILP (item))
+ alpha = - 1.0;
+ else if (FLOATP (item))
+ {
+ alpha = XFLOAT_DATA (item);
+ if (! (0 <= alpha && alpha <= 1.0))
+ args_out_of_range (make_float (0.0), make_float (1.0));
+ }
+ else if (FIXNUMP (item))
+ {
+ EMACS_INT ialpha = XFIXNUM (item);
+ if (! (0 <= ialpha && ialpha <= 100))
+ args_out_of_range (make_fixnum (0), make_fixnum (100));
+ alpha = ialpha / 100.0;
+ }
+ else
+ wrong_type_argument (Qnumberp, item);
+ newval[i] = alpha;
+ }
+
+ for (i = 0; i < 2; i++)
+ f->alpha[i] = newval[i];
+
+ if (FRAME_TERMINAL (f)->set_frame_alpha_hook)
+ {
+ block_input ();
+ FRAME_TERMINAL (f)->set_frame_alpha_hook (f);
+ unblock_input ();
+ }
+}
+
+static void
+android_set_no_focus_on_map (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value)
+{
+ if (!EQ (new_value, old_value))
+ {
+ android_set_dont_focus_on_map (FRAME_ANDROID_WINDOW (f),
+ new_value);
+ FRAME_NO_FOCUS_ON_MAP (f) = !NILP (new_value);
+ }
+}
+
+static void
+android_set_no_accept_focus (struct frame *f, Lisp_Object new_value,
+ Lisp_Object old_value)
+{
+ if (!EQ (new_value, old_value))
+ {
+ android_set_dont_accept_focus (FRAME_ANDROID_WINDOW (f),
+ new_value);
+ FRAME_NO_ACCEPT_FOCUS (f) = !NILP (new_value);
+ }
+}
+
+frame_parm_handler android_frame_parm_handlers[] =
+{
+ gui_set_autoraise,
+ gui_set_autolower,
+ android_set_background_color,
+ android_set_border_color,
+ gui_set_border_width,
+ android_set_cursor_color,
+ android_set_cursor_type,
+ gui_set_font,
+ android_set_foreground_color,
+ NULL,
+ NULL,
+ android_set_child_frame_border_width,
+ android_set_internal_border_width,
+ gui_set_right_divider_width,
+ gui_set_bottom_divider_width,
+ android_set_menu_bar_lines,
+ android_set_mouse_color,
+ android_explicitly_set_name,
+ gui_set_scroll_bar_width,
+ gui_set_scroll_bar_height,
+ android_set_title,
+ gui_set_unsplittable,
+ gui_set_vertical_scroll_bars,
+ gui_set_horizontal_scroll_bars,
+ gui_set_visibility,
+ android_set_tab_bar_lines,
+ android_set_tool_bar_lines,
+ NULL,
+ NULL,
+ gui_set_screen_gamma,
+ gui_set_line_spacing,
+ gui_set_left_fringe,
+ gui_set_right_fringe,
+ NULL,
+ gui_set_fullscreen,
+ gui_set_font_backend,
+ android_set_alpha,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ android_set_parent_frame,
+ NULL,
+ android_set_no_focus_on_map,
+ android_set_no_accept_focus,
+ NULL,
+ NULL,
+ gui_set_no_special_glyphs,
+ NULL,
+ NULL,
+};
+
+
+
+/* Battery information support. */
+
+DEFUN ("android-query-battery", Fandroid_query_battery,
+ Sandroid_query_battery, 0, 0, 0,
+ doc: /* Perform a query for battery information.
+Value is nil upon failure, or a list of the form:
+
+ (CAPACITY CHARGE-COUNTER CURRENT-AVERAGE CURRENT-NOW STATUS
+ REMAINING PLUGGED TEMP)
+
+where REMAINING, CURRENT-AVERAGE, and CURRENT-NOW are undefined prior
+to Android 5.0.
+
+See the documentation at
+
+ https://developer.android.com/reference/android/os/BatteryManager
+
+for more details about these values. */)
+ (void)
+{
+ struct android_battery_state state;
+
+ /* Make sure the Android libraries have been initialized. */
+
+ if (!android_init_gui)
+ return Qnil;
+
+ /* Perform the query. */
+
+ if (android_query_battery (&state))
+ return Qnil;
+
+ return listn (8, make_int (state.capacity),
+ make_fixnum (state.charge_counter),
+ make_int (state.current_average),
+ make_int (state.current_now),
+ make_fixnum (state.status),
+ make_int (state.remaining),
+ make_fixnum (state.plugged),
+ make_fixnum (state.temperature));
+}
+
+
+
+/* Miscellaneous input method related stuff. */
+
+/* Report X, Y, by the phys cursor width and height as the cursor
+ anchor rectangle for W's frame. */
+
+void
+android_set_preeditarea (struct window *w, int x, int y)
+{
+ struct frame *f;
+
+ f = WINDOW_XFRAME (w);
+
+ /* Convert the window coordinates to the frame's coordinate
+ space. */
+ x = (WINDOW_TO_FRAME_PIXEL_X (w, x)
+ + WINDOW_LEFT_FRINGE_WIDTH (w)
+ + WINDOW_LEFT_MARGIN_WIDTH (w));
+ y = WINDOW_TO_FRAME_PIXEL_Y (w, y);
+
+ /* Note that calculating the baseline is too hard, so the bottom of
+ the cursor is used instead. */
+ android_update_cursor_anchor_info (FRAME_ANDROID_WINDOW (f), x,
+ y, y + w->phys_cursor_height,
+ y + w->phys_cursor_height);
+}
+
+#endif
+
+
+
+void
+syms_of_androidfns (void)
+{
+ /* Miscellaneous symbols used by some functions here. */
+ DEFSYM (Qtrue_color, "true-color");
+ DEFSYM (Qalways, "always");
+
+ DEFVAR_LISP ("x-pointer-shape", Vx_pointer_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_pointer_shape = Qnil;
+
+#if false /* This doesn't really do anything. */
+ DEFVAR_LISP ("x-nontext-pointer-shape", Vx_nontext_pointer_shape,
+ doc: /* SKIP: real doc in xfns.c. */);
+#endif
+ Vx_nontext_pointer_shape = Qnil;
+
+ DEFVAR_LISP ("x-hourglass-pointer-shape", Vx_hourglass_pointer_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_hourglass_pointer_shape = Qnil;
+
+ DEFVAR_LISP ("x-sensitive-text-pointer-shape",
+ Vx_sensitive_text_pointer_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_sensitive_text_pointer_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-horizontal-drag-cursor",
+ Vx_window_horizontal_drag_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_horizontal_drag_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-vertical-drag-cursor",
+ Vx_window_vertical_drag_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_vertical_drag_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-left-edge-cursor",
+ Vx_window_left_edge_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_left_edge_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-top-left-corner-cursor",
+ Vx_window_top_left_corner_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_top_left_corner_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-top-edge-cursor",
+ Vx_window_top_edge_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_top_edge_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-top-right-corner-cursor",
+ Vx_window_top_right_corner_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_top_right_corner_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-right-edge-cursor",
+ Vx_window_right_edge_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_right_edge_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-bottom-right-corner-cursor",
+ Vx_window_bottom_right_corner_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_bottom_right_corner_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-bottom-edge-cursor",
+ Vx_window_bottom_edge_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_bottom_edge_shape = Qnil;
+
+#if false /* This doesn't really do anything. */
+ DEFVAR_LISP ("x-mode-pointer-shape", Vx_mode_pointer_shape,
+ doc: /* SKIP: real doc in xfns.c. */);
+#endif
+ Vx_mode_pointer_shape = Qnil;
+
+ DEFVAR_LISP ("x-window-bottom-left-corner-cursor",
+ Vx_window_bottom_left_corner_shape,
+ doc: /* SKIP: real text in xfns.c. */);
+ Vx_window_bottom_left_corner_shape = Qnil;
+
+ DEFVAR_LISP ("x-cursor-fore-pixel", Vx_cursor_fore_pixel,
+ doc: /* SKIP: real doc in xfns.c. */);
+ Vx_cursor_fore_pixel = Qnil;
+
+ /* Used by Fx_show_tip. */
+ DEFSYM (Qrun_at_time, "run-at-time");
+ DEFSYM (Qx_hide_tip, "x-hide-tip");
+ DEFSYM (Qcancel_timer, "cancel-timer");
+ DEFSYM (Qassq_delete_all, "assq-delete-all");
+ DEFSYM (Qcolor, "color");
+
+ DEFVAR_LISP ("x-max-tooltip-size", Vx_max_tooltip_size,
+ doc: /* SKIP: real doc in xfns.c. */);
+ Vx_max_tooltip_size = Qnil;
+
+ DEFVAR_BOOL ("android-pass-multimedia-buttons-to-system",
+ android_pass_multimedia_buttons_to_system,
+ doc: /* Whether or not to pass volume control buttons to the system.
+Generally, the `volume-up', `volume-down' and `volume-mute' keys are
+processed by Emacs, but setting this to non-nil they are passed to the
+operating system instead of being intercepted by Emacs.
+
+Note that if you set this, you will no longer be able to quit Emacs
+using the volume down button. */);
+ android_pass_multimedia_buttons_to_system = false;
+
+ DEFVAR_BOOL ("android-use-exec-loader", android_use_exec_loader,
+ doc: /* Whether or not to bypass system restrictions on program execution.
+
+Android 10 and later prevent programs from executing files installed
+in writable directories, such as the application data directory.
+
+When non-nil, Emacs will bypass this restriction by running such
+executables under system call tracing, and replacing the `execve'
+system call with a version which ignores the system's security
+restrictions.
+
+This option has no effect on Android 9 and earlier. */);
+ android_use_exec_loader = true;
+
+ /* Functions defined. */
+ defsubr (&Sx_create_frame);
+ defsubr (&Sxw_color_defined_p);
+ defsubr (&Sxw_color_values);
+ defsubr (&Sxw_display_color_p);
+ defsubr (&Sx_display_grayscale_p);
+ defsubr (&Sx_display_pixel_width);
+ defsubr (&Sx_display_pixel_height);
+ defsubr (&Sx_display_planes);
+ defsubr (&Sx_display_color_cells);
+ defsubr (&Sx_display_screens);
+ defsubr (&Sx_display_mm_width);
+ defsubr (&Sx_display_mm_height);
+ defsubr (&Sx_display_backing_store);
+ defsubr (&Sx_display_visual_class);
+ defsubr (&Sandroid_display_monitor_attributes_list);
+ defsubr (&Sandroid_frame_geometry);
+ defsubr (&Sandroid_frame_edges);
+ defsubr (&Sandroid_frame_list_z_order);
+ defsubr (&Sandroid_frame_restack);
+ defsubr (&Sandroid_mouse_absolute_pixel_position);
+ defsubr (&Sandroid_set_mouse_absolute_pixel_position);
+ defsubr (&Sandroid_get_connection);
+ defsubr (&Sx_display_list);
+ defsubr (&Sx_show_tip);
+ defsubr (&Sx_hide_tip);
+ defsubr (&Sandroid_detect_mouse);
+ defsubr (&Sandroid_toggle_on_screen_keyboard);
+ defsubr (&Sx_server_vendor);
+ defsubr (&Sx_server_version);
+#ifndef ANDROID_STUBIFY
+ defsubr (&Sandroid_query_battery);
+
+ tip_timer = Qnil;
+ staticpro (&tip_timer);
+ tip_frame = Qnil;
+ staticpro (&tip_frame);
+ tip_last_frame = Qnil;
+ staticpro (&tip_last_frame);
+ tip_last_string = Qnil;
+ staticpro (&tip_last_string);
+ tip_last_parms = Qnil;
+ staticpro (&tip_last_parms);
+ tip_dx = Qnil;
+ staticpro (&tip_dx);
+ tip_dy = Qnil;
+ staticpro (&tip_dy);
+#endif
+}
diff --git a/src/androidfont.c b/src/androidfont.c
new file mode 100644
index 00000000000..1a09027bca7
--- /dev/null
+++ b/src/androidfont.c
@@ -0,0 +1,1108 @@
+/* Android fallback font driver.
+
+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/>. */
+
+/* Due to the terrible nature of the Android Typeface subsystems, this
+ font driver is only used as a fallback when sfntfont-android.c
+ fails to enumerate any fonts at all. */
+
+#include <config.h>
+
+#include "lisp.h"
+#include "dispextern.h"
+#include "composite.h"
+#include "blockinput.h"
+#include "charset.h"
+#include "frame.h"
+#include "window.h"
+#include "fontset.h"
+#include "androidterm.h"
+#include "character.h"
+#include "coding.h"
+#include "font.h"
+#include "termchar.h"
+#include "pdumper.h"
+#include "android.h"
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+struct android_emacs_font_driver
+{
+ jclass class;
+ jmethodID list;
+ jmethodID match;
+ jmethodID list_families;
+ jmethodID open_font;
+ jmethodID has_char;
+ jmethodID text_extents;
+ jmethodID encode_char;
+ jmethodID draw;
+
+ /* Static methods. */
+ jmethodID create_font_driver;
+};
+
+struct android_emacs_font_spec
+{
+ jclass class;
+ jfieldID foundry;
+ jfieldID family;
+ jfieldID adstyle;
+ jfieldID registry;
+ jfieldID width;
+ jfieldID weight;
+ jfieldID slant;
+ jfieldID size;
+ jfieldID spacing;
+ jfieldID avgwidth;
+ jfieldID dpi;
+};
+
+struct android_emacs_font_metrics
+{
+ jclass class;
+ jfieldID lbearing;
+ jfieldID rbearing;
+ jfieldID width;
+ jfieldID ascent;
+ jfieldID descent;
+};
+
+struct android_emacs_font_object
+{
+ jclass class;
+ jfieldID min_width;
+ jfieldID max_width;
+ jfieldID pixel_size;
+ jfieldID height;
+ jfieldID space_width;
+ jfieldID average_width;
+ jfieldID ascent;
+ jfieldID descent;
+ jfieldID underline_thickness;
+ jfieldID underline_position;
+ jfieldID baseline_offset;
+ jfieldID relative_compose;
+ jfieldID default_ascent;
+ jfieldID encoding_charset;
+ jfieldID repertory_charset;
+};
+
+struct android_integer
+{
+ jclass class;
+ jmethodID constructor;
+ jmethodID int_value;
+};
+
+struct androidfont_info
+{
+ /* The font pseudo-vector object. */
+ struct font font;
+
+ /* The Java-side font. */
+ jobject object;
+
+ /* Cached glyph metrics arranged in a two dimensional array. */
+ struct font_metrics **metrics;
+};
+
+struct androidfont_entity
+{
+ /* The font entity pvec. */
+ struct font_entity font;
+
+ /* The Java-side font entity. */
+ jobject object;
+};
+
+/* Method and class identifiers associated with the EmacsFontDriver
+ class. */
+
+struct android_emacs_font_driver font_driver_class;
+
+/* Field and class identifiers associated with the
+ EmacsFontDriver$FontSpec class. */
+
+struct android_emacs_font_spec font_spec_class;
+
+/* Method and class identifiers associated with the Integer class. */
+
+struct android_integer integer_class;
+
+/* Field and class identifiers associated with the
+ EmacsFontDriver$FontMetrics class. */
+
+struct android_emacs_font_metrics font_metrics_class;
+
+/* Field and class identifiers associated with the
+ EmacsFontDriver$FontObject class. */
+
+struct android_emacs_font_object font_object_class;
+
+/* The font cache. */
+
+static Lisp_Object font_cache;
+
+/* The Java-side font driver. */
+
+static jobject font_driver;
+
+
+
+/* Initialize the class and method identifiers for functions in the
+ EmacsFontDriver class, and place them in `font_driver_class'. */
+
+static void
+android_init_font_driver (void)
+{
+ jclass old;
+
+ font_driver_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsFontDriver");
+ eassert (font_driver_class.class);
+
+ old = font_driver_class.class;
+ font_driver_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_driver_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ font_driver_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ font_driver_class.class, \
+ name, signature); \
+ eassert (font_driver_class.c_name);
+
+ FIND_METHOD (list, "list", "(Lorg/gnu/emacs/EmacsFontDriver$FontSpec;)"
+ "[Lorg/gnu/emacs/EmacsFontDriver$FontEntity;");
+ FIND_METHOD (match, "match", "(Lorg/gnu/emacs/EmacsFontDriver$FontSpec;)"
+ "Lorg/gnu/emacs/EmacsFontDriver$FontEntity;");
+ FIND_METHOD (list_families, "listFamilies", "()[Ljava/lang/String;");
+ FIND_METHOD (open_font, "openFont", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
+ "Entity;I)Lorg/gnu/emacs/EmacsFontDriver$FontObject;");
+ FIND_METHOD (has_char, "hasChar", "(Lorg/gnu/emacs/EmacsFontDriver$Font"
+ "Spec;C)I");
+ FIND_METHOD (text_extents, "textExtents", "(Lorg/gnu/emacs/EmacsFontDriver"
+ "$FontObject;[ILorg/gnu/emacs/EmacsFontDriver$FontMetrics;)V");
+ FIND_METHOD (encode_char, "encodeChar", "(Lorg/gnu/emacs/EmacsFontDriver"
+ "$FontObject;C)I");
+ FIND_METHOD (draw, "draw", "(Lorg/gnu/emacs/EmacsFontDriver$FontObject;"
+ "Lorg/gnu/emacs/EmacsGC;Lorg/gnu/emacs/EmacsDrawable;[IIIIZ)I");
+
+ font_driver_class.create_font_driver
+ = (*android_java_env)->GetStaticMethodID (android_java_env,
+ font_driver_class.class,
+ "createFontDriver",
+ "()Lorg/gnu/emacs/"
+ "EmacsFontDriver;");
+ eassert (font_driver_class.create_font_driver);
+#undef FIND_METHOD
+}
+
+/* Initialize the class and field identifiers for functions in the
+ EmacsFontDriver$FontSpec class, and place them in
+ `font_spec_class'. */
+
+static void
+android_init_font_spec (void)
+{
+ jclass old;
+
+ font_spec_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsFontDriver"
+ "$FontSpec");
+ eassert (font_spec_class.class);
+
+ old = font_spec_class.class;
+ font_spec_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_spec_class.class)
+ emacs_abort ();
+
+#define FIND_FIELD(c_name, name, signature) \
+ font_spec_class.c_name \
+ = (*android_java_env)->GetFieldID (android_java_env, \
+ font_spec_class.class, \
+ name, signature); \
+ eassert (font_spec_class.c_name);
+
+ FIND_FIELD (foundry, "foundry", "Ljava/lang/String;");
+ FIND_FIELD (family, "family", "Ljava/lang/String;");
+ FIND_FIELD (adstyle, "adstyle", "Ljava/lang/String;");
+ FIND_FIELD (registry, "registry", "Ljava/lang/String;");
+ FIND_FIELD (width, "width", "Ljava/lang/Integer;");
+ FIND_FIELD (weight, "weight", "Ljava/lang/Integer;");
+ FIND_FIELD (slant, "slant", "Ljava/lang/Integer;");
+ FIND_FIELD (size, "size", "Ljava/lang/Integer;");
+ FIND_FIELD (spacing, "spacing", "Ljava/lang/Integer;");
+ FIND_FIELD (avgwidth, "avgwidth", "Ljava/lang/Integer;");
+ FIND_FIELD (dpi, "dpi", "Ljava/lang/Integer;");
+#undef FIND_FIELD
+}
+
+static void
+android_init_font_metrics (void)
+{
+ jclass old;
+
+ font_metrics_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsFontDriver"
+ "$FontMetrics");
+ eassert (font_metrics_class.class);
+
+ old = font_metrics_class.class;
+ font_metrics_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_metrics_class.class)
+ emacs_abort ();
+
+#define FIND_FIELD(c_name, name, signature) \
+ font_metrics_class.c_name \
+ = (*android_java_env)->GetFieldID (android_java_env, \
+ font_metrics_class.class, \
+ name, signature); \
+ eassert (font_metrics_class.c_name);
+
+ FIND_FIELD (lbearing, "lbearing", "S");
+ FIND_FIELD (rbearing, "rbearing", "S");
+ FIND_FIELD (width, "width", "S");
+ FIND_FIELD (ascent, "ascent", "S");
+ FIND_FIELD (descent, "descent", "S");
+#undef FIND_FIELD
+}
+
+static void
+android_init_integer (void)
+{
+ jclass old;
+
+ integer_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "java/lang/Integer");
+ eassert (integer_class.class);
+
+ old = integer_class.class;
+ integer_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!integer_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ integer_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ integer_class.class, \
+ name, signature); \
+ eassert (integer_class.c_name);
+
+ FIND_METHOD (constructor, "<init>", "(I)V");
+ FIND_METHOD (int_value, "intValue", "()I");
+#undef FIND_METHOD
+}
+
+static void
+android_init_font_object (void)
+{
+ jclass old;
+
+ font_object_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsFontDriver"
+ "$FontObject");
+ eassert (font_object_class.class);
+
+ old = font_object_class.class;
+ font_object_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_object_class.class)
+ emacs_abort ();
+
+#define FIND_FIELD(c_name, name, signature) \
+ font_object_class.c_name \
+ = (*android_java_env)->GetFieldID (android_java_env, \
+ font_object_class.class, \
+ name, signature); \
+ eassert (font_object_class.c_name);
+
+ FIND_FIELD (min_width, "minWidth", "I");
+ FIND_FIELD (max_width, "maxWidth", "I");
+ FIND_FIELD (pixel_size, "pixelSize", "I");
+ FIND_FIELD (height, "height", "I");
+ FIND_FIELD (space_width, "spaceWidth", "I");
+ FIND_FIELD (average_width, "averageWidth", "I");
+ FIND_FIELD (ascent, "ascent", "I");
+ FIND_FIELD (descent, "descent", "I");
+ FIND_FIELD (underline_thickness, "underlineThickness", "I");
+ FIND_FIELD (underline_position, "underlinePosition", "I");
+ FIND_FIELD (baseline_offset, "baselineOffset", "I");
+ FIND_FIELD (relative_compose, "relativeCompose", "I");
+ FIND_FIELD (default_ascent, "defaultAscent", "I");
+ FIND_FIELD (encoding_charset, "encodingCharset", "I");
+ FIND_FIELD (repertory_charset, "repertoryCharset", "I");
+#undef FIND_FIELD
+}
+
+static Lisp_Object
+androidfont_get_cache (struct frame *frame)
+{
+ return font_cache;
+}
+
+/* Initialize the Java side of the font driver if it has not already
+ been initialized. This is only done whenever necessary because the
+ font driver otherwise uses a lot of memory, as it has to keep every
+ typeface open. */
+
+static void
+androidfont_check_init (void)
+{
+ jmethodID method;
+ jobject old;
+
+ if (font_driver)
+ return;
+
+ /* Log a loud message. This font driver really should not be
+ used. */
+ __android_log_print (ANDROID_LOG_WARN, __func__,
+ "The Android font driver is being used."
+ " Please investigate why this is so.");
+
+ method = font_driver_class.create_font_driver;
+
+ /* Initialize the font driver on the Java side. */
+ font_driver
+ = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ font_driver_class.class,
+ method);
+ android_exception_check ();
+
+ old = font_driver;
+ font_driver
+ = (*android_java_env)->NewGlobalRef (android_java_env, font_driver);
+ ANDROID_DELETE_LOCAL_REF (old);
+}
+
+/* Return a local reference to an instance of EmacsFontDriver$FontSpec
+ with the same values as FONT. */
+
+static jobject
+androidfont_from_lisp (Lisp_Object font)
+{
+ jobject spec, integer;
+ jstring string;
+ Lisp_Object tem;
+
+ spec = (*android_java_env)->AllocObject (android_java_env,
+ font_spec_class.class);
+ android_exception_check ();
+
+#define DO_SYMBOL_FIELD(field, index) \
+ tem = AREF (font, index); \
+ if (SYMBOLP (tem)) \
+ { \
+ /* Java seems to DTRT with the Emacs string encoding, so this does \
+ not matter at all. */ \
+ string = (*android_java_env)->NewStringUTF (android_java_env, \
+ SSDATA (SYMBOL_NAME (tem))); \
+ android_exception_check_1 (spec); \
+ \
+ (*android_java_env)->SetObjectField (android_java_env, spec, \
+ font_spec_class.field, \
+ string); \
+ ANDROID_DELETE_LOCAL_REF (string); \
+ } \
+
+ DO_SYMBOL_FIELD (foundry, FONT_FOUNDRY_INDEX);
+ DO_SYMBOL_FIELD (family, FONT_FAMILY_INDEX);
+ DO_SYMBOL_FIELD (adstyle, FONT_ADSTYLE_INDEX);
+ DO_SYMBOL_FIELD (registry, FONT_REGISTRY_INDEX);
+
+#undef DO_SYMBOL_FIELD
+
+#define DO_CARDINAL_FIELD(field, value) \
+ if (value != -1) \
+ { \
+ integer = (*android_java_env)->NewObject (android_java_env, \
+ integer_class.class, \
+ integer_class.constructor, \
+ (jint) value); \
+ android_exception_check_1 (spec); \
+ \
+ (*android_java_env)->SetObjectField (android_java_env, spec, \
+ font_spec_class.field, \
+ integer); \
+ ANDROID_DELETE_LOCAL_REF (integer); \
+ }
+
+ DO_CARDINAL_FIELD (width, FONT_WIDTH_NUMERIC (font));
+ DO_CARDINAL_FIELD (weight, FONT_WEIGHT_NUMERIC (font));
+ DO_CARDINAL_FIELD (slant, FONT_SLANT_NUMERIC (font));
+ DO_CARDINAL_FIELD (size, (FIXNUMP (AREF (font, FONT_SIZE_INDEX))
+ ? XFIXNUM (AREF (font, FONT_SIZE_INDEX))
+ : -1));
+ DO_CARDINAL_FIELD (spacing, (FIXNUMP (AREF (font, FONT_SPACING_INDEX))
+ ? XFIXNUM (AREF (font, FONT_SPACING_INDEX))
+ : -1));
+ DO_CARDINAL_FIELD (avgwidth, (FIXNUMP (AREF (font, FONT_AVGWIDTH_INDEX))
+ ? XFIXNUM (AREF (font, FONT_AVGWIDTH_INDEX))
+ : -1));
+ DO_CARDINAL_FIELD (dpi, (FIXNUMP (AREF (font, FONT_DPI_INDEX))
+ ? XFIXNUM (AREF (font, FONT_DPI_INDEX))
+ : -1));
+
+#undef DO_CARDINAL_FIELD
+
+ return spec;
+}
+
+static void
+androidfont_from_java (jobject spec, Lisp_Object entity)
+{
+ jobject tem;
+ jint value;
+ const char *string;
+
+#define DO_SYMBOL_FIELD(field, index) \
+ tem = (*android_java_env)->GetObjectField (android_java_env, \
+ spec, \
+ font_spec_class.field); \
+ if (tem) \
+ { \
+ string = (*android_java_env)->GetStringUTFChars (android_java_env, \
+ tem, NULL); \
+ if (!string) \
+ memory_full (0); \
+ ASET (entity, index, intern (string)); \
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env, \
+ tem, string); \
+ ANDROID_DELETE_LOCAL_REF (tem); \
+ }
+
+ DO_SYMBOL_FIELD (foundry, FONT_FOUNDRY_INDEX);
+ DO_SYMBOL_FIELD (family, FONT_FAMILY_INDEX);
+ DO_SYMBOL_FIELD (adstyle, FONT_ADSTYLE_INDEX);
+ DO_SYMBOL_FIELD (registry, FONT_REGISTRY_INDEX);
+
+#undef DO_SYMBOL_FIELD
+#define DO_CARDINAL_FIELD(field, index, is_style) \
+ tem = (*android_java_env)->GetObjectField (android_java_env, \
+ spec, \
+ font_spec_class.field); \
+ if (tem) \
+ { \
+ value \
+ = (*android_java_env)->CallIntMethod (android_java_env, \
+ tem, \
+ integer_class.int_value); \
+ if (!is_style) \
+ ASET (entity, index, make_fixnum (value)); \
+ else \
+ FONT_SET_STYLE (entity, index, make_fixnum (value)); \
+ ANDROID_DELETE_LOCAL_REF (tem); \
+ }
+
+ DO_CARDINAL_FIELD (width, FONT_WIDTH_INDEX, true);
+ DO_CARDINAL_FIELD (weight, FONT_WEIGHT_INDEX, true);
+ DO_CARDINAL_FIELD (slant, FONT_SLANT_INDEX, true);
+ DO_CARDINAL_FIELD (size, FONT_SIZE_INDEX, false);
+ DO_CARDINAL_FIELD (spacing, FONT_SPACING_INDEX, false);
+ DO_CARDINAL_FIELD (avgwidth, FONT_AVGWIDTH_INDEX, false);
+ DO_CARDINAL_FIELD (dpi, FONT_DPI_INDEX, false);
+
+#undef DO_CARDINAL_FIELD
+}
+
+/* Transfer the values from FONT, which must be some kind of font
+ entity, */
+
+static Lisp_Object
+androidfont_list (struct frame *f, Lisp_Object font_spec)
+{
+ jobject spec, array, tem;
+ jarray entities;
+ jsize i, size;
+ Lisp_Object value, entity;
+ struct androidfont_entity *info;
+
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
+ spec = androidfont_from_lisp (font_spec);
+ array = (*android_java_env)->CallObjectMethod (android_java_env,
+ font_driver,
+ font_driver_class.list,
+ spec);
+ android_exception_check_1 (spec);
+ ANDROID_DELETE_LOCAL_REF (spec);
+
+ entities = (jarray) array;
+ size = (*android_java_env)->GetArrayLength (android_java_env,
+ entities);
+ value = Qnil;
+
+ for (i = 0; i < size; ++i)
+ {
+ entity = font_make_entity_android (VECSIZE (struct androidfont_entity));
+ info = (struct androidfont_entity *) XFONT_ENTITY (entity);
+
+ /* The type must be set correctly, or font_open_entity won't be
+ able to find the right font driver. */
+ ASET (entity, FONT_TYPE_INDEX, Qandroid);
+
+ /* Clear this now in case GC happens without it set, which can
+ happen if androidfont_from_java runs out of memory. */
+ info->object = NULL;
+
+ tem = (*android_java_env)->GetObjectArrayElement (android_java_env,
+ entities, i);
+ androidfont_from_java (tem, entity);
+
+ /* Now, make a global reference to the Java font entity. */
+ info->object = (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) tem);
+ android_exception_check_2 (tem, entities);
+ ANDROID_DELETE_LOCAL_REF (tem);
+
+ value = Fcons (entity, value);
+ }
+
+ ANDROID_DELETE_LOCAL_REF (entities);
+ return Fnreverse (value);
+}
+
+static Lisp_Object
+androidfont_match (struct frame *f, Lisp_Object font_spec)
+{
+ jobject spec, result;
+ Lisp_Object entity;
+ struct androidfont_entity *info;
+
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
+ spec = androidfont_from_lisp (font_spec);
+ result = (*android_java_env)->CallObjectMethod (android_java_env,
+ font_driver,
+ font_driver_class.match,
+ spec);
+ android_exception_check_1 (spec);
+ ANDROID_DELETE_LOCAL_REF (spec);
+
+ entity = font_make_entity_android (VECSIZE (struct androidfont_entity));
+ info = (struct androidfont_entity *) XFONT_ENTITY (entity);
+
+ /* The type must be set correctly, or font_open_entity won't be able
+ to find the right font driver. */
+ ASET (entity, FONT_TYPE_INDEX, Qandroid);
+
+ info->object = NULL;
+ androidfont_from_java (result, entity);
+ info->object = (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) result);
+ android_exception_check_2 (entity, result);
+ ANDROID_DELETE_LOCAL_REF (result);
+
+ return entity;
+}
+
+static int
+androidfont_draw (struct glyph_string *s, int from, int to,
+ int x, int y, bool with_background)
+{
+ struct androidfont_info *info;
+ jarray chars;
+ int rc;
+ jobject gcontext, drawable;
+
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
+ verify (sizeof (unsigned int) == sizeof (jint));
+ info = (struct androidfont_info *) s->font;
+
+ gcontext = android_resolve_handle (s->gc->gcontext,
+ ANDROID_HANDLE_GCONTEXT);
+ drawable = android_resolve_handle (FRAME_ANDROID_DRAWABLE (s->f),
+ ANDROID_HANDLE_WINDOW);
+ chars = (*android_java_env)->NewIntArray (android_java_env,
+ to - from);
+ android_exception_check ();
+
+ (*android_java_env)->SetIntArrayRegion (android_java_env, chars,
+ 0, to - from,
+ (jint *) s->char2b + from);
+
+ info = (struct androidfont_info *) s->font;
+ prepare_face_for_display (s->f, s->face);
+
+ rc = (*android_java_env)->CallIntMethod (android_java_env,
+ font_driver,
+ font_driver_class.draw,
+ info->object,
+ gcontext, drawable,
+ chars, (jint) x, (jint) y,
+ (jint) s->width,
+ (jboolean) with_background);
+ android_exception_check_1 (chars);
+ ANDROID_DELETE_LOCAL_REF (chars);
+
+ return rc;
+}
+
+static Lisp_Object
+androidfont_open_font (struct frame *f, Lisp_Object font_entity,
+ int pixel_size)
+{
+ struct androidfont_info *font_info;
+ struct androidfont_entity *entity;
+ struct font *font;
+ Lisp_Object font_object;
+ jobject old;
+ jint value;
+
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
+ if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
+ pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
+ else if (pixel_size == 0)
+ {
+ /* This bit was copied from xfont.c. The values might need
+ adjustment. */
+
+ if (FRAME_FONT (f))
+ pixel_size = FRAME_FONT (f)->pixel_size;
+ else
+ pixel_size = 12;
+ }
+
+ __android_log_print (ANDROID_LOG_DEBUG, __func__,
+ "opening font entity %"pI"x:%d",
+ (EMACS_INT) font_entity, pixel_size);
+
+ entity = (struct androidfont_entity *) XFONT_ENTITY (font_entity);
+
+ block_input ();
+ font_object = font_make_object (VECSIZE (struct androidfont_info),
+ font_entity, pixel_size);
+ ASET (font_object, FONT_TYPE_INDEX, Qandroid);
+ font_info = (struct androidfont_info *) XFONT_OBJECT (font_object);
+ font = &font_info->font;
+ font->driver = &androidfont_driver;
+
+ /* Clear font_info->object and font_info->metrics early in case GC
+ happens later on! */
+ font_info->object = NULL;
+ font_info->metrics = NULL;
+ unblock_input ();
+
+ font_info->object
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ font_driver,
+ font_driver_class.open_font,
+ entity->object,
+ (jint) pixel_size);
+ android_exception_check ();
+
+ old = font_info->object;
+ font_info->object
+ = (*android_java_env)->NewGlobalRef (android_java_env, old);
+ android_exception_check_1 (old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!font_info->object)
+ return Qnil;
+
+ /* Copy the font attributes from the Java object. */
+ androidfont_from_java (font_info->object, font_object);
+
+ /* Copy font attributes inside EmacsFontDriver$FontObject. */
+#define DO_CARDINAL_FIELD(field) \
+ value \
+ = (*android_java_env)->GetIntField (android_java_env, \
+ font_info->object, \
+ font_object_class.field); \
+ font->field = value;
+
+ DO_CARDINAL_FIELD (min_width);
+ DO_CARDINAL_FIELD (max_width);
+ DO_CARDINAL_FIELD (pixel_size);
+ DO_CARDINAL_FIELD (height);
+ DO_CARDINAL_FIELD (space_width);
+ DO_CARDINAL_FIELD (average_width);
+ DO_CARDINAL_FIELD (ascent);
+ DO_CARDINAL_FIELD (descent);
+ DO_CARDINAL_FIELD (underline_thickness);
+ DO_CARDINAL_FIELD (underline_position);
+ DO_CARDINAL_FIELD (baseline_offset);
+ DO_CARDINAL_FIELD (relative_compose);
+ DO_CARDINAL_FIELD (default_ascent);
+ DO_CARDINAL_FIELD (encoding_charset);
+ DO_CARDINAL_FIELD (repertory_charset);
+
+#undef DO_CARDINAL_FIELD
+
+ /* This should eventually become unnecessary. */
+ font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+
+ return font_object;
+}
+
+static void
+androidfont_close_font (struct font *font)
+{
+ struct androidfont_info *info;
+ int i;
+
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
+ info = (struct androidfont_info *) font;
+
+ /* Free the font metrics cache if it exists. */
+
+ if (info->metrics)
+ {
+ for (i = 0; i < 256; ++i)
+ xfree (info->metrics[i]);
+ xfree (info->metrics);
+ }
+
+ info->metrics = NULL;
+
+ /* If info->object is NULL, then FONT was unsuccessfully created,
+ and there is no global reference that has to be deleted.
+
+ Alternatively, FONT may have been closed by font_close_object,
+ with this function called from GC. */
+
+ if (!info->object)
+ return;
+
+ (*android_java_env)->DeleteGlobalRef (android_java_env,
+ info->object);
+ info->object = NULL;
+}
+
+static int
+androidfont_has_char (Lisp_Object font, int c)
+{
+ struct androidfont_info *info;
+ struct androidfont_entity *entity;
+
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
+ if (FONT_ENTITY_P (font))
+ {
+ entity = (struct androidfont_entity *) XFONT_ENTITY (font);
+
+ return (*android_java_env)->CallIntMethod (android_java_env,
+ font_driver,
+ font_driver_class.has_char,
+ entity->object, (jint) c);
+ }
+ else
+ {
+ info = (struct androidfont_info *) XFONT_OBJECT (font);
+
+ return (*android_java_env)->CallIntMethod (android_java_env,
+ font_driver,
+ font_driver_class.has_char,
+ info->object, (jint) c);
+ }
+}
+
+static unsigned
+androidfont_encode_char (struct font *font, int c)
+{
+ struct androidfont_info *info;
+
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
+ info = (struct androidfont_info *) font;
+
+ return (*android_java_env)->CallIntMethod (android_java_env,
+ font_driver,
+ font_driver_class.encode_char,
+ info->object, (jchar) c);
+}
+
+static void
+androidfont_cache_text_extents (struct androidfont_info *info,
+ unsigned int glyph,
+ struct font_metrics *metrics)
+{
+ int i;
+
+ /* Glyphs larger than 65535 can't be cached. */
+ if (glyph >= 256 * 256)
+ return;
+
+ if (!info->metrics)
+ info->metrics = xzalloc (256 * sizeof *info->metrics);
+
+ if (!info->metrics[glyph / 256])
+ {
+ info->metrics[glyph / 256]
+ = xnmalloc (256, sizeof **info->metrics);
+
+ /* Now, all the metrics in that array as invalid by setting
+ lbearing to SHRT_MAX. */
+ for (i = 0; i < 256; ++i)
+ info->metrics[glyph / 256][i].lbearing = SHRT_MAX;
+ }
+
+ /* Finally, cache the glyph. */
+ info->metrics[glyph / 256][glyph % 256] = *metrics;
+}
+
+static bool
+androidfont_check_cached_extents (struct androidfont_info *info,
+ unsigned int glyph,
+ struct font_metrics *metrics)
+{
+ if (info->metrics && info->metrics[glyph / 256]
+ && info->metrics[glyph / 256][glyph % 256].lbearing != SHRT_MAX)
+ {
+ *metrics = info->metrics[glyph / 256][glyph % 256];
+ return true;
+ }
+
+ return false;
+}
+
+static void
+androidfont_text_extents (struct font *font, const unsigned int *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ struct androidfont_info *info;
+ jarray codepoint_array;
+ jobject metrics_object;
+ short value;
+
+ /* Maybe initialize the font driver. */
+ androidfont_check_init ();
+
+ info = (struct androidfont_info *) font;
+
+ if (nglyphs == 1
+ && androidfont_check_cached_extents (info, *code, metrics))
+ return;
+
+ /* Allocate the arrays of code points and font metrics. */
+ codepoint_array
+ = (*android_java_env)->NewIntArray (android_java_env,
+ nglyphs);
+ if (!codepoint_array)
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ memory_full (0);
+ }
+
+ verify (sizeof (unsigned int) == sizeof (jint));
+
+ /* Always true on every Android device. */
+ (*android_java_env)->SetIntArrayRegion (android_java_env,
+ codepoint_array,
+ 0, nglyphs,
+ (jint *) code);
+
+ metrics_object
+ = (*android_java_env)->AllocObject (android_java_env,
+ font_metrics_class.class);
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ font_driver,
+ font_driver_class.text_extents,
+ info->object, codepoint_array,
+ metrics_object);
+
+ if ((*android_java_env)->ExceptionCheck (android_java_env))
+ {
+ (*android_java_env)->ExceptionClear (android_java_env);
+ ANDROID_DELETE_LOCAL_REF (metrics_object);
+ ANDROID_DELETE_LOCAL_REF (codepoint_array);
+ memory_full (0);
+ }
+
+#define DO_CARDINAL_FIELD(field) \
+ value \
+ = (*android_java_env)->GetShortField (android_java_env, \
+ metrics_object, \
+ font_metrics_class.field); \
+ metrics->field = value;
+
+ DO_CARDINAL_FIELD (lbearing);
+ DO_CARDINAL_FIELD (rbearing);
+ DO_CARDINAL_FIELD (width);
+ DO_CARDINAL_FIELD (ascent);
+ DO_CARDINAL_FIELD (descent);
+
+#undef DO_CARDINAL_FIELD
+
+ ANDROID_DELETE_LOCAL_REF (metrics_object);
+ ANDROID_DELETE_LOCAL_REF (codepoint_array);
+
+ /* Emacs spends a lot of time in androidfont_text_extents, which
+ makes calling JNI too slow. Cache the metrics for this single
+ glyph. */
+
+ if (nglyphs == 1)
+ androidfont_cache_text_extents (info, *code, metrics);
+}
+
+static Lisp_Object
+androidfont_list_family (struct frame *f)
+{
+ Lisp_Object families;
+ jarray family_array;
+ jobject string;
+ jsize i, length;
+ const char *family;
+
+ /* Return if the Android font driver is not initialized. Loading
+ every font under Android takes a non trivial amount of memory,
+ and is not something that should be done when the user tries to
+ list all of the font families. */
+
+ if (!font_driver)
+ return Qnil;
+
+ family_array
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ font_driver,
+ font_driver_class.list_families);
+ android_exception_check ();
+
+ length = (*android_java_env)->GetArrayLength (android_java_env,
+ family_array);
+ families = Qnil;
+
+ for (i = 0; i < length; ++i)
+ {
+ string = (*android_java_env)->GetObjectArrayElement (android_java_env,
+ family_array, i);
+ family = (*android_java_env)->GetStringUTFChars (android_java_env,
+ (jstring) string, NULL);
+
+ if (!family)
+ {
+ ANDROID_DELETE_LOCAL_REF (string);
+ ANDROID_DELETE_LOCAL_REF (family_array);
+ }
+
+ families = Fcons (build_string_from_utf8 (string), families);
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ (jstring) string,
+ family);
+ ANDROID_DELETE_LOCAL_REF (string);
+ }
+
+ ANDROID_DELETE_LOCAL_REF (family_array);
+ return Fnreverse (families);
+}
+
+struct font_driver androidfont_driver =
+ {
+ .type = LISPSYM_INITIALLY (Qandroid),
+ .case_sensitive = true,
+ .get_cache = androidfont_get_cache,
+ .list = androidfont_list,
+ .match = androidfont_match,
+ .draw = androidfont_draw,
+ .open_font = androidfont_open_font,
+ .close_font = androidfont_close_font,
+ .has_char = androidfont_has_char,
+ .encode_char = androidfont_encode_char,
+ .text_extents = androidfont_text_extents,
+ .list_family = androidfont_list_family,
+ };
+
+static void
+syms_of_androidfont_for_pdumper (void)
+{
+ register_font_driver (&androidfont_driver, NULL);
+}
+
+void
+syms_of_androidfont (void)
+{
+ DEFSYM (Qfontsize, "fontsize");
+
+ pdumper_do_now_and_after_load (syms_of_androidfont_for_pdumper);
+
+ font_cache = list (Qnil);
+ staticpro (&font_cache);
+}
+
+void
+init_androidfont (void)
+{
+ if (!android_init_gui)
+ return;
+
+ android_init_font_driver ();
+ android_init_font_spec ();
+ android_init_font_metrics ();
+ android_init_font_object ();
+ android_init_integer ();
+
+ /* The Java font driver is not initialized here because it uses a lot
+ of memory. */
+}
+
+void
+android_finalize_font_entity (struct font_entity *entity)
+{
+ struct androidfont_entity *info;
+
+ info = (struct androidfont_entity *) entity;
+
+ if (info->object)
+ (*android_java_env)->DeleteGlobalRef (android_java_env,
+ info->object);
+
+ /* Not sure if this can be called twice. */
+ info->object = NULL;
+}
+
+#endif
diff --git a/src/androidgui.h b/src/androidgui.h
new file mode 100644
index 00000000000..6db25098398
--- /dev/null
+++ b/src/androidgui.h
@@ -0,0 +1,724 @@
+/* Android window system support.
+ 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/>. */
+
+#ifndef _ANDROID_GUI_H_
+#define _ANDROID_GUI_H_
+
+struct android_char_struct
+{
+ int rbearing;
+ int lbearing;
+ int width;
+ int ascent;
+ int descent;
+};
+
+typedef struct android_char_struct XCharStruct;
+
+typedef unsigned short android_handle;
+
+typedef android_handle android_pixmap, Emacs_Pixmap;
+typedef android_handle android_window, Emacs_Window;
+typedef android_handle android_gcontext, GContext;
+typedef android_handle android_drawable, Drawable;
+typedef android_handle android_cursor, Emacs_Cursor;
+
+typedef unsigned int android_time;
+
+struct android_rectangle
+{
+ int x, y;
+ unsigned width, height;
+};
+
+struct android_point
+{
+ int x, y;
+};
+
+/* Keep this in sync with EmacsGC.java! */
+
+enum android_gc_function
+ {
+ ANDROID_GC_COPY = 0,
+ ANDROID_GC_XOR = 1,
+ };
+
+enum android_gc_value_mask
+ {
+ ANDROID_GC_FOREGROUND = (1 << 0),
+ ANDROID_GC_BACKGROUND = (1 << 1),
+ ANDROID_GC_FUNCTION = (1 << 2),
+ ANDROID_GC_CLIP_X_ORIGIN = (1 << 3),
+ ANDROID_GC_CLIP_Y_ORIGIN = (1 << 4),
+ ANDROID_GC_CLIP_MASK = (1 << 5),
+ ANDROID_GC_STIPPLE = (1 << 6),
+ ANDROID_GC_FILL_STYLE = (1 << 7),
+ ANDROID_GC_TILE_STIP_X_ORIGIN = (1 << 8),
+ ANDROID_GC_TILE_STIP_Y_ORIGIN = (1 << 9),
+ };
+
+enum android_fill_style
+ {
+ ANDROID_FILL_SOLID = 0,
+ ANDROID_FILL_OPAQUE_STIPPLED = 1,
+ };
+
+enum android_window_value_mask
+ {
+ ANDROID_CW_BACK_PIXEL = (1 << 1),
+ ANDROID_CW_OVERRIDE_REDIRECT = (1 << 2),
+ };
+
+struct android_set_window_attributes
+{
+ /* The background pixel. */
+ unsigned long background_pixel;
+
+ /* Whether or not the window is override redirect. This cannot be
+ set after creation on Android. */
+ bool override_redirect;
+};
+
+struct android_gc_values
+{
+ /* The foreground and background. */
+ unsigned long foreground, background;
+
+ /* The function. */
+ enum android_gc_function function;
+
+ /* The fill style. */
+ enum android_fill_style fill_style;
+
+ /* The clip X and Y origin. */
+ int clip_x_origin, clip_y_origin;
+
+ /* The clip mask image and stipple. */
+ android_pixmap clip_mask, stipple;
+
+ /* The tile-stipple X and Y origins. */
+ int ts_x_origin, ts_y_origin;
+};
+
+/* X-like graphics context structure. This is implemented in
+ EmacsGC.java, but a copy is kept here to avoid sending changes all
+ the time. */
+
+struct android_gc
+{
+ /* Array of clip rectangles. */
+ struct android_rectangle *clip_rects;
+
+ /* Number of clip rectangles. When -1, it means clipping should not
+ be applied. */
+ int num_clip_rects;
+
+ /* The Java-side handle. */
+ android_gcontext gcontext;
+
+ /* Current foreground color. */
+ unsigned long foreground;
+
+ /* Current background color. */
+ unsigned long background;
+};
+
+enum android_swap_action
+ {
+ ANDROID_COPIED,
+ };
+
+enum android_shape
+ {
+ ANDROID_CONVEX,
+ };
+
+enum android_coord_mode
+ {
+ ANDROID_COORD_MODE_ORIGIN,
+ };
+
+struct android_swap_info
+{
+ /* The window to swap. */
+ android_window swap_window;
+
+ /* Unused field present only for consistency with X. */
+ enum android_swap_action swap_action;
+};
+
+#define NativeRectangle Emacs_Rectangle
+#define CONVERT_TO_NATIVE_RECT(xr, nr) ((xr) = (nr))
+#define CONVERT_FROM_EMACS_RECT(xr, nr) ((nr) = (xr))
+
+#define STORE_NATIVE_RECT(nr, rx, ry, rwidth, rheight) \
+ ((nr).x = (rx), (nr).y = (ry), \
+ (nr).width = (rwidth), (nr).height = (rheight)) \
+
+#define ForgetGravity 0
+#define NorthWestGravity 1
+#define NorthGravity 2
+#define NorthEastGravity 3
+#define WestGravity 4
+#define CenterGravity 5
+#define EastGravity 6
+#define SouthWestGravity 7
+#define SouthGravity 8
+#define SouthEastGravity 9
+#define StaticGravity 10
+
+#define NoValue 0x0000
+#define XValue 0x0001
+#define YValue 0x0002
+#define WidthValue 0x0004
+#define HeightValue 0x0008
+#define AllValues 0x000F
+#define XNegative 0x0010
+#define YNegative 0x0020
+
+#define USPosition (1L << 0) /* user specified x, y */
+#define USSize (1L << 1) /* user specified width, height */
+#define PPosition (1L << 2) /* program specified position */
+#define PSize (1L << 3) /* program specified size */
+#define PMinSize (1L << 4) /* program specified minimum size */
+#define PMaxSize (1L << 5) /* program specified maximum size */
+#define PResizeInc (1L << 6) /* program specified resize increments */
+#define PAspect (1L << 7) /* program specified min, max aspect ratios */
+#define PBaseSize (1L << 8) /* program specified base for incrementing */
+#define PWinGravity (1L << 9) /* program specified window gravity */
+
+#ifndef ANDROID_STUBIFY
+
+/* Universal NULL handle. */
+static const int ANDROID_NONE, ANDROID_NO_SYMBOL;
+
+/* Keep these as conceptually close to X as possible: that makes
+ synchronizing code between the ports much easier. */
+
+enum android_event_type
+ {
+ ANDROID_KEY_PRESS,
+ ANDROID_KEY_RELEASE,
+ ANDROID_CONFIGURE_NOTIFY,
+ ANDROID_FOCUS_IN,
+ ANDROID_FOCUS_OUT,
+ ANDROID_WINDOW_ACTION,
+ ANDROID_ENTER_NOTIFY,
+ ANDROID_LEAVE_NOTIFY,
+ ANDROID_MOTION_NOTIFY,
+ ANDROID_BUTTON_PRESS,
+ ANDROID_BUTTON_RELEASE,
+ ANDROID_TOUCH_DOWN,
+ ANDROID_TOUCH_UP,
+ ANDROID_TOUCH_MOVE,
+ ANDROID_WHEEL,
+ ANDROID_ICONIFIED,
+ ANDROID_DEICONIFIED,
+ ANDROID_CONTEXT_MENU,
+ ANDROID_EXPOSE,
+ ANDROID_INPUT_METHOD,
+ };
+
+struct android_any_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+};
+
+enum android_modifier_mask
+ {
+ ANDROID_SHIFT_MASK = 193,
+ ANDROID_CONTROL_MASK = 4096,
+ ANDROID_ALT_MASK = 2,
+ ANDROID_SUPER_MASK = 4,
+ };
+
+struct android_key_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ android_time time;
+ unsigned int state;
+ unsigned int keycode;
+
+ /* If this field is -1, then android_lookup_string should be called
+ to retrieve the associated individual characters. */
+ unsigned int unicode_char;
+};
+
+typedef struct android_key_event android_key_pressed_event;
+
+/* These hard coded values are Android modifier keycodes derived
+ through experimentation. */
+
+#define ANDROID_IS_MODIFIER_KEY(key) \
+ ((key) == 57 || (key) == 58 || (key) == 113 || (key) == 114 \
+ || (key) == 119 || (key) == 117 || (key) == 118 || (key) == 78 \
+ || (key) == 94 || (key) == 59 || (key) == 60 || (key) == 95 \
+ || (key) == 63 || (key) == 115)
+
+struct android_configure_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ android_time time;
+ int x, y;
+ int width, height;
+};
+
+struct android_focus_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ android_time time;
+};
+
+struct android_window_action_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+
+ /* The window handle. This can be ANDROID_NONE. */
+ android_window window;
+
+ /* Numerical identifier for this action. If 0 and WINDOW is set,
+ then it means the frame associated with that window has been
+ destroyed. Otherwise, it means Emacs should create a new
+ frame. */
+ unsigned int action;
+};
+
+struct android_crossing_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ int x, y;
+ unsigned long time;
+};
+
+struct android_motion_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ int x, y;
+ unsigned long time;
+};
+
+struct android_button_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ int x, y;
+ unsigned long time;
+ unsigned int state;
+ unsigned int button;
+};
+
+struct android_expose_event
+{
+ enum android_event_type type;
+ unsigned long serial;
+ android_window window;
+ int x, y;
+ int width, height;
+};
+
+struct android_touch_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* Serial identifying the event. */
+ unsigned long serial;
+
+ /* Window associated with the event. */
+ android_window window;
+
+ /* X and Y coordinates of the event. */
+ int x, y;
+
+ /* Time of the event, and the pointer identifier. */
+ unsigned long time;
+
+ /* Index of the pointer being tracked. */
+ unsigned int pointer_id;
+};
+
+struct android_wheel_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* Serial identifying the event. */
+ unsigned long serial;
+
+ /* Window associated with the event. */
+ android_window window;
+
+ /* X and Y coordinates of the event. */
+ int x, y;
+
+ /* Time of the event, and the pointer identifier. */
+ unsigned long time;
+
+ /* Modifier state at the time of the event. */
+ int state;
+
+ /* Motion alongside the X and Y axes. */
+ double x_delta, y_delta;
+};
+
+struct android_iconify_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* Serial identifying the event. */
+ unsigned long serial;
+
+ /* Window associated with the event. */
+ android_window window;
+};
+
+struct android_menu_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* Serial identifying the event. */
+ unsigned long serial;
+
+ /* Window associated with the event. Always None. */
+ android_window window;
+
+ /* Menu event ID. */
+ int menu_event_id;
+
+ /* Menu event serial; this counter identifies the context menu. */
+ int menu_event_serial;
+};
+
+enum android_ime_operation
+ {
+ ANDROID_IME_COMMIT_TEXT,
+ ANDROID_IME_DELETE_SURROUNDING_TEXT,
+ ANDROID_IME_FINISH_COMPOSING_TEXT,
+ ANDROID_IME_SET_COMPOSING_TEXT,
+ ANDROID_IME_SET_COMPOSING_REGION,
+ ANDROID_IME_SET_POINT,
+ ANDROID_IME_START_BATCH_EDIT,
+ ANDROID_IME_END_BATCH_EDIT,
+ ANDROID_IME_REQUEST_SELECTION_UPDATE,
+ ANDROID_IME_REQUEST_CURSOR_UPDATES,
+ };
+
+enum
+ {
+ ANDROID_CURSOR_UPDATE_IMMEDIATE = 1,
+ ANDROID_CURSOR_UPDATE_MONITOR = (1 << 1),
+ };
+
+struct android_ime_event
+{
+ /* Type of the event. */
+ enum android_event_type type;
+
+ /* The event serial. */
+ unsigned long serial;
+
+ /* The associated window. */
+ android_window window;
+
+ /* What operation is being performed. */
+ enum android_ime_operation operation;
+
+ /* The details of the operation. START and END provide buffer
+ indices, and may actually mean ``left'' and ``right''. */
+ ptrdiff_t start, end, position;
+
+ /* The number of characters in TEXT.
+
+ If OPERATION is ANDROID_IME_REQUEST_CURSOR_UPDATES, then this is
+ actually the cursor update mode associated with that
+ operation. */
+ size_t length;
+
+ /* TEXT is either NULL, or a pointer to LENGTH bytes of malloced
+ UTF-16 encoded text that must be decoded by Emacs.
+
+ POSITION is where point should end up after the text is
+ committed, relative to TEXT. If POSITION is less than 0, it is
+ relative to TEXT's start; otherwise, it is relative to its
+ end. */
+ unsigned short *text;
+
+ /* Value to set the counter to after the operation completes. */
+ unsigned long counter;
+};
+
+union android_event
+{
+ enum android_event_type type;
+ struct android_any_event xany;
+ struct android_key_event xkey;
+ struct android_configure_event xconfigure;
+ struct android_focus_event xfocus;
+ struct android_window_action_event xaction;
+ struct android_crossing_event xcrossing;
+ struct android_motion_event xmotion;
+ struct android_button_event xbutton;
+ struct android_expose_event xexpose;
+
+ /* This has no parallel in X, since the X model of having
+ monotonically increasing touch IDs can't work on Android. */
+ struct android_touch_event touch;
+
+ /* This has no parallel in X outside the X Input Extension, and
+ emulating the input extension interface would be awfully
+ complicated. */
+ struct android_wheel_event wheel;
+
+ /* This has no parallel in X because Android doesn't have window
+ properties. */
+ struct android_iconify_event iconified;
+
+ /* This is only used to transmit selected menu items. */
+ struct android_menu_event menu;
+
+ /* This is used to dispatch input method editing requests. */
+ struct android_ime_event ime;
+};
+
+enum
+ {
+ ANDROID_CURRENT_TIME = 0L,
+ };
+
+enum android_lookup_status
+ {
+ ANDROID_BUFFER_OVERFLOW,
+ ANDROID_LOOKUP_NONE,
+ ANDROID_LOOKUP_CHARS,
+ ANDROID_LOOKUP_KEYSYM,
+ ANDROID_LOOKUP_BOTH,
+ };
+
+enum android_ic_mode
+ {
+ ANDROID_IC_MODE_NULL = 0,
+ ANDROID_IC_MODE_ACTION = 1,
+ ANDROID_IC_MODE_TEXT = 2,
+ };
+
+extern int android_pending (void);
+extern void android_next_event (union android_event *);
+extern bool android_check_if_event (union android_event *,
+ bool (*) (union android_event *,
+ void *),
+ void *);
+
+extern android_window android_create_window (android_window, int,
+ int, int, int,
+ enum android_window_value_mask,
+ struct
+ android_set_window_attributes *);
+extern void android_change_window_attributes (android_window,
+ enum android_window_value_mask,
+ struct
+ android_set_window_attributes *);
+extern void android_set_window_background (android_window, unsigned long);
+extern void android_destroy_window (android_window);
+extern void android_reparent_window (android_window, android_window,
+ int, int);
+extern void android_set_clip_rectangles (struct android_gc *,
+ int, int,
+ struct android_rectangle *,
+ int);
+extern void android_change_gc (struct android_gc *,
+ enum android_gc_value_mask,
+ struct android_gc_values *);
+
+extern void android_clear_window (android_window);
+extern void android_map_window (android_window);
+extern void android_unmap_window (android_window);
+extern void android_resize_window (android_window, unsigned int,
+ unsigned int);
+extern void android_move_window (android_window, int, int);
+extern void android_swap_buffers (struct android_swap_info *, int);
+extern void android_get_gc_values (struct android_gc *,
+ enum android_gc_value_mask,
+ struct android_gc_values *);
+extern void android_set_foreground (struct android_gc *,
+ unsigned long);
+extern void android_fill_rectangle (android_drawable, struct android_gc *,
+ int, int, unsigned int, unsigned int);
+extern android_pixmap android_create_pixmap_from_bitmap_data (char *,
+ unsigned int,
+ unsigned int,
+ unsigned long,
+ unsigned long,
+ unsigned int);
+extern void android_set_clip_mask (struct android_gc *, android_pixmap);
+extern void android_set_fill_style (struct android_gc *,
+ enum android_fill_style);
+extern void android_copy_area (android_drawable, android_drawable,
+ struct android_gc *, int, int,
+ unsigned int, unsigned int, int, int);
+extern void android_free_pixmap (android_drawable);
+
+extern void android_set_background (struct android_gc *, unsigned long);
+extern void android_fill_polygon (android_drawable, struct android_gc *,
+ struct android_point *, int,
+ enum android_shape,
+ enum android_coord_mode);
+extern void android_draw_rectangle (android_drawable, struct android_gc *,
+ int, int, unsigned int, unsigned int);
+extern void android_draw_point (android_window, struct android_gc *,
+ int, int);
+extern void android_draw_line (android_window, struct android_gc *,
+ int, int, int, int);
+extern android_pixmap android_create_pixmap (unsigned int, unsigned int,
+ int);
+extern void android_set_ts_origin (struct android_gc *, int, int);
+extern void android_clear_area (android_window, int, int, unsigned int,
+ unsigned int);
+extern android_pixmap android_create_bitmap_from_data (char *, unsigned int,
+ unsigned int);
+
+extern void android_bell (void);
+extern void android_set_input_focus (android_window, unsigned long);
+extern void android_raise_window (android_window);
+extern void android_lower_window (android_window);
+extern int android_query_tree (android_window, android_window *,
+ android_window *, android_window **,
+ unsigned int *);
+extern void android_get_geometry (android_window, android_window *,
+ int *, int *, unsigned int *,
+ unsigned int *, unsigned int *);
+extern void android_move_resize_window (android_window, int, int,
+ unsigned int, unsigned int);
+extern void android_map_raised (android_window);
+extern void android_translate_coordinates (android_window, int,
+ int, int *, int *);
+extern int android_wc_lookup_string (android_key_pressed_event *,
+ wchar_t *, int, int *,
+ enum android_lookup_status *);
+extern void android_update_ic (android_window, ptrdiff_t, ptrdiff_t,
+ ptrdiff_t, ptrdiff_t);
+extern void android_reset_ic (android_window, enum android_ic_mode);
+extern void android_update_extracted_text (android_window, void *,
+ int);
+extern void android_update_cursor_anchor_info (android_window, float,
+ float, float, float);
+extern int android_set_fullscreen (android_window, bool);
+
+enum android_cursor_shape
+ {
+ ANDROID_XC_XTERM = 1008,
+ ANDROID_XC_LEFT_PTR = 1000,
+ ANDROID_XC_WATCH = 1004,
+ ANDROID_XC_HAND2 = 1002,
+ ANDROID_XC_SB_H_DOUBLE_ARROW = 1014,
+ ANDROID_XC_SB_V_DOUBLE_ARROW = 1015,
+ ANDROID_XC_LEFT_SIDE = 1020,
+ ANDROID_XC_TOP_LEFT_CORNER = 1020,
+ ANDROID_XC_TOP_SIDE = 1020,
+ ANDROID_XC_TOP_RIGHT_CORNER = 1020,
+ ANDROID_XC_RIGHT_SIDE = 1020,
+ ANDROID_XC_BOTTOM_RIGHT_CORNER = 1020,
+ ANDROID_XC_BOTTOM_SIDE = 1020,
+ ANDROID_XC_BOTTOM_LEFT_CORNER = 1020,
+ ANDROID_XC_NULL = 0,
+ };
+
+extern android_cursor android_create_font_cursor (enum android_cursor_shape);
+extern void android_define_cursor (android_window, android_cursor);
+extern void android_free_cursor (android_cursor);
+
+#endif
+
+
+
+/* Image support. Keep the API as similar to XImage as possible. To
+ avoid leaving a huge mess of "#ifndef ANDROID_STUBIFY" in image.c,
+ stubs should be defined for all functions. */
+
+enum android_image_format
+ {
+ ANDROID_Z_PIXMAP,
+ };
+
+struct android_image
+{
+ int width, height;
+ enum android_image_format format;
+ char *data;
+ int depth;
+ int bytes_per_line;
+ int bits_per_pixel;
+};
+
+extern struct android_image *android_create_image (unsigned int,
+ enum android_image_format,
+ char *, unsigned int,
+ unsigned int);
+extern void android_destroy_image (struct android_image *);
+
+extern void android_put_pixel (struct android_image *, int, int,
+ unsigned long);
+extern unsigned long android_get_pixel (struct android_image *, int, int);
+extern struct android_image *android_get_image (android_drawable,
+ enum android_image_format);
+extern void android_put_image (android_pixmap, struct android_image *);
+
+
+/* Native image transforms. */
+
+/* 3x2 matrix describing a projective transform. See
+ android_transform_coordinates for details. */
+
+struct android_transform
+{
+ float m1, m2, m3;
+ float m4, m5, m6;
+};
+
+extern void android_project_image_bilinear (struct android_image *,
+ struct android_image *,
+ struct android_transform *);
+extern void android_project_image_nearest (struct android_image *,
+ struct android_image *,
+ struct android_transform *);
+
+
+
+/* X emulation stuff also needed while building stubs. */
+
+extern struct android_gc *android_create_gc (enum android_gc_value_mask,
+ struct android_gc_values *);
+extern void android_free_gc (struct android_gc *);
+
+#endif /* _ANDROID_GUI_H_ */
diff --git a/src/androidmenu.c b/src/androidmenu.c
new file mode 100644
index 00000000000..f74e7ca6d99
--- /dev/null
+++ b/src/androidmenu.c
@@ -0,0 +1,829 @@
+/* Communication module for Android terminals.
+
+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 "lisp.h"
+#include "androidterm.h"
+#include "android.h"
+#include "blockinput.h"
+#include "keyboard.h"
+#include "menu.h"
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+/* Flag indicating whether or not a popup menu has been posted and not
+ yet popped down. */
+
+static int popup_activated_flag;
+
+/* Serial number used to identify which context menu events are
+ associated with the context menu currently being displayed. */
+
+unsigned int current_menu_serial;
+
+int
+popup_activated (void)
+{
+ return popup_activated_flag;
+}
+
+
+
+/* Toolkit menu implementation. */
+
+/* Structure describing the EmacsContextMenu class. */
+
+struct android_emacs_context_menu
+{
+ jclass class;
+ jmethodID create_context_menu;
+ jmethodID add_item;
+ jmethodID add_submenu;
+ jmethodID add_pane;
+ jmethodID parent;
+ jmethodID display;
+ jmethodID dismiss;
+};
+
+/* Identifiers associated with the EmacsContextMenu class. */
+static struct android_emacs_context_menu menu_class;
+
+static void
+android_init_emacs_context_menu (void)
+{
+ jclass old;
+
+ menu_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsContextMenu");
+ eassert (menu_class.class);
+
+ old = menu_class.class;
+ menu_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!menu_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ menu_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ menu_class.class, \
+ name, signature); \
+ eassert (menu_class.c_name);
+
+#define FIND_METHOD_STATIC(c_name, name, signature) \
+ menu_class.c_name \
+ = (*android_java_env)->GetStaticMethodID (android_java_env, \
+ menu_class.class, \
+ name, signature); \
+ eassert (menu_class.c_name);
+
+ FIND_METHOD_STATIC (create_context_menu, "createContextMenu",
+ "(Ljava/lang/String;)"
+ "Lorg/gnu/emacs/EmacsContextMenu;");
+
+ FIND_METHOD (add_item, "addItem", "(ILjava/lang/String;ZZZ"
+ "Ljava/lang/String;Z)V");
+ FIND_METHOD (add_submenu, "addSubmenu", "(Ljava/lang/String;"
+ "Ljava/lang/String;Ljava/lang/String;)"
+ "Lorg/gnu/emacs/EmacsContextMenu;");
+ FIND_METHOD (add_pane, "addPane", "(Ljava/lang/String;)V");
+ FIND_METHOD (parent, "parent", "()Lorg/gnu/emacs/EmacsContextMenu;");
+ FIND_METHOD (display, "display", "(Lorg/gnu/emacs/EmacsWindow;III)Z");
+ FIND_METHOD (dismiss, "dismiss", "(Lorg/gnu/emacs/EmacsWindow;)V");
+
+#undef FIND_METHOD
+#undef FIND_METHOD_STATIC
+}
+
+static void
+android_unwind_local_frame (void)
+{
+ (*android_java_env)->PopLocalFrame (android_java_env, NULL);
+}
+
+/* Push a local reference frame to the JVM stack and record it on the
+ specpdl. Release local references created within that frame when
+ the specpdl is unwound past where it is after returning. */
+
+static void
+android_push_local_frame (void)
+{
+ int rc;
+
+ rc = (*android_java_env)->PushLocalFrame (android_java_env, 30);
+
+ /* This means the JVM ran out of memory. */
+ if (rc < 1)
+ android_exception_check ();
+
+ record_unwind_protect_void (android_unwind_local_frame);
+}
+
+/* Data for android_dismiss_menu. */
+
+struct android_dismiss_menu_data
+{
+ /* The menu object. */
+ jobject menu;
+
+ /* The window object. */
+ jobject window;
+};
+
+/* Cancel the context menu passed in POINTER. Also, clear
+ popup_activated_flag. */
+
+static void
+android_dismiss_menu (void *pointer)
+{
+ struct android_dismiss_menu_data *data;
+
+ data = pointer;
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ data->menu,
+ menu_class.dismiss,
+ data->window);
+ popup_activated_flag = 0;
+}
+
+/* Recursively process events until a ANDROID_CONTEXT_MENU event
+ arrives. Then, return the item ID specified in the event in
+ *ID. */
+
+static void
+android_process_events_for_menu (int *id)
+{
+ int blocked;
+
+ /* Set menu_event_id to -1; handle_one_android_event will set it to
+ the event ID upon receiving a context menu event. This can cause
+ a non-local exit. */
+ x_display_list->menu_event_id = -1;
+
+ /* Unblock input completely. */
+ blocked = interrupt_input_blocked;
+ totally_unblock_input ();
+
+ /* Now wait for the menu event ID to change. */
+ while (x_display_list->menu_event_id == -1)
+ {
+ /* Wait for events to become available. */
+ android_wait_event ();
+
+ /* Process pending signals. */
+ process_pending_signals ();
+
+ /* Maybe quit. This is important because the framework (on
+ Android 4.0.3) can sometimes fail to deliver context menu
+ closed events if a submenu was opened, and the user still
+ needs to be able to quit. */
+ maybe_quit ();
+ }
+
+ /* Restore the input block. */
+ interrupt_input_blocked = blocked;
+
+ /* Return the ID. */
+ *id = x_display_list->menu_event_id;
+}
+
+/* Structure describing a ``subprefix'' in the menu. */
+
+struct android_menu_subprefix
+{
+ /* The subprefix above. */
+ struct android_menu_subprefix *last;
+
+ /* The subprefix itself. */
+ Lisp_Object subprefix;
+};
+
+/* Free the subprefixes starting from *DATA. */
+
+static void
+android_free_subprefixes (void *data)
+{
+ struct android_menu_subprefix **head, *subprefix;
+
+ head = data;
+
+ while (*head)
+ {
+ subprefix = *head;
+ *head = subprefix->last;
+
+ xfree (subprefix);
+ }
+}
+
+Lisp_Object
+android_menu_show (struct frame *f, int x, int y, int menuflags,
+ Lisp_Object title, const char **error_name)
+{
+ jobject context_menu, current_context_menu;
+ jobject title_string, help_string, temp;
+ size_t i;
+ Lisp_Object pane_name, prefix;
+ const char *pane_string;
+ specpdl_ref count, count1;
+ Lisp_Object item_name, enable, def, tem, entry, type, selected;
+ Lisp_Object help;
+ jmethodID method;
+ jobject store;
+ bool rc;
+ jobject window;
+ int id, item_id, submenu_depth;
+ struct android_dismiss_menu_data data;
+ struct android_menu_subprefix *subprefix, *temp_subprefix;
+ struct android_menu_subprefix *subprefix_1;
+ bool checkmark;
+ unsigned int serial;
+
+ count = SPECPDL_INDEX ();
+ serial = ++current_menu_serial;
+
+ block_input ();
+
+ /* Push the first local frame. */
+ android_push_local_frame ();
+
+ /* Push the first local frame for the context menu. */
+ title_string = (!NILP (title)
+ ? (jobject) android_build_string (title)
+ : NULL);
+ method = menu_class.create_context_menu;
+ current_context_menu = context_menu
+ = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ menu_class.class,
+ method,
+ title_string);
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ /* Push the second local frame for temporaries. */
+ count1 = SPECPDL_INDEX ();
+ android_push_local_frame ();
+
+ /* Iterate over the menu. */
+ i = 0, submenu_depth = 0;
+
+ while (i < menu_items_used)
+ {
+ if (NILP (AREF (menu_items, i)))
+ {
+ /* This is the start of a new submenu. However, it can be
+ ignored here. */
+ i += 1;
+ submenu_depth += 1;
+ }
+ else if (EQ (AREF (menu_items, i), Qlambda))
+ {
+ /* This is the end of a submenu. Go back to the previous
+ context menu. */
+ store = current_context_menu;
+ current_context_menu
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ current_context_menu,
+ menu_class.parent);
+ android_exception_check ();
+
+ if (store != context_menu)
+ ANDROID_DELETE_LOCAL_REF (store);
+ i += 1;
+ submenu_depth -= 1;
+
+ if (!current_context_menu || submenu_depth < 0)
+ {
+ __android_log_print (ANDROID_LOG_FATAL, __func__,
+ "unbalanced submenu pop in menu_items");
+ emacs_abort ();
+ }
+ }
+ else if (EQ (AREF (menu_items, i), Qt)
+ && submenu_depth != 0)
+ i += MENU_ITEMS_PANE_LENGTH;
+ else if (EQ (AREF (menu_items, i), Qquote))
+ i += 1;
+ else if (EQ (AREF (menu_items, i), Qt))
+ {
+ /* This is a new pane. Switch back to the topmost context
+ menu. */
+ if (current_context_menu != context_menu)
+ ANDROID_DELETE_LOCAL_REF (current_context_menu);
+ current_context_menu = context_menu;
+
+ /* Now figure out the title of this pane. */
+ pane_name = AREF (menu_items, i + MENU_ITEMS_PANE_NAME);
+ prefix = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+ pane_string = (NILP (pane_name)
+ ? "" : SSDATA (pane_name));
+ if ((menuflags & MENU_KEYMAPS) && !NILP (prefix))
+ pane_string++;
+
+ /* Add the pane. */
+ temp = (*android_java_env)->NewStringUTF (android_java_env,
+ pane_string);
+ android_exception_check ();
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_pane,
+ temp);
+ android_exception_check ();
+ ANDROID_DELETE_LOCAL_REF (temp);
+
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ else
+ {
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+ def = AREF (menu_items, i + MENU_ITEMS_ITEM_DEFINITION);
+ type = AREF (menu_items, i + MENU_ITEMS_ITEM_TYPE);
+ selected = AREF (menu_items, i + MENU_ITEMS_ITEM_SELECTED);
+ help = AREF (menu_items, i + MENU_ITEMS_ITEM_HELP);
+
+ /* This is an actual menu item (or submenu). Add it to the
+ menu. */
+
+ if (i + MENU_ITEMS_ITEM_LENGTH < menu_items_used
+ && NILP (AREF (menu_items, i + MENU_ITEMS_ITEM_LENGTH)))
+ {
+ /* This is a submenu. Add it. */
+ title_string = (!NILP (item_name)
+ ? android_build_string (item_name)
+ : NULL);
+ help_string = NULL;
+
+ /* Menu items can have tool tips on Android 26 and
+ later. In this case, set it to the help string. */
+
+ if (android_get_current_api_level () >= 26
+ && STRINGP (help))
+ help_string = android_build_string (help);
+
+ store = current_context_menu;
+ current_context_menu
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_submenu,
+ title_string, NULL,
+ help_string);
+ android_exception_check ();
+
+ if (store != context_menu)
+ ANDROID_DELETE_LOCAL_REF (store);
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ if (help_string)
+ ANDROID_DELETE_LOCAL_REF (help_string);
+ }
+ else if (NILP (def) && menu_separator_name_p (SSDATA (item_name)))
+ /* Ignore this separator item. */
+ ;
+ else
+ {
+ /* Compute the item ID. This is the index of value.
+ Make sure it doesn't overflow. */
+
+ if (!INT_ADD_OK (0, i + MENU_ITEMS_ITEM_VALUE, &item_id))
+ memory_full (i + MENU_ITEMS_ITEM_VALUE * sizeof (Lisp_Object));
+
+ /* Add this menu item with the appropriate state. */
+
+ title_string = (!NILP (item_name)
+ ? android_build_string (item_name)
+ : NULL);
+ help_string = NULL;
+
+ /* Menu items can have tool tips on Android 26 and
+ later. In this case, set it to the help string. */
+
+ if (android_get_current_api_level () >= 26
+ && STRINGP (help))
+ help_string = android_build_string (help);
+
+ /* Determine whether or not to display a check box. */
+
+ checkmark = (EQ (type, QCtoggle)
+ || EQ (type, QCradio));
+
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ current_context_menu,
+ menu_class.add_item,
+ (jint) item_id,
+ title_string,
+ (jboolean) !NILP (enable),
+ (jboolean) checkmark,
+ (jboolean) !NILP (selected),
+ help_string,
+ (jboolean) (EQ (type,
+ QCradio)));
+ android_exception_check ();
+
+ if (title_string)
+ ANDROID_DELETE_LOCAL_REF (title_string);
+
+ if (help_string)
+ ANDROID_DELETE_LOCAL_REF (help_string);
+ }
+
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ /* The menu has now been built. Pop the second local frame. */
+ unbind_to (count1, Qnil);
+
+ /* Now, display the context menu. */
+ window = android_resolve_handle (FRAME_ANDROID_WINDOW (f),
+ ANDROID_HANDLE_WINDOW);
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ context_menu,
+ menu_class.display,
+ window, (jint) x,
+ (jint) y,
+ (jint) serial);
+ android_exception_check ();
+
+ if (!rc)
+ /* This means displaying the menu failed. */
+ goto finish;
+
+ /* Make sure the context menu is always dismissed. */
+ data.menu = context_menu;
+ data.window = window;
+ record_unwind_protect_ptr (android_dismiss_menu, &data);
+
+ /* Next, process events waiting for something to be selected. */
+ popup_activated_flag = 1;
+ android_process_events_for_menu (&id);
+
+ if (!id)
+ /* This means no menu item was selected. */
+ goto finish;
+
+ /* This means the id is invalid. */
+ if (id >= ASIZE (menu_items))
+ goto finish;
+
+ /* Now return the menu item at that location. */
+ tem = Qnil;
+ subprefix = NULL;
+ record_unwind_protect_ptr (android_free_subprefixes, &subprefix);
+
+ /* Find the selected item, and its pane, to return
+ the proper value. */
+
+ prefix = entry = Qnil;
+ i = 0;
+ while (i < menu_items_used)
+ {
+ if (NILP (AREF (menu_items, i)))
+ {
+ temp_subprefix = xmalloc (sizeof *temp_subprefix);
+ temp_subprefix->last = subprefix;
+ subprefix = temp_subprefix;
+ subprefix->subprefix = prefix;
+
+ prefix = entry;
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qlambda))
+ {
+ prefix = subprefix->subprefix;
+ temp_subprefix = subprefix->last;
+ xfree (subprefix);
+ subprefix = temp_subprefix;
+
+ i++;
+ }
+ else if (EQ (AREF (menu_items, i), Qt))
+ {
+ prefix
+ = AREF (menu_items, i + MENU_ITEMS_PANE_PREFIX);
+ i += MENU_ITEMS_PANE_LENGTH;
+ }
+ /* Ignore a nil in the item list.
+ It's meaningful only for dialog boxes. */
+ else if (EQ (AREF (menu_items, i), Qquote))
+ i += 1;
+ else
+ {
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+ if (i + MENU_ITEMS_ITEM_VALUE == id)
+ {
+ if (menuflags & MENU_KEYMAPS)
+ {
+ entry = list1 (entry);
+
+ if (!NILP (prefix))
+ entry = Fcons (prefix, entry);
+
+ for (subprefix_1 = subprefix; subprefix_1;
+ subprefix_1 = subprefix_1->last)
+ if (!NILP (subprefix_1->subprefix))
+ entry = Fcons (subprefix_1->subprefix, entry);
+ }
+
+ tem = entry;
+ }
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ unblock_input ();
+ return unbind_to (count, tem);
+
+ finish:
+ unblock_input ();
+ return unbind_to (count, Qnil);
+}
+
+
+
+/* Toolkit dialog implementation. */
+
+/* Structure describing the EmacsDialog class. */
+
+struct android_emacs_dialog
+{
+ jclass class;
+ jmethodID create_dialog;
+ jmethodID add_button;
+ jmethodID display;
+};
+
+/* Identifiers associated with the EmacsDialog class. */
+static struct android_emacs_dialog dialog_class;
+
+static void
+android_init_emacs_dialog (void)
+{
+ jclass old;
+
+ dialog_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsDialog");
+ eassert (dialog_class.class);
+
+ old = dialog_class.class;
+ dialog_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ (jobject) old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!dialog_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ dialog_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ dialog_class.class, \
+ name, signature); \
+ eassert (dialog_class.c_name);
+
+#define FIND_METHOD_STATIC(c_name, name, signature) \
+ dialog_class.c_name \
+ = (*android_java_env)->GetStaticMethodID (android_java_env, \
+ dialog_class.class, \
+ name, signature); \
+
+ FIND_METHOD_STATIC (create_dialog, "createDialog", "(Ljava/lang/String;"
+ "Ljava/lang/String;I)Lorg/gnu/emacs/EmacsDialog;");
+ FIND_METHOD (add_button, "addButton", "(Ljava/lang/String;IZ)V");
+ FIND_METHOD (display, "display", "()Z");
+
+#undef FIND_METHOD
+#undef FIND_METHOD_STATIC
+}
+
+static Lisp_Object
+android_dialog_show (struct frame *f, Lisp_Object title,
+ Lisp_Object header, const char **error_name)
+{
+ specpdl_ref count;
+ jobject dialog, java_header, java_title, temp;
+ size_t i;
+ Lisp_Object item_name, enable, entry;
+ bool rc;
+ int id;
+ jmethodID method;
+ unsigned int serial;
+
+ /* Generate a unique ID for events from this dialog box. */
+ serial = ++current_menu_serial;
+
+ if (menu_items_n_panes > 1)
+ {
+ *error_name = "Multiple panes in dialog box";
+ return Qnil;
+ }
+
+ /* Do the initial setup. */
+ count = SPECPDL_INDEX ();
+ *error_name = NULL;
+
+ android_push_local_frame ();
+
+ /* Figure out what header to use. */
+ java_header = (!NILP (header)
+ ? android_build_jstring ("Information")
+ : android_build_jstring ("Question"));
+
+ /* And the title. */
+ java_title = android_build_string (title);
+
+ /* Now create the dialog. */
+ method = dialog_class.create_dialog;
+ dialog = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ dialog_class.class,
+ method, java_header,
+ java_title,
+ (jint) serial);
+ android_exception_check ();
+
+ /* Delete now unused local references. */
+ if (java_header)
+ ANDROID_DELETE_LOCAL_REF (java_header);
+ ANDROID_DELETE_LOCAL_REF (java_title);
+
+ /* Create the buttons. */
+ i = MENU_ITEMS_PANE_LENGTH;
+ while (i < menu_items_used)
+ {
+ item_name = AREF (menu_items, i + MENU_ITEMS_ITEM_NAME);
+ enable = AREF (menu_items, i + MENU_ITEMS_ITEM_ENABLE);
+
+ /* Verify that there is no submenu here. */
+
+ if (NILP (item_name))
+ {
+ *error_name = "Submenu in dialog items";
+ return unbind_to (count, Qnil);
+ }
+
+ /* Skip past boundaries between buttons on different sides. The
+ Android toolkit is too silly to understand this
+ distinction. */
+
+ if (EQ (item_name, Qquote))
+ ++i;
+ else
+ {
+ /* Make sure i is within bounds. */
+ if (i > TYPE_MAXIMUM (jint))
+ {
+ *error_name = "Dialog box too big";
+ return unbind_to (count, Qnil);
+ }
+
+ /* Add the button. */
+ temp = android_build_string (item_name);
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ dialog,
+ dialog_class.add_button,
+ temp, (jint) i,
+ (jboolean) NILP (enable));
+ android_exception_check ();
+ ANDROID_DELETE_LOCAL_REF (temp);
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ /* The dialog is now built. Run it. */
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ dialog,
+ dialog_class.display);
+ android_exception_check ();
+
+ if (!rc)
+ quit ();
+
+ /* Wait for the menu ID to arrive. */
+ android_process_events_for_menu (&id);
+
+ if (!id)
+ quit ();
+
+ /* Find the selected item, and its pane, to return
+ the proper value. */
+ i = 0;
+ while (i < menu_items_used)
+ {
+ if (EQ (AREF (menu_items, i), Qt))
+ i += MENU_ITEMS_PANE_LENGTH;
+ else if (EQ (AREF (menu_items, i), Qquote))
+ /* This is the boundary between left-side elts and right-side
+ elts. */
+ ++i;
+ else
+ {
+ entry = AREF (menu_items, i + MENU_ITEMS_ITEM_VALUE);
+
+ if (id == i)
+ return entry;
+
+ i += MENU_ITEMS_ITEM_LENGTH;
+ }
+ }
+
+ return Qnil;
+}
+
+Lisp_Object
+android_popup_dialog (struct frame *f, Lisp_Object header,
+ Lisp_Object contents)
+{
+ Lisp_Object title;
+ const char *error_name;
+ Lisp_Object selection;
+ specpdl_ref specpdl_count = SPECPDL_INDEX ();
+
+ check_window_system (f);
+
+ /* Decode the dialog items from what was specified. */
+ title = Fcar (contents);
+ CHECK_STRING (title);
+ record_unwind_protect_void (unuse_menu_items);
+
+ if (NILP (Fcar (Fcdr (contents))))
+ /* No buttons specified, add an "Ok" button so users can pop down
+ the dialog. */
+ contents = list2 (title, Fcons (build_string ("Ok"), Qt));
+
+ list_of_panes (list1 (contents));
+
+ /* Display them in a dialog box. */
+ block_input ();
+ selection = android_dialog_show (f, title, header, &error_name);
+ unblock_input ();
+
+ unbind_to (specpdl_count, Qnil);
+ discard_menu_items ();
+
+ if (error_name)
+ error ("%s", error_name);
+
+ return selection;
+}
+
+#else
+
+int
+popup_activated (void)
+{
+ return 0;
+}
+
+#endif
+
+DEFUN ("menu-or-popup-active-p", Fmenu_or_popup_active_p,
+ Smenu_or_popup_active_p, 0, 0, 0,
+ doc: /* SKIP: real doc in xfns.c. */)
+ (void)
+{
+ return (popup_activated ()) ? Qt : Qnil;
+}
+
+void
+init_androidmenu (void)
+{
+#ifndef ANDROID_STUBIFY
+ android_init_emacs_context_menu ();
+ android_init_emacs_dialog ();
+#endif
+}
+
+void
+syms_of_androidmenu (void)
+{
+ defsubr (&Smenu_or_popup_active_p);
+}
diff --git a/src/androidselect.c b/src/androidselect.c
new file mode 100644
index 00000000000..54c712ca93b
--- /dev/null
+++ b/src/androidselect.c
@@ -0,0 +1,499 @@
+/* Communication module for Android terminals.
+
+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 <assert.h>
+#include <minmax.h>
+#include <unistd.h>
+
+#include "lisp.h"
+#include "blockinput.h"
+#include "coding.h"
+#include "android.h"
+#include "androidterm.h"
+
+/* Selection support on Android is confined to copying and pasting of
+ plain text and MIME data from the clipboard. There is no primary
+ selection.
+
+ While newer versions of Android are supposed to have the necessary
+ interfaces for transferring other kinds of selection data, doing so
+ is too complicated, and involves registering ``content providers''
+ and all kinds of other stuff; for this reason, Emacs does not
+ support setting the clipboard contents to anything other than plain
+ text. */
+
+
+
+/* Structure describing the EmacsClipboard class. */
+
+struct android_emacs_clipboard
+{
+ jclass class;
+ jmethodID set_clipboard;
+ jmethodID owns_clipboard;
+ jmethodID clipboard_exists;
+ jmethodID get_clipboard;
+ jmethodID make_clipboard;
+ jmethodID get_clipboard_targets;
+ jmethodID get_clipboard_data;
+};
+
+/* Methods associated with the EmacsClipboard class. */
+static struct android_emacs_clipboard clipboard_class;
+
+/* Reference to the EmacsClipboard object. */
+static jobject clipboard;
+
+
+
+static void
+android_init_emacs_clipboard (void)
+{
+ jclass old;
+
+ clipboard_class.class
+ = (*android_java_env)->FindClass (android_java_env,
+ "org/gnu/emacs/EmacsClipboard");
+ eassert (clipboard_class.class);
+
+ old = clipboard_class.class;
+ clipboard_class.class
+ = (jclass) (*android_java_env)->NewGlobalRef (android_java_env,
+ old);
+ ANDROID_DELETE_LOCAL_REF (old);
+
+ if (!clipboard_class.class)
+ emacs_abort ();
+
+#define FIND_METHOD(c_name, name, signature) \
+ clipboard_class.c_name \
+ = (*android_java_env)->GetMethodID (android_java_env, \
+ clipboard_class.class, \
+ name, signature); \
+ assert (clipboard_class.c_name);
+
+ FIND_METHOD (set_clipboard, "setClipboard", "([B)V");
+ FIND_METHOD (owns_clipboard, "ownsClipboard", "()I");
+ FIND_METHOD (clipboard_exists, "clipboardExists", "()Z");
+ FIND_METHOD (get_clipboard, "getClipboard", "()[B");
+ FIND_METHOD (get_clipboard_targets, "getClipboardTargets",
+ "()[[B");
+ FIND_METHOD (get_clipboard_data, "getClipboardData",
+ "([B)[J");
+
+ clipboard_class.make_clipboard
+ = (*android_java_env)->GetStaticMethodID (android_java_env,
+ clipboard_class.class,
+ "makeClipboard",
+ "()Lorg/gnu/emacs/"
+ "EmacsClipboard;");
+ assert (clipboard_class.make_clipboard);
+
+#undef FIND_METHOD
+}
+
+
+
+
+DEFUN ("android-clipboard-owner-p", Fandroid_clipboard_owner_p,
+ Sandroid_clipboard_owner_p, 0, 0, 0,
+ doc: /* Return whether or not Emacs owns the clipboard.
+Alternatively, return the symbol `lambda' if that could not be
+determined. */)
+ (void)
+{
+ jint rc;
+
+ if (!android_init_gui)
+ error ("Accessing clipboard without display connection");
+
+ block_input ();
+ rc = (*android_java_env)->CallIntMethod (android_java_env,
+ clipboard,
+ clipboard_class.owns_clipboard);
+ android_exception_check ();
+ unblock_input ();
+
+ /* If rc is 0 or 1, then Emacs knows whether or not it owns the
+ clipboard. If rc is -1, then Emacs does not. */
+
+ if (rc < 0)
+ return Qlambda;
+
+ return rc ? Qt : Qnil;
+}
+
+DEFUN ("android-set-clipboard", Fandroid_set_clipboard,
+ Sandroid_set_clipboard, 1, 1, 0,
+ doc: /* Set the clipboard text to STRING. */)
+ (Lisp_Object string)
+{
+ jarray bytes;
+
+ if (!android_init_gui)
+ error ("Accessing clipboard without display connection");
+
+ CHECK_STRING (string);
+ string = ENCODE_UTF_8 (string);
+
+ bytes = (*android_java_env)->NewByteArray (android_java_env,
+ SBYTES (string));
+ android_exception_check ();
+
+ (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
+ 0, SBYTES (string),
+ (jbyte *) SDATA (string));
+ (*android_java_env)->CallVoidMethod (android_java_env,
+ clipboard,
+ clipboard_class.set_clipboard,
+ bytes);
+ android_exception_check_1 (bytes);
+
+ ANDROID_DELETE_LOCAL_REF (bytes);
+ return Qnil;
+}
+
+DEFUN ("android-get-clipboard", Fandroid_get_clipboard,
+ Sandroid_get_clipboard, 0, 0, 0,
+ doc: /* Return the current contents of the clipboard.
+Value is a multibyte string containing decoded clipboard
+text.
+Alternatively, return nil if the clipboard is empty. */)
+ (void)
+{
+ Lisp_Object string;
+ jarray bytes;
+ jmethodID method;
+ size_t length;
+ jbyte *data;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ method = clipboard_class.get_clipboard;
+ bytes
+ = (*android_java_env)->CallObjectMethod (android_java_env,
+ clipboard,
+ method);
+ android_exception_check ();
+
+ length = (*android_java_env)->GetArrayLength (android_java_env,
+ bytes);
+ data = (*android_java_env)->GetByteArrayElements (android_java_env,
+ bytes, NULL);
+ android_exception_check_nonnull (data, bytes);
+
+ string = make_unibyte_string ((char *) data, length);
+
+ (*android_java_env)->ReleaseByteArrayElements (android_java_env,
+ bytes, data,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (bytes);
+
+ /* Now decode the resulting string. */
+ return code_convert_string_norecord (string, Qutf_8, Qnil);
+}
+
+DEFUN ("android-clipboard-exists-p", Fandroid_clipboard_exists_p,
+ Sandroid_clipboard_exists_p, 0, 0, 0,
+ doc: /* Return whether or not clipboard contents exist. */)
+ (void)
+{
+ jboolean rc;
+ jmethodID method;
+
+ if (!android_init_gui)
+ error ("No Android display connection");
+
+ method = clipboard_class.clipboard_exists;
+ rc = (*android_java_env)->CallBooleanMethod (android_java_env,
+ clipboard,
+ method);
+ android_exception_check ();
+
+ return rc ? Qt : Qnil;
+}
+
+DEFUN ("android-browse-url", Fandroid_browse_url,
+ Sandroid_browse_url, 1, 1, 0,
+ doc: /* Start the system web browser.
+Then, point the web browser to URL, which should be a URL-encoded
+URL with a scheme specified. Signal an error upon failure. */)
+ (Lisp_Object url)
+{
+ Lisp_Object value;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ CHECK_STRING (url);
+ value = android_browse_url (url);
+
+ /* Signal an error upon failure. */
+ if (!NILP (value))
+ signal_error ("Error browsing URL", value);
+
+ return Qnil;
+}
+
+
+
+/* MIME clipboard support. This provides support for reading MIME
+ data (but not text) from the clipboard. */
+
+DEFUN ("android-get-clipboard-targets", Fandroid_get_clipboard_targets,
+ Sandroid_get_clipboard_targets, 0, 0, 0,
+ doc: /* Return a list of data types in the clipboard.
+Value is a list of MIME types as strings, each defining a single extra
+data type available from the clipboard. */)
+ (void)
+{
+ jarray bytes_array;
+ jbyteArray bytes;
+ jmethodID method;
+ size_t length, length1, i;
+ jbyte *data;
+ Lisp_Object targets, tem;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ targets = Qnil;
+ block_input ();
+ method = clipboard_class.get_clipboard_targets;
+ bytes_array = (*android_java_env)->CallObjectMethod (android_java_env,
+ clipboard, method);
+ android_exception_check ();
+
+ if (!bytes_array)
+ goto fail;
+
+ length = (*android_java_env)->GetArrayLength (android_java_env,
+ bytes_array);
+ for (i = 0; i < length; ++i)
+ {
+ /* Retireve the MIME type. */
+ bytes
+ = (*android_java_env)->GetObjectArrayElement (android_java_env,
+ bytes_array, i);
+ android_exception_check_nonnull (bytes, bytes_array);
+
+ /* Cons it onto the list of targets. */
+ length1 = (*android_java_env)->GetArrayLength (android_java_env,
+ bytes);
+ data = (*android_java_env)->GetByteArrayElements (android_java_env,
+ bytes, NULL);
+ android_exception_check_nonnull_1 (data, bytes, bytes_array);
+
+ /* Decode the string. */
+ tem = make_unibyte_string ((char *) data, length1);
+ tem = code_convert_string_norecord (tem, Qutf_8, Qnil);
+ targets = Fcons (tem, targets);
+
+ /* Delete the retrieved data. */
+ (*android_java_env)->ReleaseByteArrayElements (android_java_env,
+ bytes, data,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (bytes);
+ }
+ unblock_input ();
+
+ ANDROID_DELETE_LOCAL_REF (bytes_array);
+ return Fnreverse (targets);
+
+ fail:
+ unblock_input ();
+ return Qnil;
+}
+
+/* Free the memory inside PTR, a pointer to a char pointer. */
+
+static void
+android_xfree_inside (void *ptr)
+{
+ xfree (*(char **) ptr);
+}
+
+DEFUN ("android-get-clipboard-data", Fandroid_get_clipboard_data,
+ Sandroid_get_clipboard_data, 1, 1, 0,
+ doc: /* Return the clipboard data of the given MIME TYPE.
+Value is a unibyte string containing the entire contents of the
+clipboard, after its owner has converted the data to the given
+MIME type. Value is nil if the conversion fails, or if the data
+is not present.
+
+Value is also nil if the clipboard data consists of a single URL which
+does not have any corresponding data. In that case, use
+`android-get-clipboard' instead. */)
+ (Lisp_Object type)
+{
+ jlongArray array;
+ jbyteArray bytes;
+ jmethodID method;
+ int fd;
+ ptrdiff_t rc;
+ jlong offset, length, *longs;
+ specpdl_ref ref;
+ char *buffer, *start;
+
+ if (!android_init_gui)
+ error ("No Android display connection!");
+
+ /* Encode the string as UTF-8. */
+ CHECK_STRING (type);
+ type = ENCODE_UTF_8 (type);
+
+ /* Then give it to the selection code. */
+ block_input ();
+ bytes = (*android_java_env)->NewByteArray (android_java_env,
+ SBYTES (type));
+ (*android_java_env)->SetByteArrayRegion (android_java_env, bytes,
+ 0, SBYTES (type),
+ (jbyte *) SDATA (type));
+ android_exception_check ();
+
+ method = clipboard_class.get_clipboard_data;
+ array = (*android_java_env)->CallObjectMethod (android_java_env,
+ clipboard, method,
+ bytes);
+ android_exception_check_1 (bytes);
+ ANDROID_DELETE_LOCAL_REF (bytes);
+
+ if (!array)
+ goto fail;
+
+ longs = (*android_java_env)->GetLongArrayElements (android_java_env,
+ array, NULL);
+ android_exception_check_nonnull (longs, array);
+
+ /* longs[0] is the file descriptor.
+ longs[1] is an offset to apply to the file.
+ longs[2] is either -1, or the number of bytes to read from the
+ file. */
+ fd = longs[0];
+ offset = longs[1];
+ length = longs[2];
+
+ (*android_java_env)->ReleaseLongArrayElements (android_java_env,
+ array, longs,
+ JNI_ABORT);
+ ANDROID_DELETE_LOCAL_REF (array);
+ unblock_input ();
+
+ /* Now begin reading from longs[0]. */
+ ref = SPECPDL_INDEX ();
+ record_unwind_protect_int (close_file_unwind, fd);
+
+ if (length != -1)
+ {
+ buffer = xmalloc (MIN (length, PTRDIFF_MAX));
+ record_unwind_protect_ptr (xfree, buffer);
+
+ rc = emacs_read_quit (fd, buffer,
+ MIN (length, PTRDIFF_MAX));
+
+ /* Return nil upon an IO problem. */
+ if (rc < 0)
+ return unbind_to (ref, Qnil);
+
+ /* Return the data as a unibyte string. */
+ return unbind_to (ref, make_unibyte_string (buffer, rc));
+ }
+
+ /* Otherwise, read BUFSIZ bytes at a time. */
+ buffer = xmalloc (BUFSIZ);
+ length = 0;
+ start = buffer;
+
+ record_unwind_protect_ptr (android_xfree_inside, &buffer);
+
+ /* Seek to the start of the data. */
+
+ if (offset)
+ {
+ if (lseek (fd, offset, SEEK_SET) < 0)
+ return unbind_to (ref, Qnil);
+ }
+
+ while (true)
+ {
+ rc = emacs_read_quit (fd, start, BUFSIZ);
+
+ if (!INT_ADD_OK (rc, length, &length)
+ || PTRDIFF_MAX - length < BUFSIZ)
+ memory_full (PTRDIFF_MAX);
+
+ if (rc < 0)
+ return unbind_to (ref, Qnil);
+
+ if (rc < BUFSIZ)
+ break;
+
+ buffer = xrealloc (buffer, length + BUFSIZ);
+ start = buffer + length;
+ }
+
+ return unbind_to (ref, make_unibyte_string (buffer, rc));
+
+ fail:
+ unblock_input ();
+ return Qnil;
+}
+
+
+
+void
+init_androidselect (void)
+{
+ jobject tem;
+ jmethodID make_clipboard;
+
+ if (!android_init_gui)
+ return;
+
+ android_init_emacs_clipboard ();
+
+ make_clipboard = clipboard_class.make_clipboard;
+ tem
+ = (*android_java_env)->CallStaticObjectMethod (android_java_env,
+ clipboard_class.class,
+ make_clipboard);
+ if (!tem)
+ emacs_abort ();
+
+ clipboard = (*android_java_env)->NewGlobalRef (android_java_env, tem);
+
+ if (!clipboard)
+ emacs_abort ();
+
+ ANDROID_DELETE_LOCAL_REF (tem);
+}
+
+void
+syms_of_androidselect (void)
+{
+ defsubr (&Sandroid_clipboard_owner_p);
+ defsubr (&Sandroid_set_clipboard);
+ defsubr (&Sandroid_get_clipboard);
+ defsubr (&Sandroid_clipboard_exists_p);
+ defsubr (&Sandroid_browse_url);
+ defsubr (&Sandroid_get_clipboard_targets);
+ defsubr (&Sandroid_get_clipboard_data);
+}
diff --git a/src/androidterm.c b/src/androidterm.c
new file mode 100644
index 00000000000..6f7c06875ca
--- /dev/null
+++ b/src/androidterm.c
@@ -0,0 +1,6073 @@
+/* Communication module for Android terminals.
+
+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 <math.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <semaphore.h>
+
+#include "lisp.h"
+#include "androidterm.h"
+#include "keyboard.h"
+#include "blockinput.h"
+#include "android.h"
+#include "buffer.h"
+#include "window.h"
+#include "textconv.h"
+#include "coding.h"
+#include "pdumper.h"
+
+/* This is a chain of structures for all the X displays currently in
+ use. */
+
+struct android_display_info *x_display_list;
+
+
+
+/* Android terminal interface functions. */
+
+#ifndef ANDROID_STUBIFY
+
+#include <android/log.h>
+
+/* Non-zero means that a HELP_EVENT has been generated since Emacs
+ start. */
+
+static bool any_help_event_p;
+
+/* Counters for tallying up scroll wheel events if
+ mwheel_coalesce_scroll_events is true. */
+
+static double wheel_event_x, wheel_event_y;
+
+enum
+ {
+ ANDROID_EVENT_NORMAL,
+ ANDROID_EVENT_GOTO_OUT,
+ ANDROID_EVENT_DROP,
+ };
+
+/* Find the frame whose window has the identifier WDESC.
+
+ This is like x_window_to_frame in xterm.c, except that DPYINFO may
+ be NULL, as there is only at most one Android display, and is only
+ specified in order to stay consistent with X. */
+
+static struct frame *
+android_window_to_frame (struct android_display_info *dpyinfo,
+ android_window wdesc)
+{
+ Lisp_Object tail, frame;
+ struct frame *f;
+
+ if (wdesc == ANDROID_NONE)
+ return NULL;
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+
+ if (!FRAME_ANDROID_P (f))
+ continue;
+
+ if (FRAME_ANDROID_WINDOW (f) == wdesc)
+ return f;
+ }
+
+ return NULL;
+}
+
+static void
+android_clear_frame (struct frame *f)
+{
+ /* Clearing the frame will erase any cursor, so mark them all as no
+ longer visible. */
+ mark_window_cursors_off (XWINDOW (FRAME_ROOT_WINDOW (f)));
+ android_clear_window (FRAME_ANDROID_DRAWABLE (f));
+}
+
+static void
+android_show_hourglass (struct frame *f)
+{
+ struct android_output *x;
+
+ /* This isn't implemented like X because a window brings alongside
+ too many unneeded resources. */
+
+ x = FRAME_ANDROID_OUTPUT (f);
+
+ /* If the hourglass window is mapped inside a popup menu, input
+ could be lost if the menu is popped down and the grab is
+ relinquished, but the hourglass window is still up. Just
+ avoid displaying the hourglass at all while popups are
+ active. */
+
+ if (popup_activated ())
+ return;
+
+ x->hourglass = true;
+
+ if (!f->pointer_invisible)
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ x->hourglass_cursor);
+}
+
+static void
+android_hide_hourglass (struct frame *f)
+{
+ struct android_output *x;
+
+ x = FRAME_ANDROID_OUTPUT (f);
+ x->hourglass = false;
+
+ if (!f->pointer_invisible)
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ x->current_cursor);
+}
+
+static void
+android_flash (struct frame *f)
+{
+ struct android_gc *gc;
+ struct android_gc_values values;
+ int rc;
+ fd_set fds;
+
+ block_input ();
+
+ values.function = ANDROID_GC_XOR;
+ values.foreground = (FRAME_FOREGROUND_PIXEL (f)
+ ^ FRAME_BACKGROUND_PIXEL (f));
+
+ gc = android_create_gc ((ANDROID_GC_FUNCTION
+ | ANDROID_GC_FOREGROUND),
+ &values);
+
+ /* Get the height not including a menu bar widget. */
+ int height = FRAME_PIXEL_HEIGHT (f);
+ /* Height of each line to flash. */
+ int flash_height = FRAME_LINE_HEIGHT (f);
+ /* These will be the left and right margins of the rectangles. */
+ int flash_left = FRAME_INTERNAL_BORDER_WIDTH (f);
+ int flash_right = FRAME_PIXEL_WIDTH (f) - FRAME_INTERNAL_BORDER_WIDTH (f);
+ int width = flash_right - flash_left;
+
+ /* If window is tall, flash top and bottom line. */
+ if (height > 3 * FRAME_LINE_HEIGHT (f))
+ {
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left,
+ (FRAME_INTERNAL_BORDER_WIDTH (f)
+ + FRAME_TOP_MARGIN_HEIGHT (f)),
+ width, flash_height);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left,
+ (height - flash_height
+ - FRAME_INTERNAL_BORDER_WIDTH (f)),
+ width, flash_height);
+
+ }
+ else
+ /* If it is short, flash it all. */
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+ width, (height - 2
+ * FRAME_INTERNAL_BORDER_WIDTH (f)));
+
+ flush_frame (f);
+
+ struct timespec delay = make_timespec (0, 150 * 1000 * 1000);
+ struct timespec wakeup = timespec_add (current_timespec (), delay);
+
+ /* Keep waiting until past the time wakeup or any input gets
+ available. */
+ while (! detect_input_pending ())
+ {
+ struct timespec current = current_timespec ();
+ struct timespec timeout;
+
+ /* Break if result would not be positive. */
+ if (timespec_cmp (wakeup, current) <= 0)
+ break;
+
+ /* How long `select' should wait. */
+ timeout = make_timespec (0, 10 * 1000 * 1000);
+
+ /* Wait for some input to become available on the X
+ connection. */
+ FD_ZERO (&fds);
+
+ /* Try to wait that long--but we might wake up sooner. */
+ rc = pselect (0, &fds, NULL, NULL, &timeout, NULL);
+
+ /* Some input is available, exit the visible bell. */
+ if (rc >= 0)
+ break;
+ }
+
+ /* If window is tall, flash top and bottom line. */
+ if (height > 3 * FRAME_LINE_HEIGHT (f))
+ {
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left,
+ (FRAME_INTERNAL_BORDER_WIDTH (f)
+ + FRAME_TOP_MARGIN_HEIGHT (f)),
+ width, flash_height);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left,
+ (height - flash_height
+ - FRAME_INTERNAL_BORDER_WIDTH (f)),
+ width, flash_height);
+ }
+ else
+ /* If it is short, flash it all. */
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ flash_left, FRAME_INTERNAL_BORDER_WIDTH (f),
+ width, (height - 2
+ * FRAME_INTERNAL_BORDER_WIDTH (f)));
+
+ android_free_gc (gc);
+ flush_frame (f);
+
+ unblock_input ();
+}
+
+static void
+android_ring_bell (struct frame *f)
+{
+ if (visible_bell)
+ android_flash (f);
+ else
+ {
+ block_input ();
+ android_bell ();
+ unblock_input ();
+ }
+}
+
+static android_cursor
+make_invisible_cursor (struct android_display_info *dpyinfo)
+{
+ return android_create_font_cursor (ANDROID_XC_NULL);
+}
+
+static void
+android_toggle_visible_pointer (struct frame *f, bool invisible)
+{
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+
+ if (!dpyinfo->invisible_cursor)
+ dpyinfo->invisible_cursor = make_invisible_cursor (dpyinfo);
+
+ if (invisible)
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ dpyinfo->invisible_cursor);
+ else
+ android_define_cursor (FRAME_ANDROID_WINDOW (f),
+ (FRAME_ANDROID_OUTPUT (f)->hourglass
+ ? f->output_data.android->hourglass_cursor
+ : f->output_data.android->current_cursor));
+
+ f->pointer_invisible = invisible;
+}
+
+static void
+android_toggle_invisible_pointer (struct frame *f, bool invisible)
+{
+ block_input ();
+ android_toggle_visible_pointer (f, invisible);
+ unblock_input ();
+}
+
+/* Start an update of frame F. This function is installed as a hook
+ for update_begin, i.e. it is called when update_begin is called.
+ This function is called prior to calls to gui_update_window_begin
+ for each window being updated. Currently, there is nothing to do
+ here because all interesting stuff is done on a window basis. */
+
+static void
+android_update_begin (struct frame *f)
+{
+ /* The frame is no longer complete, as it is in the midst of an
+ update. */
+ FRAME_ANDROID_COMPLETE_P (f) = false;
+}
+
+/* End update of frame F. This function is installed as a hook in
+ update_end. */
+
+static void
+android_update_end (struct frame *f)
+{
+ /* Mouse highlight may be displayed again. */
+ MOUSE_HL_INFO (f)->mouse_face_defer = false;
+}
+
+static void
+show_back_buffer (struct frame *f)
+{
+ struct android_swap_info swap_info;
+
+ memset (&swap_info, 0, sizeof (swap_info));
+ swap_info.swap_window = FRAME_ANDROID_WINDOW (f);
+ swap_info.swap_action = ANDROID_COPIED;
+ android_swap_buffers (&swap_info, 1);
+
+ /* Now the back buffer no longer needs to be flipped. */
+ FRAME_ANDROID_NEED_BUFFER_FLIP (f) = false;
+}
+
+/* Flip back buffers on F if it has undrawn content. */
+
+static void
+android_flush_dirty_back_buffer_on (struct frame *f)
+{
+ if (FRAME_GARBAGED_P (f)
+ || buffer_flipping_blocked_p ()
+ /* If the frame is not already up to date, do not flush buffers
+ on input, as that will result in flicker. */
+ || !FRAME_ANDROID_COMPLETE_P (f)
+ || !FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+ return;
+
+ show_back_buffer (f);
+}
+
+/* Convert between the modifier bits Android uses and the modifier
+ bits Emacs uses. */
+
+static int
+android_android_to_emacs_modifiers (struct android_display_info *dpyinfo,
+ int state)
+{
+ return ((state & ANDROID_CONTROL_MASK) ? ctrl_modifier : 0
+ | (state & ANDROID_SHIFT_MASK) ? shift_modifier : 0
+ | (state & ANDROID_ALT_MASK) ? meta_modifier : 0
+ | (state & ANDROID_SUPER_MASK) ? super_modifier : 0);
+}
+
+static int
+android_emacs_to_android_modifiers (struct android_display_info *dpyinfo,
+ intmax_t state)
+{
+ return ((state & ctrl_modifier) ? ANDROID_CONTROL_MASK : 0
+ | (state & shift_modifier) ? ANDROID_SHIFT_MASK : 0
+ | (state & meta_modifier) ? ANDROID_ALT_MASK : 0
+ | (state & super_modifier) ? ANDROID_SUPER_MASK : 0);
+}
+
+static void android_frame_rehighlight (struct android_display_info *);
+
+static void
+android_lower_frame (struct frame *f)
+{
+ android_lower_window (FRAME_ANDROID_WINDOW (f));
+}
+
+static void
+android_raise_frame (struct frame *f)
+{
+ android_raise_window (FRAME_ANDROID_WINDOW (f));
+}
+
+static void
+android_new_focus_frame (struct android_display_info *dpyinfo,
+ struct frame *frame)
+{
+ struct frame *old_focus;
+
+ old_focus = dpyinfo->focus_frame;
+
+ if (frame != dpyinfo->focus_frame)
+ {
+ /* Set this before calling other routines, so that they see
+ the correct value of x_focus_frame. */
+ dpyinfo->focus_frame = frame;
+
+ if (old_focus && old_focus->auto_lower)
+ android_lower_frame (old_focus);
+
+ if (dpyinfo->focus_frame && dpyinfo->focus_frame->auto_raise)
+ dpyinfo->pending_autoraise_frame = dpyinfo->focus_frame;
+ else
+ dpyinfo->pending_autoraise_frame = NULL;
+ }
+
+ android_frame_rehighlight (dpyinfo);
+}
+
+static void
+android_focus_changed (int type, int state,
+ struct android_display_info *dpyinfo,
+ struct frame *frame, struct input_event *bufp)
+{
+ if (type == ANDROID_FOCUS_IN)
+ {
+ if (dpyinfo->x_focus_event_frame != frame)
+ {
+ android_new_focus_frame (dpyinfo, frame);
+ dpyinfo->x_focus_event_frame = frame;
+ bufp->kind = FOCUS_IN_EVENT;
+ XSETFRAME (bufp->frame_or_window, frame);
+ }
+
+ frame->output_data.android->focus_state |= state;
+ }
+ else if (type == ANDROID_FOCUS_OUT)
+ {
+ frame->output_data.android->focus_state &= ~state;
+
+ if (dpyinfo->x_focus_event_frame == frame)
+ {
+ dpyinfo->x_focus_event_frame = 0;
+ android_new_focus_frame (dpyinfo, 0);
+
+ bufp->kind = FOCUS_OUT_EVENT;
+ XSETFRAME (bufp->frame_or_window, frame);
+ }
+
+ if (frame->pointer_invisible)
+ android_toggle_invisible_pointer (frame, false);
+ }
+}
+
+static void
+android_detect_focus_change (struct android_display_info *dpyinfo,
+ struct frame *frame,
+ union android_event *event,
+ struct input_event *bufp)
+{
+ if (!frame)
+ return;
+
+ switch (event->type)
+ {
+ case ANDROID_FOCUS_IN:
+ case ANDROID_FOCUS_OUT:
+ android_focus_changed (event->type, FOCUS_EXPLICIT,
+ dpyinfo, frame, bufp);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static bool
+android_note_mouse_movement (struct frame *frame,
+ struct android_motion_event *event)
+{
+ struct android_display_info *dpyinfo;
+ Emacs_Rectangle *r;
+
+ if (!FRAME_ANDROID_OUTPUT (frame))
+ return false;
+
+ dpyinfo = FRAME_DISPLAY_INFO (frame);
+ dpyinfo->last_mouse_motion_frame = frame;
+ dpyinfo->last_mouse_motion_x = event->x;
+ dpyinfo->last_mouse_motion_y = event->y;
+ dpyinfo->last_mouse_movement_time = event->time;
+
+ /* Has the mouse moved off the glyph it was on at the last sighting? */
+ r = &dpyinfo->last_mouse_glyph;
+ if (frame != dpyinfo->last_mouse_glyph_frame
+ || event->x < r->x || event->x >= r->x + r->width
+ || event->y < r->y || event->y >= r->y + r->height)
+ {
+ frame->mouse_moved = true;
+ note_mouse_highlight (frame, event->x, event->y);
+ /* Remember which glyph we're now on. */
+ remember_mouse_glyph (frame, event->x, event->y, r);
+ dpyinfo->last_mouse_glyph_frame = frame;
+ return true;
+ }
+
+ return false;
+}
+
+static struct frame *
+mouse_or_wdesc_frame (struct android_display_info *dpyinfo, int wdesc)
+{
+ struct frame *lm_f = (gui_mouse_grabbed (dpyinfo)
+ ? dpyinfo->last_mouse_frame
+ : NULL);
+
+ if (lm_f && !EQ (track_mouse, Qdropping)
+ && !EQ (track_mouse, Qdrag_source))
+ return lm_f;
+ else
+ {
+ struct frame *w_f = android_window_to_frame (dpyinfo, wdesc);
+
+ /* Do not return a tooltip frame. */
+ if (!w_f || FRAME_TOOLTIP_P (w_f))
+ return EQ (track_mouse, Qdropping) ? lm_f : NULL;
+ else
+ /* When dropping it would be probably nice to raise w_f
+ here. */
+ return w_f;
+ }
+}
+
+static Lisp_Object
+android_construct_mouse_click (struct input_event *result,
+ struct android_button_event *event,
+ struct frame *f)
+{
+ struct android_display_info *dpyinfo;
+ int x, y;
+
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+ x = event->x;
+ y = event->y;
+
+ /* Make the event type NO_EVENT; we'll change that when we decide
+ otherwise. */
+ result->kind = MOUSE_CLICK_EVENT;
+ result->code = event->button - 1;
+ result->timestamp = event->time;
+ result->modifiers = (android_android_to_emacs_modifiers (dpyinfo,
+ event->state)
+ | (event->type == ANDROID_BUTTON_RELEASE
+ ? up_modifier : down_modifier));
+
+ XSETINT (result->x, x);
+ XSETINT (result->y, y);
+ XSETFRAME (result->frame_or_window, f);
+ result->arg = Qnil;
+ return Qnil;
+}
+
+/* Generate a TOUCHSCREEN_UPDATE_EVENT for all pressed tools in FRAME.
+ Return the event in IE. Do not set IE->timestamp, as that is left
+ to the caller. */
+
+static void
+android_update_tools (struct frame *f, struct input_event *ie)
+{
+ struct android_touch_point *touchpoint;
+
+ ie->kind = TOUCHSCREEN_UPDATE_EVENT;
+ XSETFRAME (ie->frame_or_window, f);
+ ie->arg = Qnil;
+
+ /* Build the list of active touches. */
+ for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
+ touchpoint; touchpoint = touchpoint->next)
+ {
+ /* Skip touch points which originated on the tool bar. */
+
+ if (touchpoint->tool_bar_p)
+ continue;
+
+ ie->arg = Fcons (list3i (touchpoint->x,
+ touchpoint->y,
+ touchpoint->tool_id),
+ ie->arg);
+ }
+}
+
+/* Find and return an existing tool pressed against FRAME, identified
+ by POINTER_ID. Return NULL if no tool by that ID was found. */
+
+static struct android_touch_point *
+android_find_tool (struct frame *f, int pointer_id)
+{
+ struct android_touch_point *touchpoint;
+
+ for (touchpoint = FRAME_OUTPUT_DATA (f)->touch_points;
+ touchpoint; touchpoint = touchpoint->next)
+ {
+ if (touchpoint->tool_id == pointer_id)
+ return touchpoint;
+ }
+
+ return NULL;
+}
+
+/* Decode STRING, an array of N little endian UTF-16 characters, into
+ a Lisp string. Return Qnil if the string is too large, and the
+ encoded string otherwise. */
+
+static Lisp_Object
+android_decode_utf16 (unsigned short *utf16, size_t n)
+{
+ struct coding_system coding;
+ ptrdiff_t size;
+
+ if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size))
+ return Qnil;
+
+ /* Set up the coding system. Decoding a UTF-16 string (with no BOM)
+ should not signal. */
+
+ memset (&coding, 0, sizeof coding);
+
+ setup_coding_system (Qutf_16le, &coding);
+ coding.source = (const unsigned char *) utf16;
+ decode_coding_object (&coding, Qnil, 0, 0, size,
+ size, Qt);
+
+ return coding.dst_object;
+}
+
+/* Handle a cursor update request for F from the input method.
+ MODE specifies whether or not an update should be sent immediately,
+ and whether or not they are needed in the future.
+
+ If MODE & ANDROID_CURSOR_UPDATE_IMMEDIATE, report the position of
+ F's old selected window's phys cursor now.
+
+ If MODE & ANDROID_CURSOR_UPDATE_MONITOR, set
+ `need_cursor_updates'. */
+
+static void
+android_request_cursor_updates (struct frame *f, int mode)
+{
+ struct window *w;
+
+ if (mode & ANDROID_CURSOR_UPDATE_IMMEDIATE
+ && WINDOWP (WINDOW_LIVE_P (f->old_selected_window)
+ ? f->old_selected_window
+ : f->selected_window))
+ {
+ /* Prefer the old selected window, as its selection is what was
+ reported to the IME previously. */
+
+ w = XWINDOW (WINDOW_LIVE_P (f->old_selected_window)
+ ? f->old_selected_window
+ : f->selected_window);
+ android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+ }
+
+ /* Now say whether or not updates are needed in the future. */
+ FRAME_OUTPUT_DATA (f)->need_cursor_updates
+ = (mode & ANDROID_CURSOR_UPDATE_MONITOR);
+}
+
+/* Handle a single input method event EVENT, delivered to the frame
+ F.
+
+ Perform the text conversion action specified inside. */
+
+static void
+android_handle_ime_event (union android_event *event, struct frame *f)
+{
+ Lisp_Object text UNINIT;
+
+ /* First, decode the text if necessary. */
+
+ switch (event->ime.operation)
+ {
+ case ANDROID_IME_COMMIT_TEXT:
+ case ANDROID_IME_FINISH_COMPOSING_TEXT:
+ case ANDROID_IME_SET_COMPOSING_TEXT:
+ text = android_decode_utf16 (event->ime.text,
+ event->ime.length);
+ xfree (event->ime.text);
+ break;
+
+ default:
+ break;
+ }
+
+ /* Finally, perform the appropriate conversion action. */
+
+ switch (event->ime.operation)
+ {
+ case ANDROID_IME_COMMIT_TEXT:
+ commit_text (f, text, event->ime.position,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_DELETE_SURROUNDING_TEXT:
+ delete_surrounding_text (f, event->ime.start,
+ event->ime.end,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_FINISH_COMPOSING_TEXT:
+ finish_composing_text (f, event->ime.counter);
+ break;
+
+ case ANDROID_IME_SET_COMPOSING_TEXT:
+ set_composing_text (f, text, event->ime.position,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_SET_COMPOSING_REGION:
+ set_composing_region (f, event->ime.start,
+ event->ime.end,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_SET_POINT:
+ textconv_set_point_and_mark (f, event->ime.start,
+ event->ime.end,
+ event->ime.counter);
+ break;
+
+ case ANDROID_IME_START_BATCH_EDIT:
+ start_batch_edit (f, event->ime.counter);
+ break;
+
+ case ANDROID_IME_END_BATCH_EDIT:
+ end_batch_edit (f, event->ime.counter);
+ break;
+
+ case ANDROID_IME_REQUEST_SELECTION_UPDATE:
+ request_point_update (f, event->ime.counter);
+ break;
+
+ case ANDROID_IME_REQUEST_CURSOR_UPDATES:
+ android_request_cursor_updates (f, event->ime.length);
+ break;
+ }
+}
+
+static int
+handle_one_android_event (struct android_display_info *dpyinfo,
+ union android_event *event, int *finish,
+ struct input_event *hold_quit)
+{
+ union android_event configureEvent;
+ struct frame *f, *any, *mouse_frame;
+ Mouse_HLInfo *hlinfo;
+ union buffered_input_event inev;
+ int modifiers, count, do_help;
+ struct android_touch_point *touchpoint, **last;
+ Lisp_Object window;
+ int scroll_height;
+ double scroll_unit;
+ int keysym;
+ ptrdiff_t nchars, i;
+ struct window *w;
+
+ /* It is okay for this to not resemble handle_one_xevent so much.
+ Differences in event handling code are much less nasty than
+ stuble differences in the graphics code. */
+
+ do_help = count = 0;
+ hlinfo = &dpyinfo->mouse_highlight;
+ *finish = ANDROID_EVENT_NORMAL;
+ any = android_window_to_frame (dpyinfo, event->xany.window);
+ nchars = 0;
+
+ if (any && any->wait_event_type == event->type)
+ any->wait_event_type = 0; /* Indicates we got it. */
+
+ EVENT_INIT (inev.ie);
+
+ switch (event->type)
+ {
+ case ANDROID_CONFIGURE_NOTIFY:
+ configureEvent = *event;
+
+ f = android_window_to_frame (dpyinfo,
+ configureEvent.xconfigure.window);
+
+ if (!f)
+ goto OTHER;
+
+ if (FRAME_TOOLTIP_P (f))
+ {
+ if (FRAME_PIXEL_HEIGHT (f) != configureEvent.xconfigure.height
+ || FRAME_PIXEL_WIDTH (f) != configureEvent.xconfigure.width)
+ SET_FRAME_GARBAGED (f);
+
+ FRAME_PIXEL_HEIGHT (f) = configureEvent.xconfigure.height;
+ FRAME_PIXEL_WIDTH (f) = configureEvent.xconfigure.width;
+ }
+
+ int width = configureEvent.xconfigure.width;
+ int height = configureEvent.xconfigure.height;
+
+ if (CONSP (frame_size_history))
+ frame_size_history_extra (f, build_string ("ConfigureNotify"),
+ FRAME_PIXEL_WIDTH (f),
+ FRAME_PIXEL_HEIGHT (f),
+ width, height, f->new_width,
+ f->new_height);
+
+ /* Even if the number of character rows and columns has
+ not changed, the font size may have changed, so we need
+ to check the pixel dimensions as well. */
+
+ if (width != FRAME_PIXEL_WIDTH (f)
+ || height != FRAME_PIXEL_HEIGHT (f)
+ || (f->new_size_p
+ && ((f->new_width >= 0 && width != f->new_width)
+ || (f->new_height >= 0 && height != f->new_height))))
+ {
+ change_frame_size (f, width, height, false, true, false);
+ android_clear_under_internal_border (f);
+ SET_FRAME_GARBAGED (f);
+ cancel_mouse_face (f);
+ }
+
+ /* Now change the left and top position of this window. */
+
+ {
+ int old_left = f->left_pos;
+ int old_top = f->top_pos;
+ Lisp_Object frame;
+
+ XSETFRAME (frame, f);
+
+ {
+ android_window root;
+ unsigned int dummy_uint;
+
+ android_get_geometry (FRAME_ANDROID_WINDOW (f),
+ &root, &f->left_pos, &f->top_pos,
+ &dummy_uint, &dummy_uint,
+ &dummy_uint);
+ }
+
+ if (!FRAME_TOOLTIP_P (f)
+ && (old_left != f->left_pos || old_top != f->top_pos))
+ {
+ inev.ie.kind = MOVE_FRAME_EVENT;
+ XSETFRAME (inev.ie.frame_or_window, f);
+ }
+
+ if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates)
+ {
+ w = XWINDOW (f->selected_window);
+ android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+ }
+ }
+
+ goto OTHER;
+
+ case ANDROID_KEY_PRESS:
+
+ /* Set f to any. There are no ``outer windows'' on Android. */
+ f = any;
+
+ /* If mouse-highlight is an integer, input clears out
+ mouse highlighting. */
+ if (!hlinfo->mouse_face_hidden && FIXNUMP (Vmouse_highlight)
+ && (any == 0
+ || !EQ (any->tool_bar_window, hlinfo->mouse_face_window)
+ || !EQ (any->tab_bar_window, hlinfo->mouse_face_window)))
+ {
+ mouse_frame = hlinfo->mouse_face_mouse_frame;
+
+ clear_mouse_face (hlinfo);
+ hlinfo->mouse_face_hidden = true;
+
+ if (mouse_frame)
+ android_flush_dirty_back_buffer_on (mouse_frame);
+ }
+
+ if (!f)
+ goto OTHER;
+
+ wchar_t copy_buffer[129];
+ wchar_t *copy_bufptr = copy_buffer;
+ int copy_bufsiz = 128 * sizeof (wchar_t);
+
+ event->xkey.state
+ |= android_emacs_to_android_modifiers (dpyinfo,
+ extra_keyboard_modifiers);
+ modifiers = event->xkey.state;
+
+ /* Common for all keysym input events. */
+ XSETFRAME (inev.ie.frame_or_window, any);
+ inev.ie.modifiers
+ = android_android_to_emacs_modifiers (dpyinfo, modifiers);
+ inev.ie.timestamp = event->xkey.time;
+
+ keysym = event->xkey.keycode;
+
+ {
+ enum android_lookup_status status_return;
+
+ nchars = android_wc_lookup_string (&event->xkey, copy_bufptr,
+ copy_bufsiz, &keysym,
+ &status_return);
+
+ /* android_lookup_string can't be called twice, so there's no
+ way to recover from buffer overflow. */
+ if (status_return == ANDROID_BUFFER_OVERFLOW)
+ goto done_keysym;
+ else if (status_return == ANDROID_LOOKUP_NONE)
+ {
+ /* Don't skip preedit text events. */
+ if (event->xkey.keycode != (uint32_t) -1)
+ goto done_keysym;
+ }
+ else if (status_return == ANDROID_LOOKUP_CHARS)
+ keysym = ANDROID_NO_SYMBOL;
+ else if (status_return != ANDROID_LOOKUP_KEYSYM
+ && status_return != ANDROID_LOOKUP_BOTH)
+ emacs_abort ();
+
+ /* Deal with pre-edit text events. On Android, these are
+ simply encoded as events with associated strings and a
+ keycode set to ``-1''. */
+
+ if (event->xkey.keycode == (uint32_t) -1)
+ {
+ inev.ie.kind = PREEDIT_TEXT_EVENT;
+ inev.ie.arg = Qnil;
+
+ /* If text was looked up, decode it and make it the
+ preedit text. */
+
+ if (status_return == ANDROID_LOOKUP_CHARS && nchars)
+ {
+ copy_bufptr[nchars] = 0;
+ inev.ie.arg = from_unicode_buffer (copy_bufptr);
+ }
+
+ goto done_keysym;
+ }
+ }
+
+ if (nchars == 1 && copy_bufptr[0] >= 32)
+ {
+ /* Deal with characters. */
+
+ if (copy_bufptr[0] < 128)
+ inev.ie.kind = ASCII_KEYSTROKE_EVENT;
+ else
+ inev.ie.kind = MULTIBYTE_CHAR_KEYSTROKE_EVENT;
+
+ inev.ie.code = copy_bufptr[0];
+ }
+ else if (nchars < 2 && keysym)
+ {
+ /* If the key is a modifier key, just return. */
+ if (ANDROID_IS_MODIFIER_KEY (keysym))
+ goto done_keysym;
+
+ /* Next, deal with special ``characters'' by giving the
+ keycode to keyboard.c. */
+ inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+ inev.ie.code = keysym;
+ }
+ else
+ {
+ /* Finally, deal with strings. */
+
+ for (i = 0; i < nchars; ++i)
+ {
+ inev.ie.kind = (SINGLE_BYTE_CHAR_P (copy_bufptr[i])
+ ? ASCII_KEYSTROKE_EVENT
+ : MULTIBYTE_CHAR_KEYSTROKE_EVENT);
+ inev.ie.code = copy_bufptr[i];
+
+ /* If the character is actually '\n', then change this
+ to RET. */
+
+ if (copy_bufptr[i] == '\n')
+ {
+ inev.ie.kind = NON_ASCII_KEYSTROKE_EVENT;
+ inev.ie.code = 66;
+ }
+
+ kbd_buffer_store_buffered_event (&inev, hold_quit);
+ }
+
+ count += nchars;
+ inev.ie.kind = NO_EVENT; /* Already stored above. */
+ }
+
+ goto done_keysym;
+
+ done_keysym:
+
+ /* Now proceed to tell the input method the current position of
+ the cursor, if required. */
+
+ if (f && FRAME_OUTPUT_DATA (f)->need_cursor_updates)
+ {
+ w = XWINDOW (f->selected_window);
+ android_set_preeditarea (w, w->cursor.x, w->cursor.y);
+ }
+
+ goto OTHER;
+
+ case ANDROID_FOCUS_IN:
+ case ANDROID_FOCUS_OUT:
+ android_detect_focus_change (dpyinfo, any, event, &inev.ie);
+ goto OTHER;
+
+ case ANDROID_WINDOW_ACTION:
+
+ /* This is a special event sent by android_run_in_emacs_thread
+ used to make Android run stuff. */
+
+ if (!event->xaction.window && !event->xaction.action)
+ {
+ /* Check for and run anything the UI thread wants to run on the main
+ thread. */
+ android_check_query ();
+ goto OTHER;
+ }
+
+ f = any;
+
+ if (event->xaction.action == 0)
+ {
+ /* Action 0 either means that a window has been destroyed
+ and its associated frame should be as well. */
+
+ if (event->xaction.window)
+ {
+ if (!f)
+ goto OTHER;
+
+ inev.ie.kind = DELETE_WINDOW_EVENT;
+ XSETFRAME (inev.ie.frame_or_window, f);
+ }
+ }
+
+ case ANDROID_ENTER_NOTIFY:
+ f = any;
+
+ if (f)
+ android_note_mouse_movement (f, &event->xmotion);
+ goto OTHER;
+
+ case ANDROID_MOTION_NOTIFY:
+
+ previous_help_echo_string = help_echo_string;
+ help_echo_string = Qnil;
+
+ if (hlinfo->mouse_face_hidden)
+ {
+ hlinfo->mouse_face_hidden = false;
+ clear_mouse_face (hlinfo);
+ }
+
+ f = any;
+
+ if (f)
+ {
+ /* Maybe generate a SELECT_WINDOW_EVENT for
+ `mouse-autoselect-window' but don't let popup menus
+ interfere with this (Bug#1261). */
+ if (!NILP (Vmouse_autoselect_window)
+ && !popup_activated ()
+ /* Don't switch if we're currently in the minibuffer.
+ This tries to work around problems where the
+ minibuffer gets unselected unexpectedly, and where
+ you then have to move your mouse all the way down to
+ the minibuffer to select it. */
+ && !MINI_WINDOW_P (XWINDOW (selected_window))
+ /* With `focus-follows-mouse' non-nil create an event
+ also when the target window is on another frame. */
+ && (f == XFRAME (selected_frame)
+ || !NILP (focus_follows_mouse)))
+ {
+ static Lisp_Object last_mouse_window;
+ Lisp_Object window
+ = window_from_coordinates (f, event->xmotion.x,
+ event->xmotion.y, 0,
+ false, false);
+
+ /* A window will be autoselected only when it is not
+ selected now and the last mouse movement event was
+ not in it. The remainder of the code is a bit vague
+ wrt what a "window" is. For immediate autoselection,
+ the window is usually the entire window but for GTK
+ where the scroll bars don't count. For delayed
+ autoselection the window is usually the window's text
+ area including the margins. */
+ if (WINDOWP (window)
+ && !EQ (window, last_mouse_window)
+ && !EQ (window, selected_window))
+ {
+ inev.ie.kind = SELECT_WINDOW_EVENT;
+ inev.ie.frame_or_window = window;
+ }
+
+ /* Remember the last window where we saw the mouse. */
+ last_mouse_window = window;
+ }
+
+ if (!android_note_mouse_movement (f, &event->xmotion))
+ help_echo_string = previous_help_echo_string;
+ }
+
+ /* If the contents of the global variable help_echo_string
+ has changed, generate a HELP_EVENT. */
+ if (!NILP (help_echo_string)
+ || !NILP (previous_help_echo_string))
+ do_help = 1;
+
+ if (f)
+ android_flush_dirty_back_buffer_on (f);
+
+ goto OTHER;
+
+ case ANDROID_LEAVE_NOTIFY:
+ f = any;
+
+ if (f)
+ {
+ /* Now clear dpyinfo->last_mouse_motion_frame, or
+ gui_redo_mouse_highlight will end up highlighting the
+ last known position of the mouse if a tooltip frame is
+ later unmapped. */
+
+ if (f == dpyinfo->last_mouse_motion_frame)
+ dpyinfo->last_mouse_motion_frame = NULL;
+
+ /* Something similar applies to
+ dpyinfo->last_mouse_glyph_frame. */
+ if (f == dpyinfo->last_mouse_glyph_frame)
+ dpyinfo->last_mouse_glyph_frame = NULL;
+
+ if (f == hlinfo->mouse_face_mouse_frame)
+ {
+ /* If we move outside the frame, then we're
+ certainly no longer on any text in the frame. */
+ clear_mouse_face (hlinfo);
+ hlinfo->mouse_face_mouse_frame = 0;
+ android_flush_dirty_back_buffer_on (f);
+ }
+
+ /* Generate a nil HELP_EVENT to cancel a help-echo.
+ Do it only if there's something to cancel.
+ Otherwise, the startup message is cleared when
+ the mouse leaves the frame. */
+ if (any_help_event_p
+ /* But never if `mouse-drag-and-drop-region' is in
+ progress, since that results in the tooltip being
+ dismissed when the mouse moves on top. */
+ && !((EQ (track_mouse, Qdrag_source)
+ || EQ (track_mouse, Qdropping))
+ && gui_mouse_grabbed (dpyinfo)))
+ do_help = -1;
+ }
+
+ goto OTHER;
+
+ case ANDROID_EXPOSE:
+
+ f = any;
+
+ if (f)
+ {
+ if (!FRAME_VISIBLE_P (f))
+ {
+ f->output_data.android->has_been_visible = true;
+ SET_FRAME_GARBAGED (f);
+ }
+
+ if (!FRAME_GARBAGED_P (f))
+ {
+ expose_frame (f, event->xexpose.x, event->xexpose.y,
+ event->xexpose.width, event->xexpose.height);
+ show_back_buffer (f);
+ }
+ }
+
+ goto OTHER;
+
+ case ANDROID_BUTTON_PRESS:
+ case ANDROID_BUTTON_RELEASE:
+ /* If we decide we want to generate an event to be seen
+ by the rest of Emacs, we put it here. */
+
+ f = any;
+
+ Lisp_Object tab_bar_arg = Qnil;
+ bool tab_bar_p = false;
+ bool tool_bar_p = false;
+
+ dpyinfo->last_mouse_glyph_frame = NULL;
+
+ f = mouse_or_wdesc_frame (dpyinfo, event->xbutton.window);
+
+ if (f && event->xbutton.type == ANDROID_BUTTON_PRESS
+ && !popup_activated ()
+ /* && !x_window_to_scroll_bar (event->xbutton.display, */
+ /* event->xbutton.window, 2) */
+ && !FRAME_NO_ACCEPT_FOCUS (f))
+ {
+ /* When clicking into a child frame or when clicking
+ into a parent frame with the child frame selected and
+ `no-accept-focus' is not set, select the clicked
+ frame. */
+ struct frame *hf = dpyinfo->highlight_frame;
+
+ if (FRAME_PARENT_FRAME (f) || (hf && frame_ancestor_p (f, hf)))
+ {
+ android_set_input_focus (FRAME_ANDROID_WINDOW (f),
+ event->xbutton.time);
+
+ if (FRAME_PARENT_FRAME (f))
+ android_raise_window (FRAME_ANDROID_WINDOW (f));
+ }
+ }
+
+ if (f)
+ {
+ /* Is this in the tab-bar? */
+ if (WINDOWP (f->tab_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tab_bar_window)))
+ {
+ Lisp_Object window;
+ int x = event->xbutton.x;
+ int y = event->xbutton.y;
+
+ window = window_from_coordinates (f, x, y, 0, true, true);
+ tab_bar_p = EQ (window, f->tab_bar_window);
+
+ if (tab_bar_p)
+ {
+ tab_bar_arg = handle_tab_bar_click
+ (f, x, y, (event->xbutton.type
+ == ANDROID_BUTTON_PRESS),
+ android_android_to_emacs_modifiers (dpyinfo,
+ event->xbutton.state));
+ android_flush_dirty_back_buffer_on (f);
+ }
+ }
+
+ /* Is this in the tool-bar? */
+ if (WINDOWP (f->tool_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (f->tool_bar_window)))
+ {
+ Lisp_Object window;
+ int x = event->xbutton.x;
+ int y = event->xbutton.y;
+
+ window = window_from_coordinates (f, x, y, 0, true, true);
+ tool_bar_p = (EQ (window, f->tool_bar_window)
+ && ((event->xbutton.type
+ != ANDROID_BUTTON_RELEASE)
+ || f->last_tool_bar_item != -1));
+
+ if (tool_bar_p && event->xbutton.button < 4)
+ {
+ handle_tool_bar_click
+ (f, x, y, (event->xbutton.type
+ == ANDROID_BUTTON_PRESS),
+ android_android_to_emacs_modifiers (dpyinfo,
+ event->xbutton.state));
+ android_flush_dirty_back_buffer_on (f);
+ }
+ }
+
+ if (!(tab_bar_p && NILP (tab_bar_arg)) && !tool_bar_p)
+ {
+ android_construct_mouse_click (&inev.ie, &event->xbutton, f);
+
+ if (!NILP (tab_bar_arg))
+ inev.ie.arg = tab_bar_arg;
+ }
+ }
+
+ if (event->type == ANDROID_BUTTON_PRESS)
+ {
+ dpyinfo->grabbed |= (1 << event->xbutton.button);
+ dpyinfo->last_mouse_frame = f;
+ if (f && !tab_bar_p)
+ f->last_tab_bar_item = -1;
+ if (f && !tool_bar_p)
+ f->last_tool_bar_item = -1;
+ }
+ else
+ dpyinfo->grabbed &= ~(1 << event->xbutton.button);
+
+ /* Ignore any mouse motion that happened before this event;
+ any subsequent mouse-movement Emacs events should reflect
+ only motion after the ButtonPress/Release. */
+ if (f != 0)
+ f->mouse_moved = false;
+
+ goto OTHER;
+
+ /* Touch events. The events here don't parallel X so much. */
+ case ANDROID_TOUCH_DOWN:
+
+ if (!any)
+ goto OTHER;
+
+ /* This event is sent when a tool is put on the screen. X and Y
+ are the location of the finger, and pointer_id identifies the
+ tool for as long as it is still held down. First, see if the
+ touch point already exists and can be reused (this shouldn't
+ happen, but be safe.) */
+
+ touchpoint = android_find_tool (any, event->touch.pointer_id);
+
+ if (touchpoint)
+ {
+ /* Simply update the tool position and send an update. */
+ touchpoint->x = event->touch.x;
+ touchpoint->y = event->touch.x;
+ android_update_tools (any, &inev.ie);
+ inev.ie.timestamp = event->touch.time;
+
+ goto OTHER;
+ }
+
+ /* Otherwise, link a new touchpoint onto the output's list of
+ pressed tools. */
+
+ touchpoint = xmalloc (sizeof *touchpoint);
+ touchpoint->tool_id = event->touch.pointer_id;
+ touchpoint->x = event->touch.x;
+ touchpoint->y = event->touch.x;
+ touchpoint->next = FRAME_OUTPUT_DATA (any)->touch_points;
+ touchpoint->tool_bar_p = false;
+ FRAME_OUTPUT_DATA (any)->touch_points = touchpoint;
+
+ /* Figure out whether or not the tool was pressed on the tool
+ bar. Note that the code which runs when it was is more or
+ less an abuse of the mouse highlight machinery, but it works
+ well enough in practice. */
+
+ if (WINDOWP (any->tool_bar_window)
+ && WINDOW_TOTAL_LINES (XWINDOW (any->tool_bar_window)))
+ {
+ Lisp_Object window;
+ int x = event->touch.x;
+ int y = event->touch.y;
+
+ window = window_from_coordinates (any, x, y, 0, true,
+ true);
+
+ /* If this touch has started in the tool bar, do not
+ send it to Lisp. Instead, simulate a tool bar
+ click, releasing it once it goes away. */
+
+ if (EQ (window, any->tool_bar_window))
+ {
+ /* Call note_mouse_highlight on the tool bar
+ item. Otherwise, get_tool_bar_item will
+ return 1.
+
+ This is not necessary when mouse-highlight is
+ nil. */
+
+ 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
+ update the mouse highlight, no matter
+ where it is. */
+ memset (&dpyinfo->last_mouse_glyph, 0,
+ sizeof dpyinfo->last_mouse_glyph);
+ dpyinfo->last_mouse_glyph_frame = any;
+ }
+
+ handle_tool_bar_click (any, x, y, true, 0);
+
+ /* Flush any changes made by that to the front
+ buffer. */
+ android_flush_dirty_back_buffer_on (any);
+
+ /* Mark the touch point as being grabbed by the tool
+ bar. */
+ touchpoint->tool_bar_p = true;
+ goto OTHER;
+ }
+ }
+
+ /* Now generate the Emacs event. */
+ inev.ie.kind = TOUCHSCREEN_BEGIN_EVENT;
+ inev.ie.timestamp = event->touch.time;
+ XSETFRAME (inev.ie.frame_or_window, any);
+ XSETINT (inev.ie.x, event->touch.x);
+ XSETINT (inev.ie.y, event->touch.y);
+ XSETINT (inev.ie.arg, event->touch.pointer_id);
+
+ goto OTHER;
+
+ case ANDROID_TOUCH_MOVE:
+
+ if (!any)
+ goto OTHER;
+
+ /* Look for the tool that moved. */
+
+ touchpoint = android_find_tool (any, event->touch.pointer_id);
+
+ /* If it doesn't exist or has been grabbed by the tool bar, skip
+ processing this event. */
+
+ if (!touchpoint || touchpoint->tool_bar_p)
+ goto OTHER;
+
+ /* Otherwise, update the position and send the update event. */
+
+ touchpoint->x = event->touch.x;
+ touchpoint->y = event->touch.y;
+ android_update_tools (any, &inev.ie);
+ inev.ie.timestamp = event->touch.time;
+
+ goto OTHER;
+
+ case ANDROID_TOUCH_UP:
+
+ if (!any)
+ goto OTHER;
+
+ /* Now find and unlink the tool in question. */
+
+ last = &FRAME_OUTPUT_DATA (any)->touch_points;
+ while ((touchpoint = *last))
+ {
+ if (touchpoint->tool_id == event->touch.pointer_id)
+ {
+ *last = touchpoint->next;
+
+ if (touchpoint->tool_bar_p)
+ {
+ xfree (touchpoint);
+
+ /* Do what is necessary to release the tool bar and
+ possibly trigger a click. */
+
+ if (any->last_tool_bar_item != -1)
+ handle_tool_bar_click (any, event->touch.x,
+ event->touch.y, false,
+ 0);
+
+ /* Cancel any outstanding mouse highlight. */
+ note_mouse_highlight (any, -1, -1);
+ android_flush_dirty_back_buffer_on (any);
+
+ goto OTHER;
+ }
+
+ /* The tool was unlinked. Free it and generate the
+ appropriate Emacs event (assuming that it was not
+ grabbed by the tool bar). */
+ xfree (touchpoint);
+
+ inev.ie.kind = TOUCHSCREEN_END_EVENT;
+ inev.ie.timestamp = event->touch.time;
+
+ XSETFRAME (inev.ie.frame_or_window, any);
+ XSETINT (inev.ie.x, event->touch.x);
+ XSETINT (inev.ie.y, event->touch.y);
+ XSETINT (inev.ie.arg, event->touch.pointer_id);
+
+ /* Break out of the loop. */
+ goto OTHER;
+ }
+ else
+ last = &touchpoint->next;
+ }
+
+ /* No touch point was found. This shouldn't happen. */
+ goto OTHER;
+
+ /* Wheel motion. The events here don't parallel X because
+ Android doesn't have scroll valuators. */
+
+ case ANDROID_WHEEL:
+
+ if (!any)
+ goto OTHER;
+
+ if (fabs (event->wheel.x_delta) > 0
+ || fabs (event->wheel.y_delta) > 0)
+ {
+ if (mwheel_coalesce_scroll_events)
+ {
+ if (signbit (event->wheel.x_delta)
+ != signbit (wheel_event_x))
+ wheel_event_x = 0.0;
+
+ if (signbit (event->wheel.y_delta)
+ != signbit (wheel_event_y))
+ wheel_event_y = 0.0;
+
+ /* Tally up deltas until one of them exceeds 1.0. */
+ wheel_event_x += event->wheel.x_delta;
+ wheel_event_y += event->wheel.y_delta;
+
+ if (fabs (wheel_event_x) < 1.0
+ && fabs (wheel_event_y) < 1.0)
+ goto OTHER;
+ }
+ else
+ {
+ /* Use the deltas in the event. */
+ wheel_event_x = event->wheel.x_delta;
+ wheel_event_y = event->wheel.y_delta;
+ }
+
+ /* Determine what kind of event to send. */
+ inev.ie.kind = ((fabs (wheel_event_y)
+ >= fabs (wheel_event_x))
+ ? WHEEL_EVENT : HORIZ_WHEEL_EVENT);
+ inev.ie.timestamp = event->wheel.time;
+
+ /* Set the event coordinates. */
+ XSETINT (inev.ie.x, event->wheel.x);
+ XSETINT (inev.ie.y, event->wheel.y);
+
+ /* Set the frame. */
+ XSETFRAME (inev.ie.frame_or_window, any);
+
+ /* Figure out the scroll direction. */
+ inev.ie.modifiers = (signbit ((fabs (wheel_event_x)
+ >= fabs (wheel_event_y))
+ ? wheel_event_x
+ : wheel_event_y)
+ ? down_modifier : up_modifier);
+
+ /* Figure out how much to scale the deltas by. */
+ window = window_from_coordinates (any, event->wheel.x,
+ event->wheel.y, NULL,
+ false, false);
+
+ if (WINDOWP (window))
+ scroll_height = XWINDOW (window)->pixel_height;
+ else
+ /* EVENT_X and EVENT_Y can be outside the
+ frame if F holds the input grab, so fall
+ back to the height of the frame instead. */
+ scroll_height = FRAME_PIXEL_HEIGHT (any);
+
+ scroll_unit = pow (scroll_height, 2.0 / 3.0);
+
+ /* Add the keyboard modifiers. */
+ inev.ie.modifiers
+ |= android_android_to_emacs_modifiers (dpyinfo,
+ event->wheel.state);
+
+ /* Finally include the scroll deltas. */
+ inev.ie.arg = list3 (Qnil,
+ make_float (wheel_event_x
+ * scroll_unit),
+ make_float (wheel_event_y
+ * scroll_unit));
+
+ wheel_event_x = 0.0;
+ wheel_event_y = 0.0;
+ }
+
+ goto OTHER;
+
+ /* Iconification. This is vastly simpler than on X. */
+ case ANDROID_ICONIFIED:
+
+ if (!any)
+ goto OTHER;
+
+ if (FRAME_ICONIFIED_P (any))
+ goto OTHER;
+
+ SET_FRAME_VISIBLE (any, false);
+ SET_FRAME_ICONIFIED (any, true);
+
+ inev.ie.kind = ICONIFY_EVENT;
+ XSETFRAME (inev.ie.frame_or_window, any);
+ goto OTHER;
+
+ case ANDROID_DEICONIFIED:
+
+ if (!any)
+ goto OTHER;
+
+ if (!FRAME_ICONIFIED_P (any))
+ goto OTHER;
+
+ SET_FRAME_VISIBLE (any, true);
+ SET_FRAME_ICONIFIED (any, false);
+
+ inev.ie.kind = DEICONIFY_EVENT;
+ XSETFRAME (inev.ie.frame_or_window, any);
+ goto OTHER;
+
+ /* Context menu handling. */
+ case ANDROID_CONTEXT_MENU:
+
+ if (dpyinfo->menu_event_id == -1
+ /* Previously displayed popup menus might generate events
+ after dismissal, which might interfere.
+ `current_menu_serial' is always set to an identifier
+ identifying the last context menu to be displayed. */
+ && event->menu.menu_event_serial == current_menu_serial)
+ dpyinfo->menu_event_id = event->menu.menu_event_id;
+
+ goto OTHER;
+
+ /* Input method events. textconv.c functions are called here to
+ queue events, which are then executed in a safe context
+ inside keyboard.c. */
+ case ANDROID_INPUT_METHOD:
+
+ if (!any)
+ /* Free any text allocated for this event. */
+ xfree (event->ime.text);
+ else
+ android_handle_ime_event (event, any);
+
+ goto OTHER;
+
+ default:
+ goto OTHER;
+ }
+
+ OTHER:
+ if (inev.ie.kind != NO_EVENT)
+ {
+ kbd_buffer_store_buffered_event (&inev, hold_quit);
+ count++;
+ }
+
+ if (do_help
+ && !(hold_quit && hold_quit->kind != NO_EVENT))
+ {
+ Lisp_Object frame;
+
+ if (f)
+ XSETFRAME (frame, f);
+ else
+ frame = Qnil;
+
+ if (do_help > 0)
+ {
+ any_help_event_p = true;
+ gen_help_event (help_echo_string, frame, help_echo_window,
+ help_echo_object, help_echo_pos);
+ }
+ else
+ {
+ help_echo_string = Qnil;
+ gen_help_event (Qnil, frame, Qnil, Qnil, 0);
+ }
+ count++;
+ }
+
+ return count;
+}
+
+static int
+android_read_socket (struct terminal *terminal,
+ struct input_event *hold_quit)
+{
+ int count = 0;
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = terminal->display_info.android;
+
+ block_input ();
+ while (android_pending ())
+ {
+ int finish;
+ union android_event event;
+
+ android_next_event (&event);
+ count += handle_one_android_event (dpyinfo, &event, &finish,
+ hold_quit);
+
+ if (finish == ANDROID_EVENT_GOTO_OUT)
+ break;
+ }
+ unblock_input ();
+
+ /* If the focus was just given to an auto-raising frame, raise it
+ now. */
+ if (dpyinfo->pending_autoraise_frame)
+ {
+ android_raise_frame (dpyinfo->pending_autoraise_frame);
+ dpyinfo->pending_autoraise_frame = NULL;
+ }
+
+ return count;
+}
+
+static void
+android_frame_up_to_date (struct frame *f)
+{
+ eassert (FRAME_ANDROID_P (f));
+ block_input ();
+ FRAME_MOUSE_UPDATE (f);
+
+ if (!buffer_flipping_blocked_p ()
+ && FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+ show_back_buffer (f);
+
+ /* The frame is now complete, as its contents have been drawn. */
+ FRAME_ANDROID_COMPLETE_P (f) = true;
+
+ /* Shrink the scanline buffer used by the font backend. */
+ sfntfont_android_shrink_scanline_buffer ();
+ unblock_input ();
+}
+
+static void
+android_buffer_flipping_unblocked_hook (struct frame *f)
+{
+ block_input ();
+
+ if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+ show_back_buffer (f);
+
+ unblock_input ();
+}
+
+static void
+android_query_frame_background_color (struct frame *f, Emacs_Color *bgcolor)
+{
+ unsigned long background;
+
+ background = FRAME_BACKGROUND_PIXEL (f);
+ bgcolor->pixel = background;
+
+ android_query_colors (f, bgcolor, 1);
+}
+
+int
+android_parse_color (struct frame *f, const char *color_name,
+ Emacs_Color *color)
+{
+ unsigned short r, g, b;
+ Lisp_Object tem, tem1;
+ unsigned long lisp_color;
+
+ if (parse_color_spec (color_name, &r, &g, &b))
+ {
+ color->red = r;
+ color->green = g;
+ color->blue = b;
+
+ return 1;
+ }
+
+ tem = x_display_list->color_map;
+ for (; CONSP (tem); tem = XCDR (tem))
+ {
+ tem1 = XCAR (tem);
+
+ if (CONSP (tem1)
+ && !xstrcasecmp (SSDATA (XCAR (tem1)), color_name))
+ {
+ lisp_color = XFIXNUM (XCDR (tem1));
+ color->red = RED_FROM_ULONG (lisp_color) * 257;
+ color->green = GREEN_FROM_ULONG (lisp_color) * 257;
+ color->blue = BLUE_FROM_ULONG (lisp_color) * 257;
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+bool
+android_alloc_nearest_color (struct frame *f, Emacs_Color *color)
+{
+ gamma_correct (f, color);
+ color->pixel = RGB_TO_ULONG (color->red / 256,
+ color->green / 256,
+ color->blue / 256);
+
+ return true;
+}
+
+void
+android_query_colors (struct frame *f, Emacs_Color *colors, int ncolors)
+{
+ int i;
+
+ for (i = 0; i < ncolors; ++i)
+ {
+ colors[i].red = RED_FROM_ULONG (colors[i].pixel) * 257;
+ colors[i].green = RED_FROM_ULONG (colors[i].pixel) * 257;
+ colors[i].blue = RED_FROM_ULONG (colors[i].pixel) * 257;
+ }
+}
+
+static void
+android_mouse_position (struct frame **fp, int insist,
+ Lisp_Object *bar_window,
+ enum scroll_bar_part *part, Lisp_Object *x,
+ Lisp_Object *y, Time *timestamp)
+{
+ Lisp_Object tail, frame;
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = FRAME_DISPLAY_INFO (*fp);
+
+ /* This is the best implementation possible on Android, where the
+ system doesn't let Emacs obtain any information about the mouse
+ pointer at all. */
+
+ if (dpyinfo->last_mouse_motion_frame)
+ {
+ *fp = dpyinfo->last_mouse_motion_frame;
+ *timestamp = dpyinfo->last_mouse_movement_time;
+ *x = make_fixnum (dpyinfo->last_mouse_motion_x);
+ *y = make_fixnum (dpyinfo->last_mouse_motion_y);
+ *bar_window = Qnil;
+ *part = scroll_bar_nowhere;
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ if (FRAME_ANDROID_P (XFRAME (frame)))
+ XFRAME (frame)->mouse_moved = false;
+ }
+
+ dpyinfo->last_mouse_motion_frame->mouse_moved = false;
+ }
+}
+
+static Lisp_Object
+android_get_focus_frame (struct frame *f)
+{
+ Lisp_Object lisp_focus;
+ struct frame *focus;
+
+ focus = FRAME_DISPLAY_INFO (f)->focus_frame;
+
+ if (!focus)
+ return Qnil;
+
+ XSETFRAME (lisp_focus, focus);
+ return lisp_focus;
+}
+
+static void
+android_focus_frame (struct frame *f, bool noactivate)
+{
+ /* Set the input focus to the frame's window. The system only lets
+ this work on child frames. */
+ android_set_input_focus (FRAME_ANDROID_WINDOW (f),
+ ANDROID_CURRENT_TIME);
+}
+
+/* The two procedures below only have to update the cursor on Android,
+ as there are no window borders there. */
+
+static void
+android_frame_highlight (struct frame *f)
+{
+ gui_update_cursor (f, true);
+}
+
+static void
+android_frame_unhighlight (struct frame *f)
+{
+ gui_update_cursor (f, true);
+}
+
+static void
+android_frame_rehighlight (struct android_display_info *dpyinfo)
+{
+ struct frame *old_highlight;
+
+ old_highlight = dpyinfo->highlight_frame;
+
+ if (dpyinfo->focus_frame)
+ {
+ dpyinfo->highlight_frame
+ = ((FRAMEP (FRAME_FOCUS_FRAME (dpyinfo->focus_frame)))
+ ? XFRAME (FRAME_FOCUS_FRAME (dpyinfo->focus_frame))
+ : dpyinfo->focus_frame);
+ if (!FRAME_LIVE_P (dpyinfo->highlight_frame))
+ {
+ fset_focus_frame (dpyinfo->focus_frame, Qnil);
+ dpyinfo->highlight_frame = dpyinfo->focus_frame;
+ }
+ }
+ else
+ dpyinfo->highlight_frame = 0;
+
+ if (dpyinfo->highlight_frame != old_highlight)
+ {
+ /* This is not yet required on Android. */
+ if (old_highlight)
+ android_frame_unhighlight (old_highlight);
+ if (dpyinfo->highlight_frame)
+ android_frame_highlight (dpyinfo->highlight_frame);
+ }
+}
+
+static void
+android_frame_rehighlight_hook (struct frame *f)
+{
+ android_frame_rehighlight (FRAME_DISPLAY_INFO (f));
+}
+
+static void
+android_frame_raise_lower (struct frame *f, bool raise_flag)
+{
+ if (raise_flag)
+ android_raise_frame (f);
+ else
+ android_lower_frame (f);
+}
+
+void
+android_make_frame_visible (struct frame *f)
+{
+ android_map_window (FRAME_ANDROID_WINDOW (f));
+
+ SET_FRAME_VISIBLE (f, true);
+ SET_FRAME_ICONIFIED (f, false);
+}
+
+void
+android_make_frame_invisible (struct frame *f)
+{
+ /* Don't keep the highlight on an invisible frame. */
+ if (FRAME_DISPLAY_INFO (f)->highlight_frame == f)
+ FRAME_DISPLAY_INFO (f)->highlight_frame = 0;
+
+ android_unmap_window (FRAME_ANDROID_WINDOW (f));
+
+ SET_FRAME_VISIBLE (f, false);
+ SET_FRAME_ICONIFIED (f, false);
+}
+
+static void
+android_make_frame_visible_invisible (struct frame *f, bool visible)
+{
+ if (visible)
+ android_make_frame_visible (f);
+ else
+ android_make_frame_invisible (f);
+}
+
+static void
+android_fullscreen_hook (struct frame *f)
+{
+ Lisp_Object wanted;
+
+ if (!FRAME_PARENT_FRAME (f))
+ {
+ /* Explicitly setting fullscreen is not supported on older
+ Android versions. */
+
+ wanted = (f->want_fullscreen == FULLSCREEN_BOTH
+ ? Qfullscreen : Qmaximized);
+
+ if (android_set_fullscreen (FRAME_ANDROID_WINDOW (f),
+ EQ (wanted, Qfullscreen)))
+ store_frame_param (f, Qfullscreen, Qmaximized);
+ else
+ store_frame_param (f, Qfullscreen, wanted);
+ }
+ else
+ {
+ store_frame_param (f, Qfullscreen, Qnil);
+
+ /* If this is a child frame, don't keep it fullscreen
+ anymore. */
+ android_set_fullscreen (FRAME_ANDROID_WINDOW (f), false);
+ }
+}
+
+void
+android_iconify_frame (struct frame *f)
+{
+ /* This really doesn't work on Android. */
+ error ("Can't notify window manager of iconification");
+}
+
+static void
+android_wait_for_event (struct frame *f, int eventtype)
+{
+ if (!FLOATP (Vandroid_wait_for_event_timeout))
+ return;
+
+ int level = interrupt_input_blocked;
+ struct timespec tmo, tmo_at, time_now;
+
+ f->wait_event_type = eventtype;
+
+ /* Default timeout is 0.1 second. Hopefully not noticeable. */
+ double timeout = XFLOAT_DATA (Vandroid_wait_for_event_timeout);
+ time_t timeout_seconds = (time_t) timeout;
+ tmo = make_timespec (timeout_seconds,
+ (long int) ((timeout - timeout_seconds)
+ * 1000 * 1000 * 1000));
+ tmo_at = timespec_add (current_timespec (), tmo);
+
+ while (f->wait_event_type)
+ {
+ pending_signals = true;
+ totally_unblock_input ();
+ /* XTread_socket is called after unblock. */
+ block_input ();
+ interrupt_input_blocked = level;
+
+ time_now = current_timespec ();
+ if (timespec_cmp (tmo_at, time_now) < 0)
+ break;
+
+ tmo = timespec_sub (tmo_at, time_now);
+ if (android_select (0, NULL, NULL, NULL, &tmo) == 0)
+ break; /* Timeout */
+ }
+
+ f->wait_event_type = 0;
+}
+
+static void
+android_set_window_size_1 (struct frame *f, bool change_gravity,
+ int width, int height)
+{
+ if (change_gravity)
+ f->win_gravity = NorthWestGravity;
+
+ android_resize_window (FRAME_ANDROID_WINDOW (f), width,
+ height);
+
+ SET_FRAME_GARBAGED (f);
+
+ if (FRAME_VISIBLE_P (f))
+ {
+ android_wait_for_event (f, ANDROID_CONFIGURE_NOTIFY);
+
+ if (CONSP (frame_size_history))
+ frame_size_history_extra (f, build_string ("set_window_size_1 visible"),
+ FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+ width, height, f->new_width, f->new_height);
+ }
+ else
+ {
+ if (CONSP (frame_size_history))
+ frame_size_history_extra (f, build_string ("set_window_size_1 "
+ "invisible"),
+ FRAME_PIXEL_WIDTH (f), FRAME_PIXEL_HEIGHT (f),
+ width, height, f->new_width, f->new_height);
+
+ adjust_frame_size (f, FRAME_PIXEL_TO_TEXT_WIDTH (f, width),
+ FRAME_PIXEL_TO_TEXT_HEIGHT (f, height),
+ 5, 0, Qx_set_window_size_1);
+ }
+}
+
+void
+android_set_window_size (struct frame *f, bool change_gravity,
+ int width, int height)
+{
+ block_input ();
+
+ android_set_window_size_1 (f, change_gravity, width, height);
+ android_clear_under_internal_border (f);
+
+ /* If cursor was outside the new size, mark it as off. */
+ mark_window_cursors_off (XWINDOW (f->root_window));
+
+ /* Clear out any recollection of where the mouse highlighting was,
+ since it might be in a place that's outside the new frame size.
+ Actually checking whether it is outside is a pain in the neck,
+ so don't try--just let the highlighting be done afresh with new size. */
+ cancel_mouse_face (f);
+
+ unblock_input ();
+
+ do_pending_window_change (false);
+}
+
+static void
+android_set_offset (struct frame *f, int xoff, int yoff,
+ int change_gravity)
+{
+ if (change_gravity > 0)
+ {
+ f->top_pos = yoff;
+ f->left_pos = xoff;
+ f->size_hint_flags &= ~ (XNegative | YNegative);
+ if (xoff < 0)
+ f->size_hint_flags |= XNegative;
+ if (yoff < 0)
+ f->size_hint_flags |= YNegative;
+ f->win_gravity = NorthWestGravity;
+ }
+
+ android_move_window (FRAME_ANDROID_WINDOW (f), xoff, yoff);
+}
+
+static void
+android_set_alpha (struct frame *f)
+{
+ /* Not supported on Android. */
+}
+
+static Lisp_Object
+android_new_font (struct frame *f, Lisp_Object font_object, int fontset)
+{
+ struct font *font = XFONT_OBJECT (font_object);
+ int unit, font_ascent, font_descent;
+
+ if (fontset < 0)
+ fontset = fontset_from_font (font_object);
+ FRAME_FONTSET (f) = fontset;
+ if (FRAME_FONT (f) == font)
+ /* This font is already set in frame F. There's nothing more to
+ do. */
+ return font_object;
+
+ FRAME_FONT (f) = font;
+ FRAME_BASELINE_OFFSET (f) = font->baseline_offset;
+ FRAME_COLUMN_WIDTH (f) = font->average_width;
+ get_font_ascent_descent (font, &font_ascent, &font_descent);
+ FRAME_LINE_HEIGHT (f) = font_ascent + font_descent;
+
+ /* We could use a more elaborate calculation here. */
+ FRAME_TAB_BAR_HEIGHT (f) = FRAME_TAB_BAR_LINES (f) * FRAME_LINE_HEIGHT (f);
+
+ /* Compute character columns occupied by scrollbar.
+
+ Don't do things differently for non-toolkit scrollbars
+ (Bug#17163). */
+ unit = FRAME_COLUMN_WIDTH (f);
+ if (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) > 0)
+ FRAME_CONFIG_SCROLL_BAR_COLS (f)
+ = (FRAME_CONFIG_SCROLL_BAR_WIDTH (f) + unit - 1) / unit;
+ else
+ FRAME_CONFIG_SCROLL_BAR_COLS (f) = (14 + unit - 1) / unit;
+
+
+ /* Don't change the size of a tip frame; there's no point in doing it
+ because it's done in Fx_show_tip, and it leads to problems because
+ the tip frame has no widget. */
+ if (FRAME_ANDROID_WINDOW (f) != 0 && !FRAME_TOOLTIP_P (f))
+ adjust_frame_size (f, FRAME_COLS (f) * FRAME_COLUMN_WIDTH (f),
+ FRAME_LINES (f) * FRAME_LINE_HEIGHT (f), 3,
+ false, Qfont);
+
+ return font_object;
+}
+
+static bool
+android_bitmap_icon (struct frame *f, Lisp_Object file)
+{
+ return false;
+}
+
+static void
+android_free_pixmap_hook (struct frame *f, Emacs_Pixmap pixmap)
+{
+ android_free_pixmap (pixmap);
+}
+
+void
+android_free_frame_resources (struct frame *f)
+{
+ struct android_display_info *dpyinfo;
+ Mouse_HLInfo *hlinfo;
+ struct android_touch_point *last, *next;
+
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+ hlinfo = &dpyinfo->mouse_highlight;
+
+ block_input ();
+ free_frame_faces (f);
+
+ /* FRAME_ANDROID_WINDOW can be 0 if frame creation failed. */
+ if (FRAME_ANDROID_WINDOW (f))
+ android_destroy_window (FRAME_ANDROID_WINDOW (f));
+
+ android_free_gcs (f);
+
+ /* Free cursors. */
+ if (f->output_data.android->text_cursor)
+ android_free_cursor (f->output_data.android->text_cursor);
+ if (f->output_data.android->nontext_cursor)
+ android_free_cursor (f->output_data.android->nontext_cursor);
+ if (f->output_data.android->modeline_cursor)
+ android_free_cursor (f->output_data.android->modeline_cursor);
+ if (f->output_data.android->hand_cursor)
+ android_free_cursor (f->output_data.android->hand_cursor);
+ if (f->output_data.android->hourglass_cursor)
+ android_free_cursor (f->output_data.android->hourglass_cursor);
+ if (f->output_data.android->horizontal_drag_cursor)
+ android_free_cursor (f->output_data.android->horizontal_drag_cursor);
+ if (f->output_data.android->vertical_drag_cursor)
+ android_free_cursor (f->output_data.android->vertical_drag_cursor);
+ if (f->output_data.android->left_edge_cursor)
+ android_free_cursor (f->output_data.android->left_edge_cursor);
+ if (f->output_data.android->top_left_corner_cursor)
+ android_free_cursor (f->output_data.android->top_left_corner_cursor);
+ if (f->output_data.android->top_edge_cursor)
+ android_free_cursor (f->output_data.android->top_edge_cursor);
+ if (f->output_data.android->top_right_corner_cursor)
+ android_free_cursor (f->output_data.android->top_right_corner_cursor);
+ if (f->output_data.android->right_edge_cursor)
+ android_free_cursor (f->output_data.android->right_edge_cursor);
+ if (f->output_data.android->bottom_right_corner_cursor)
+ android_free_cursor (f->output_data.android->bottom_right_corner_cursor);
+ if (f->output_data.android->bottom_edge_cursor)
+ android_free_cursor (f->output_data.android->bottom_edge_cursor);
+ if (f->output_data.android->bottom_left_corner_cursor)
+ android_free_cursor (f->output_data.android->bottom_left_corner_cursor);
+
+ /* Free extra GCs allocated by android_setup_relief_colors. */
+ if (f->output_data.android->white_relief.gc)
+ {
+ android_free_gc (f->output_data.android->white_relief.gc);
+ f->output_data.android->white_relief.gc = 0;
+ }
+ if (f->output_data.android->black_relief.gc)
+ {
+ android_free_gc (f->output_data.android->black_relief.gc);
+ f->output_data.android->black_relief.gc = 0;
+ }
+
+ if (f == dpyinfo->focus_frame)
+ dpyinfo->focus_frame = 0;
+ if (f == dpyinfo->x_focus_event_frame)
+ dpyinfo->x_focus_event_frame = 0;
+ if (f == dpyinfo->highlight_frame)
+ dpyinfo->highlight_frame = 0;
+ if (f == hlinfo->mouse_face_mouse_frame)
+ reset_mouse_highlight (hlinfo);
+
+ /* These two need to be freed now that they are used to compute the
+ mouse position, I think. */
+ if (f == dpyinfo->last_mouse_motion_frame)
+ dpyinfo->last_mouse_motion_frame = NULL;
+ if (f == dpyinfo->last_mouse_frame)
+ dpyinfo->last_mouse_frame = NULL;
+
+ /* Free all tool presses currently active on this frame. */
+ next = FRAME_OUTPUT_DATA (f)->touch_points;
+ while (next)
+ {
+ last = next;
+ next = next->next;
+ xfree (last);
+ }
+
+ /* Clear this in case unblock_input reads events. */
+ FRAME_OUTPUT_DATA (f)->touch_points = NULL;
+
+ unblock_input ();
+}
+
+static void
+android_delete_frame (struct frame *f)
+{
+ android_free_frame_resources (f);
+ xfree (f->output_data.android);
+ f->output_data.android = NULL;
+}
+
+static void
+android_delete_terminal (struct terminal *terminal)
+{
+ error ("Cannot terminate connection to Android display server");
+}
+
+
+
+/* RIF functions. */
+
+static void
+android_scroll_run (struct window *w, struct run *run)
+{
+ struct frame *f = XFRAME (w->frame);
+ int x, y, width, height, from_y, to_y, bottom_y;
+
+ /* Get frame-relative bounding box of the text display area of W,
+ without mode lines. Include in this box the left and right
+ fringe of W. */
+ window_box (w, ANY_AREA, &x, &y, &width, &height);
+
+ from_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->current_y);
+ to_y = WINDOW_TO_FRAME_PIXEL_Y (w, run->desired_y);
+ bottom_y = y + height;
+
+ if (to_y < from_y)
+ {
+ /* Scrolling up. Make sure we don't copy part of the mode
+ line at the bottom. */
+ if (from_y + run->height > bottom_y)
+ height = bottom_y - from_y;
+ else
+ height = run->height;
+ }
+ else
+ {
+ /* Scrolling down. Make sure we don't copy over the mode line.
+ at the bottom. */
+ if (to_y + run->height > bottom_y)
+ height = bottom_y - to_y;
+ else
+ height = run->height;
+ }
+
+ block_input ();
+
+ /* Cursor off. Will be switched on again in gui_update_window_end. */
+ gui_clear_cursor (w);
+
+ /* To avoid sequence point problems, make sure to only call
+ FRAME_ANDROID_DRAWABLE once. */
+ android_copy_area (FRAME_ANDROID_DRAWABLE (f),
+ FRAME_ANDROID_WINDOW (f),
+ f->output_data.android->normal_gc,
+ x, from_y, width, height, x, to_y);
+
+ unblock_input ();
+}
+
+static void
+android_after_update_window_line (struct window *w, struct glyph_row *desired_row)
+{
+ eassert (w);
+
+ if (!desired_row->mode_line_p && !w->pseudo_window_p)
+ desired_row->redraw_fringe_bitmaps_p = true;
+}
+
+static void
+android_flip_and_flush (struct frame *f)
+{
+ block_input ();
+
+ if (FRAME_ANDROID_NEED_BUFFER_FLIP (f))
+ show_back_buffer (f);
+
+ /* The frame is complete again as its contents were just
+ flushed. */
+ FRAME_ANDROID_COMPLETE_P (f) = true;
+ unblock_input ();
+}
+
+static void
+android_clear_rectangle (struct frame *f, struct android_gc *gc, int x,
+ int y, int width, int height)
+{
+ struct android_gc_values xgcv;
+
+ android_get_gc_values (gc, (ANDROID_GC_BACKGROUND
+ | ANDROID_GC_FOREGROUND),
+ &xgcv);
+ android_set_foreground (gc, xgcv.background);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ x, y, width, height);
+ android_set_foreground (gc, xgcv.foreground);
+}
+
+static void
+android_reset_clip_rectangles (struct frame *f, struct android_gc *gc)
+{
+ android_set_clip_mask (gc, ANDROID_NONE);
+}
+
+static void
+android_clip_to_row (struct window *w, struct glyph_row *row,
+ enum glyph_row_area area, struct android_gc *gc)
+{
+ struct android_rectangle clip_rect;
+ int window_x, window_y, window_width;
+
+ window_box (w, area, &window_x, &window_y, &window_width, 0);
+
+ clip_rect.x = window_x;
+ clip_rect.y = WINDOW_TO_FRAME_PIXEL_Y (w, max (0, row->y));
+ clip_rect.y = max (clip_rect.y, window_y);
+ clip_rect.width = window_width;
+ clip_rect.height = row->visible_height;
+
+ android_set_clip_rectangles (gc, 0, 0, &clip_rect, 1);
+}
+
+static void
+android_draw_fringe_bitmap (struct window *w, struct glyph_row *row,
+ struct draw_fringe_bitmap_params *p)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct android_gc *gc = f->output_data.android->normal_gc;
+ struct face *face = p->face;
+
+ /* Must clip because of partially visible lines. */
+ android_clip_to_row (w, row, ANY_AREA, gc);
+
+ if (p->bx >= 0 && !p->overlay_p)
+ {
+ /* In case the same realized face is used for fringes and for
+ something displayed in the text (e.g. face `region' on
+ mono-displays, the fill style may have been changed to
+ ANDROID_FILL_SOLID in
+ android_draw_glyph_string_background. */
+ if (face->stipple)
+ {
+ android_set_fill_style (face->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), face->gc,
+ p->bx, p->by, p->nx, p->ny);
+ android_set_fill_style (face->gc, ANDROID_FILL_SOLID);
+
+ row->stipple_p = true;
+ }
+ else
+ {
+ android_set_background (face->gc, face->background);
+ android_clear_rectangle (f, face->gc, p->bx, p->by, p->nx, p->ny);
+ android_set_foreground (face->gc, face->foreground);
+ }
+ }
+
+ if (p->which)
+ {
+ android_drawable drawable;
+ char *bits;
+ android_pixmap pixmap, clipmask;
+ struct android_gc_values gcv;
+ unsigned long background, cursor_pixel;
+ int depth;
+
+ drawable = FRAME_ANDROID_DRAWABLE (f);
+ clipmask = ANDROID_NONE;
+ background = face->background;
+ cursor_pixel = f->output_data.android->cursor_pixel;
+ depth = FRAME_DISPLAY_INFO (f)->n_planes;
+
+ if (p->wd > 8)
+ bits = (char *) (p->bits + p->dh);
+ else
+ bits = (char *) p->bits + p->dh;
+
+ pixmap = android_create_pixmap_from_bitmap_data (bits, p->wd, p->h,
+ (p->cursor_p
+ ? (p->overlay_p
+ ? face->background
+ : cursor_pixel)
+ : face->foreground),
+ background, depth);
+
+ if (p->overlay_p)
+ {
+ clipmask = android_create_pixmap_from_bitmap_data (bits, p->wd, p->h,
+ 1, 0, 1);
+
+ gcv.clip_mask = clipmask;
+ gcv.clip_x_origin = p->x;
+ gcv.clip_y_origin = p->y;
+ android_change_gc (gc, (ANDROID_GC_CLIP_MASK
+ | ANDROID_GC_CLIP_X_ORIGIN
+ | ANDROID_GC_CLIP_Y_ORIGIN),
+ &gcv);
+ }
+
+ android_copy_area (pixmap, drawable, gc, 0, 0, p->wd, p->h,
+ p->x, p->y);
+ android_free_pixmap (pixmap);
+
+ if (p->overlay_p)
+ {
+ gcv.clip_mask = ANDROID_NONE;
+ android_change_gc (gc, ANDROID_GC_CLIP_MASK, &gcv);
+ android_free_pixmap (clipmask);
+ }
+ }
+
+ android_reset_clip_rectangles (f, gc);
+}
+
+/* Set S->gc to a suitable GC for drawing glyph string S in cursor
+ face. */
+
+static void
+android_set_cursor_gc (struct glyph_string *s)
+{
+ if (s->font == FRAME_FONT (s->f)
+ && s->face->background == FRAME_BACKGROUND_PIXEL (s->f)
+ && s->face->foreground == FRAME_FOREGROUND_PIXEL (s->f)
+ && !s->cmp)
+ s->gc = s->f->output_data.android->cursor_gc;
+ else
+ {
+ /* Cursor on non-default face: must merge. */
+ struct android_gc_values xgcv;
+ unsigned long mask;
+
+ xgcv.background = s->f->output_data.android->cursor_pixel;
+ xgcv.foreground = s->face->background;
+
+ /* If the glyph would be invisible, try a different foreground. */
+ if (xgcv.foreground == xgcv.background)
+ xgcv.foreground = s->face->foreground;
+ if (xgcv.foreground == xgcv.background)
+ xgcv.foreground = s->f->output_data.android->cursor_foreground_pixel;
+ if (xgcv.foreground == xgcv.background)
+ xgcv.foreground = s->face->foreground;
+
+ /* Make sure the cursor is distinct from text in this face. */
+ if (xgcv.background == s->face->background
+ && xgcv.foreground == s->face->foreground)
+ {
+ xgcv.background = s->face->foreground;
+ xgcv.foreground = s->face->background;
+ }
+
+ mask = (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND);
+
+ if (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc)
+ android_change_gc (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc,
+ mask, &xgcv);
+ else
+ FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc
+ = android_create_gc (mask, &xgcv);
+
+ s->gc = FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc;
+ }
+}
+
+
+/* Set up S->gc of glyph string S for drawing text in mouse face. */
+
+static void
+android_set_mouse_face_gc (struct glyph_string *s)
+{
+ if (s->font == s->face->font)
+ s->gc = s->face->gc;
+ else
+ {
+ /* Otherwise construct scratch_cursor_gc with values from FACE
+ except for FONT. */
+ struct android_gc_values xgcv;
+ unsigned long mask;
+
+ xgcv.background = s->face->background;
+ xgcv.foreground = s->face->foreground;
+
+ mask = (ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND);
+
+ if (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc)
+ android_change_gc (FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc,
+ mask, &xgcv);
+ else
+ FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc
+ = android_create_gc (mask, &xgcv);
+
+ s->gc = FRAME_DISPLAY_INFO (s->f)->scratch_cursor_gc;
+ }
+
+ eassert (s->gc != 0);
+}
+
+
+/* Set S->gc of glyph string S to a GC suitable for drawing a mode line.
+ Faces to use in the mode line have already been computed when the
+ matrix was built, so there isn't much to do, here. */
+
+static void
+android_set_mode_line_face_gc (struct glyph_string *s)
+{
+ s->gc = s->face->gc;
+}
+
+/* Set S->gc of glyph string S for drawing that glyph string. Set
+ S->stippled_p to a non-zero value if the face of S has a stipple
+ pattern. */
+
+static void
+android_set_glyph_string_gc (struct glyph_string *s)
+{
+ prepare_face_for_display (s->f, s->face);
+
+ if (s->hl == DRAW_NORMAL_TEXT)
+ {
+ s->gc = s->face->gc;
+ s->stippled_p = s->face->stipple != 0;
+ }
+ else if (s->hl == DRAW_INVERSE_VIDEO)
+ {
+ android_set_mode_line_face_gc (s);
+ s->stippled_p = s->face->stipple != 0;
+ }
+ else if (s->hl == DRAW_CURSOR)
+ {
+ android_set_cursor_gc (s);
+ s->stippled_p = false;
+ }
+ else if (s->hl == DRAW_MOUSE_FACE)
+ {
+ android_set_mouse_face_gc (s);
+ s->stippled_p = s->face->stipple != 0;
+ }
+ else if (s->hl == DRAW_IMAGE_RAISED
+ || s->hl == DRAW_IMAGE_SUNKEN)
+ {
+ s->gc = s->face->gc;
+ s->stippled_p = s->face->stipple != 0;
+ }
+ else
+ emacs_abort ();
+
+ /* GC must have been set. */
+ eassert (s->gc != 0);
+}
+
+
+/* Set clipping for output of glyph string S. S may be part of a mode
+ line or menu if we don't have X toolkit support. */
+
+static void
+android_set_glyph_string_clipping (struct glyph_string *s)
+{
+ struct android_rectangle *r = s->clip;
+ int n = get_glyph_string_clip_rects (s, r, 2);
+
+ if (n > 0)
+ android_set_clip_rectangles (s->gc, 0, 0, r, n);
+ s->num_clips = n;
+}
+
+
+/* Set SRC's clipping for output of glyph string DST. This is called
+ when we are drawing DST's left_overhang or right_overhang only in
+ the area of SRC. */
+
+static void
+android_set_glyph_string_clipping_exactly (struct glyph_string *src,
+ struct glyph_string *dst)
+{
+ struct android_rectangle r;
+
+ r.x = src->x;
+ r.width = src->width;
+ r.y = src->y;
+ r.height = src->height;
+ dst->clip[0] = r;
+ dst->num_clips = 1;
+ android_set_clip_rectangles (dst->gc, 0, 0, &r, 1);
+}
+
+static void
+android_compute_glyph_string_overhangs (struct glyph_string *s)
+{
+ if (s->cmp == NULL
+ && (s->first_glyph->type == CHAR_GLYPH
+ || s->first_glyph->type == COMPOSITE_GLYPH))
+ {
+ struct font_metrics metrics;
+
+ if (s->first_glyph->type == CHAR_GLYPH)
+ {
+ struct font *font = s->font;
+ font->driver->text_extents (font, s->char2b, s->nchars, &metrics);
+ }
+ else
+ {
+ Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+
+ composition_gstring_width (gstring, s->cmp_from, s->cmp_to, &metrics);
+ }
+ s->right_overhang = (metrics.rbearing > metrics.width
+ ? metrics.rbearing - metrics.width : 0);
+ s->left_overhang = metrics.lbearing < 0 ? - metrics.lbearing : 0;
+ }
+ else if (s->cmp)
+ {
+ s->right_overhang = s->cmp->rbearing - s->cmp->pixel_width;
+ s->left_overhang = - s->cmp->lbearing;
+ }
+}
+
+static void
+android_clear_glyph_string_rect (struct glyph_string *s, int x, int y,
+ int w, int h)
+{
+ android_clear_rectangle (s->f, s->gc, x, y, w, h);
+}
+
+static void
+android_draw_glyph_string_background (struct glyph_string *s, bool force_p)
+{
+ /* Nothing to do if background has already been drawn or if it
+ shouldn't be drawn in the first place. */
+ if (!s->background_filled_p)
+ {
+ int box_line_width = max (s->face->box_horizontal_line_width, 0);
+
+ if (s->stippled_p)
+ {
+ /* Fill background with a stipple pattern. */
+ android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, s->y + box_line_width,
+ s->background_width,
+ s->height - 2 * box_line_width);
+ android_set_fill_style (s->gc, ANDROID_FILL_SOLID);
+ s->background_filled_p = true;
+ }
+ else if (FONT_HEIGHT (s->font) < s->height - 2 * box_line_width
+ /* When xdisp.c ignores FONT_HEIGHT, we cannot trust
+ font dimensions, since the actual glyphs might be
+ much smaller. So in that case we always clear the
+ rectangle with background color. */
+ || FONT_TOO_HIGH (s->font)
+ || s->font_not_found_p
+ || s->extends_to_end_of_line_p
+ || force_p)
+ {
+ android_clear_glyph_string_rect (s, s->x, s->y + box_line_width,
+ s->background_width,
+ s->height - 2 * box_line_width);
+ s->background_filled_p = true;
+ }
+ }
+}
+
+static void
+android_fill_triangle (struct frame *f, struct android_gc *gc,
+ struct android_point point1,
+ struct android_point point2,
+ struct android_point point3)
+{
+ struct android_point abc[3];
+
+ abc[0] = point1;
+ abc[1] = point2;
+ abc[2] = point3;
+
+ android_fill_polygon (FRAME_ANDROID_DRAWABLE (f),
+ gc, abc, 3, ANDROID_CONVEX,
+ ANDROID_COORD_MODE_ORIGIN);
+}
+
+static struct android_point
+android_make_point (int x, int y)
+{
+ struct android_point pt;
+
+ pt.x = x;
+ pt.y = y;
+
+ return pt;
+}
+
+static bool
+android_inside_rect_p (struct android_rectangle *rects, int nrects, int x,
+ int y)
+{
+ int i;
+
+ for (i = 0; i < nrects; ++i)
+ {
+ if (x >= rects[i].x && y >= rects[i].y
+ && x < rects[i].x + rects[i].width
+ && y < rects[i].y + rects[i].height)
+ return true;
+ }
+
+ return false;
+}
+
+static void
+android_clear_point (struct frame *f, struct android_gc *gc,
+ int x, int y)
+{
+ struct android_gc_values xgcv;
+
+ android_get_gc_values (gc, ANDROID_GC_BACKGROUND | ANDROID_GC_FOREGROUND,
+ &xgcv);
+ android_set_foreground (gc, xgcv.background);
+ android_draw_point (FRAME_ANDROID_DRAWABLE (f), gc, x, y);
+ android_set_foreground (gc, xgcv.foreground);
+}
+
+static void
+android_draw_relief_rect (struct frame *f, int left_x, int top_y, int right_x,
+ int bottom_y, int hwidth, int vwidth, bool raised_p,
+ bool top_p, bool bot_p, bool left_p, bool right_p,
+ struct android_rectangle *clip_rect)
+{
+ struct android_gc *gc, *white_gc, *black_gc, *normal_gc;
+ android_drawable drawable;
+
+ /* This code is more complicated than it has to be, because of two
+ minor hacks to make the boxes look nicer: (i) if width > 1, draw
+ the outermost line using the black relief. (ii) Omit the four
+ corner pixels. */
+
+ white_gc = f->output_data.android->white_relief.gc;
+ black_gc = f->output_data.android->black_relief.gc;
+ normal_gc = f->output_data.android->normal_gc;
+
+ drawable = FRAME_ANDROID_DRAWABLE (f);
+
+ android_set_clip_rectangles (white_gc, 0, 0, clip_rect, 1);
+ android_set_clip_rectangles (black_gc, 0, 0, clip_rect, 1);
+
+ if (raised_p)
+ gc = white_gc;
+ else
+ gc = black_gc;
+
+ /* Draw lines. */
+
+ if (top_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
+ right_x - left_x + 1, hwidth);
+
+ if (left_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x, top_y,
+ vwidth, bottom_y - top_y + 1);
+
+ if (raised_p)
+ gc = black_gc;
+ else
+ gc = white_gc;
+
+ if (bot_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, left_x,
+ bottom_y - hwidth + 1,
+ right_x - left_x + 1, hwidth);
+
+ if (right_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc,
+ right_x - vwidth + 1,
+ top_y, vwidth, bottom_y - top_y + 1);
+
+ /* Draw corners. */
+
+ if (bot_p && left_p)
+ android_fill_triangle (f, raised_p ? white_gc : black_gc,
+ android_make_point (left_x, bottom_y - hwidth),
+ android_make_point (left_x + vwidth,
+ bottom_y - hwidth),
+ android_make_point (left_x, bottom_y));
+
+ if (top_p && right_p)
+ android_fill_triangle (f, raised_p ? white_gc : black_gc,
+ android_make_point (right_x - vwidth, top_y),
+ android_make_point (right_x, top_y),
+ android_make_point (right_x - vwidth,
+ top_y + hwidth));
+
+ /* Draw outer line. */
+
+ if (top_p && left_p && bot_p && right_p
+ && hwidth > 1 && vwidth > 1)
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ black_gc, left_x, top_y,
+ right_x - left_x, bottom_y - top_y);
+ else
+ {
+ if (top_p && hwidth > 1)
+ android_draw_line (drawable, black_gc, left_x, top_y,
+ right_x + 1, top_y);
+
+ if (bot_p && hwidth > 1)
+ android_draw_line (drawable, black_gc, left_x, bottom_y,
+ right_x + 1, bottom_y);
+
+ if (left_p && vwidth > 1)
+ android_draw_line (drawable, black_gc, left_x, top_y,
+ left_x, bottom_y + 1);
+
+ if (right_p && vwidth > 1)
+ android_draw_line (drawable, black_gc, right_x, top_y,
+ right_x, bottom_y + 1);
+ }
+
+ /* Erase corners. */
+
+ if (hwidth > 1 && vwidth > 1)
+ {
+ if (left_p && top_p && android_inside_rect_p (clip_rect, 1,
+ left_x, top_y))
+ android_clear_point (f, normal_gc, left_x, top_y);
+
+ if (left_p && bot_p && android_inside_rect_p (clip_rect, 1,
+ left_x, bottom_y))
+ android_clear_point (f, normal_gc, left_x, bottom_y);
+
+ if (right_p && top_p && android_inside_rect_p (clip_rect, 1,
+ right_x, top_y))
+ android_clear_point (f, normal_gc, right_x, top_y);
+
+ if (right_p && bot_p && android_inside_rect_p (clip_rect, 1,
+ right_x, bottom_y))
+ android_clear_point (f, normal_gc, right_x, bottom_y);
+ }
+
+ android_reset_clip_rectangles (f, white_gc);
+ android_reset_clip_rectangles (f, black_gc);
+}
+
+static void
+android_draw_box_rect (struct glyph_string *s,
+ int left_x, int top_y, int right_x, int bottom_y,
+ int hwidth, int vwidth, bool left_p, bool right_p,
+ struct android_rectangle *clip_rect)
+{
+ struct android_gc_values xgcv;
+
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->box_color);
+ android_set_clip_rectangles (s->gc, 0, 0, clip_rect, 1);
+
+ /* Top. */
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
+ top_y, right_x - left_x + 1, hwidth);
+
+ /* Left. */
+ if (left_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
+ top_y, vwidth, bottom_y - top_y + 1);
+
+ /* Bottom. */
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, left_x,
+ bottom_y - hwidth + 1, right_x - left_x + 1,
+ hwidth);
+
+ /* Right. */
+ if (right_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ right_x - vwidth + 1, top_y, vwidth,
+ bottom_y - top_y + 1);
+
+ android_set_foreground (s->gc, xgcv.foreground);
+ android_reset_clip_rectangles (s->f, s->gc);
+}
+
+#define HIGHLIGHT_COLOR_DARK_BOOST_LIMIT 48000
+
+static bool
+android_alloc_lighter_color (struct frame *f, unsigned long *pixel,
+ double factor, int delta)
+{
+ Emacs_Color color, new;
+ long bright;
+ bool success_p;
+
+ /* Get RGB color values. */
+ color.pixel = *pixel;
+ android_query_colors (f, &color, 1);
+
+ /* Change RGB values by specified FACTOR. Avoid overflow! */
+ eassert (factor >= 0);
+ new.red = min (0xffff, factor * color.red);
+ new.green = min (0xffff, factor * color.green);
+ new.blue = min (0xffff, factor * color.blue);
+
+ /* Calculate brightness of COLOR. */
+ bright = (2 * color.red + 3 * color.green + color.blue) / 6;
+
+ /* We only boost colors that are darker than
+ HIGHLIGHT_COLOR_DARK_BOOST_LIMIT. */
+ if (bright < HIGHLIGHT_COLOR_DARK_BOOST_LIMIT)
+ /* Make an additive adjustment to NEW, because it's dark enough so
+ that scaling by FACTOR alone isn't enough. */
+ {
+ /* How far below the limit this color is (0 - 1, 1 being darker). */
+ double dimness = 1 - (double) bright / HIGHLIGHT_COLOR_DARK_BOOST_LIMIT;
+ /* The additive adjustment. */
+ int min_delta = delta * dimness * factor / 2;
+
+ if (factor < 1)
+ {
+ new.red = max (0, new.red - min_delta);
+ new.green = max (0, new.green - min_delta);
+ new.blue = max (0, new.blue - min_delta);
+ }
+ else
+ {
+ new.red = min (0xffff, min_delta + new.red);
+ new.green = min (0xffff, min_delta + new.green);
+ new.blue = min (0xffff, min_delta + new.blue);
+ }
+ }
+
+ /* Try to allocate the color. */
+ success_p = android_alloc_nearest_color (f, &new);
+
+ if (success_p)
+ {
+ if (new.pixel == *pixel)
+ {
+ /* If we end up with the same color as before, try adding
+ delta to the RGB values. */
+ new.red = min (0xffff, delta + color.red);
+ new.green = min (0xffff, delta + color.green);
+ new.blue = min (0xffff, delta + color.blue);
+ success_p = android_alloc_nearest_color (f, &new);
+ }
+ else
+ success_p = true;
+
+ *pixel = new.pixel;
+ }
+
+ return success_p;
+}
+
+/* Set up the foreground color for drawing relief lines of glyph
+ string S. RELIEF is a pointer to a struct relief containing the GC
+ with which lines will be drawn. Use a color that is FACTOR or
+ DELTA lighter or darker than the relief's background which is found
+ in S->f->output_data.android->relief_background. If such a color
+ cannot be allocated, use DEFAULT_PIXEL, instead. */
+
+static void
+android_setup_relief_color (struct frame *f, struct relief *relief,
+ double factor, int delta,
+ unsigned long default_pixel)
+{
+ struct android_gc_values xgcv;
+ struct android_output *di = f->output_data.android;
+ unsigned long mask = ANDROID_GC_FOREGROUND;
+ unsigned long pixel;
+ unsigned long background = di->relief_background;
+ struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+
+ if (relief->gc && relief->pixel != -1)
+ relief->pixel = -1;
+
+ /* Allocate new color. */
+ xgcv.foreground = default_pixel;
+ pixel = background;
+
+ if (dpyinfo->n_planes != 1
+ && android_alloc_lighter_color (f, &pixel, factor, delta))
+ xgcv.foreground = relief->pixel = pixel;
+
+ if (relief->gc == 0)
+ relief->gc = android_create_gc (mask, &xgcv);
+ else
+ android_change_gc (relief->gc, mask, &xgcv);
+}
+
+/* Set up colors for the relief lines around glyph string S. */
+
+static void
+android_setup_relief_colors (struct glyph_string *s)
+{
+ struct android_output *di;
+ unsigned long color;
+
+ di = s->f->output_data.android;
+
+ if (s->face->use_box_color_for_shadows_p)
+ color = s->face->box_color;
+ else if (s->first_glyph->type == IMAGE_GLYPH
+ && s->img->pixmap
+ && !IMAGE_BACKGROUND_TRANSPARENT (s->img, s->f, 0))
+ color = IMAGE_BACKGROUND (s->img, s->f, 0);
+ else
+ {
+ struct android_gc_values xgcv;
+
+ /* Get the background color of the face. */
+ android_get_gc_values (s->gc, ANDROID_GC_BACKGROUND, &xgcv);
+ color = xgcv.background;
+ }
+
+ if (di->white_relief.gc == 0
+ || color != di->relief_background)
+ {
+ di->relief_background = color;
+ android_setup_relief_color (s->f, &di->white_relief, 1.2, 0x8000,
+ WHITE_PIX_DEFAULT (s->f));
+ android_setup_relief_color (s->f, &di->black_relief, 0.6, 0x4000,
+ BLACK_PIX_DEFAULT (s->f));
+ }
+}
+
+static void
+android_draw_glyph_string_box (struct glyph_string *s)
+{
+ int hwidth, vwidth, left_x, right_x, top_y, bottom_y, last_x;
+ bool raised_p, left_p, right_p;
+ struct glyph *last_glyph;
+ struct android_rectangle clip_rect;
+
+ last_x = ((s->row->full_width_p && !s->w->pseudo_window_p)
+ ? WINDOW_RIGHT_EDGE_X (s->w)
+ : window_box_right (s->w, s->area));
+
+ /* The glyph that may have a right box line. For static
+ compositions and images, the right-box flag is on the first glyph
+ of the glyph string; for other types it's on the last glyph. */
+ if (s->cmp || s->img)
+ last_glyph = s->first_glyph;
+ else if (s->first_glyph->type == COMPOSITE_GLYPH
+ && s->first_glyph->u.cmp.automatic)
+ {
+ /* For automatic compositions, we need to look up the last glyph
+ in the composition. */
+ struct glyph *end = s->row->glyphs[s->area] + s->row->used[s->area];
+ struct glyph *g = s->first_glyph;
+ for (last_glyph = g++;
+ g < end && g->u.cmp.automatic && g->u.cmp.id == s->cmp_id
+ && g->slice.cmp.to < s->cmp_to;
+ last_glyph = g++)
+ ;
+ }
+ else
+ last_glyph = s->first_glyph + s->nchars - 1;
+
+ vwidth = eabs (s->face->box_vertical_line_width);
+ hwidth = eabs (s->face->box_horizontal_line_width);
+ raised_p = s->face->box == FACE_RAISED_BOX;
+ left_x = s->x;
+ right_x = (s->row->full_width_p && s->extends_to_end_of_line_p
+ ? last_x - 1
+ : min (last_x, s->x + s->background_width) - 1);
+ top_y = s->y;
+ bottom_y = top_y + s->height - 1;
+
+ left_p = (s->first_glyph->left_box_line_p
+ || (s->hl == DRAW_MOUSE_FACE
+ && (s->prev == NULL
+ || s->prev->hl != s->hl)));
+ right_p = (last_glyph->right_box_line_p
+ || (s->hl == DRAW_MOUSE_FACE
+ && (s->next == NULL
+ || s->next->hl != s->hl)));
+
+ get_glyph_string_clip_rect (s, &clip_rect);
+
+ if (s->face->box == FACE_SIMPLE_BOX)
+ android_draw_box_rect (s, left_x, top_y, right_x, bottom_y, hwidth,
+ vwidth, left_p, right_p, &clip_rect);
+ else
+ {
+ android_setup_relief_colors (s);
+ android_draw_relief_rect (s->f, left_x, top_y, right_x, bottom_y, hwidth,
+ vwidth, raised_p, true, true, left_p, right_p,
+ &clip_rect);
+ }
+}
+
+static void
+android_draw_glyph_string_bg_rect (struct glyph_string *s, int x, int y,
+ int w, int h)
+{
+ if (s->stippled_p)
+ {
+ /* Fill background with a stipple pattern. */
+ android_set_fill_style (s->gc, ANDROID_FILL_OPAQUE_STIPPLED);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x,
+ y, w, h);
+ android_set_fill_style (s->gc, ANDROID_FILL_SOLID);
+ }
+ else
+ android_clear_glyph_string_rect (s, x, y, w, h);
+}
+
+static void
+android_draw_image_relief (struct glyph_string *s)
+{
+ int x1, y1, thick;
+ bool raised_p, top_p, bot_p, left_p, right_p;
+ int extra_x, extra_y;
+ struct android_rectangle r;
+ int x = s->x;
+ int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+ /* If first glyph of S has a left box line, start drawing it to the
+ right of that line. */
+ if (s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p
+ && s->slice.x == 0)
+ x += max (s->face->box_vertical_line_width, 0);
+
+ /* If there is a margin around the image, adjust x- and y-position
+ by that margin. */
+ if (s->slice.x == 0)
+ x += s->img->hmargin;
+ if (s->slice.y == 0)
+ y += s->img->vmargin;
+
+ if (s->hl == DRAW_IMAGE_SUNKEN
+ || s->hl == DRAW_IMAGE_RAISED)
+ {
+ if (s->face->id == TAB_BAR_FACE_ID)
+ thick = (tab_bar_button_relief < 0
+ ? DEFAULT_TAB_BAR_BUTTON_RELIEF
+ : min (tab_bar_button_relief, 1000000));
+ else
+ thick = (tool_bar_button_relief < 0
+ ? DEFAULT_TOOL_BAR_BUTTON_RELIEF
+ : min (tool_bar_button_relief, 1000000));
+ raised_p = s->hl == DRAW_IMAGE_RAISED;
+ }
+ else
+ {
+ thick = eabs (s->img->relief);
+ raised_p = s->img->relief > 0;
+ }
+
+ x1 = x + s->slice.width - 1;
+ y1 = y + s->slice.height - 1;
+
+ extra_x = extra_y = 0;
+ if (s->face->id == TAB_BAR_FACE_ID)
+ {
+ if (CONSP (Vtab_bar_button_margin)
+ && FIXNUMP (XCAR (Vtab_bar_button_margin))
+ && FIXNUMP (XCDR (Vtab_bar_button_margin)))
+ {
+ extra_x = XFIXNUM (XCAR (Vtab_bar_button_margin)) - thick;
+ extra_y = XFIXNUM (XCDR (Vtab_bar_button_margin)) - thick;
+ }
+ else if (FIXNUMP (Vtab_bar_button_margin))
+ extra_x = extra_y = XFIXNUM (Vtab_bar_button_margin) - thick;
+ }
+
+ if (s->face->id == TOOL_BAR_FACE_ID)
+ {
+ if (CONSP (Vtool_bar_button_margin)
+ && FIXNUMP (XCAR (Vtool_bar_button_margin))
+ && FIXNUMP (XCDR (Vtool_bar_button_margin)))
+ {
+ extra_x = XFIXNUM (XCAR (Vtool_bar_button_margin));
+ extra_y = XFIXNUM (XCDR (Vtool_bar_button_margin));
+ }
+ else if (FIXNUMP (Vtool_bar_button_margin))
+ extra_x = extra_y = XFIXNUM (Vtool_bar_button_margin);
+ }
+
+ top_p = bot_p = left_p = right_p = false;
+
+ if (s->slice.x == 0)
+ x -= thick + extra_x, left_p = true;
+ if (s->slice.y == 0)
+ y -= thick + extra_y, top_p = true;
+ if (s->slice.x + s->slice.width == s->img->width)
+ x1 += thick + extra_x, right_p = true;
+ if (s->slice.y + s->slice.height == s->img->height)
+ y1 += thick + extra_y, bot_p = true;
+
+ android_setup_relief_colors (s);
+ get_glyph_string_clip_rect (s, &r);
+ android_draw_relief_rect (s->f, x, y, x1, y1, thick, thick, raised_p,
+ top_p, bot_p, left_p, right_p, &r);
+}
+
+static void
+android_draw_image_foreground (struct glyph_string *s)
+{
+ int x = s->x;
+ int y = s->ybase - image_ascent (s->img, s->face, &s->slice);
+
+ /* If first glyph of S has a left box line, start drawing it to the
+ right of that line. */
+ if (s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p
+ && s->slice.x == 0)
+ x += max (s->face->box_vertical_line_width, 0);
+
+ /* If there is a margin around the image, adjust x- and y-position
+ by that margin. */
+ if (s->slice.x == 0)
+ x += s->img->hmargin;
+ if (s->slice.y == 0)
+ y += s->img->vmargin;
+
+ if (s->img->pixmap)
+ {
+ unsigned long mask = (ANDROID_GC_CLIP_MASK
+ | ANDROID_GC_CLIP_X_ORIGIN
+ | ANDROID_GC_CLIP_Y_ORIGIN
+ | ANDROID_GC_FUNCTION);
+ struct android_gc_values xgcv;
+ struct android_rectangle clip_rect, image_rect, r;
+
+ xgcv.clip_mask = s->img->mask;
+ xgcv.clip_x_origin = x - s->slice.x;
+ xgcv.clip_y_origin = y - s->slice.y;
+ xgcv.function = ANDROID_GC_COPY;
+ android_change_gc (s->gc, mask, &xgcv);
+
+ get_glyph_string_clip_rect (s, &clip_rect);
+ image_rect.x = x;
+ image_rect.y = y;
+ image_rect.width = s->slice.width;
+ image_rect.height = s->slice.height;
+
+ if (gui_intersect_rectangles (&clip_rect, &image_rect, &r))
+ android_copy_area (s->img->pixmap,
+ FRAME_ANDROID_DRAWABLE (s->f),
+ s->gc, s->slice.x + r.x - x,
+ s->slice.y + r.y - y,
+ r.width, r.height, r.x, r.y);
+
+ /* When the image has a mask, we can expect that at least part
+ of a mouse highlight or a block cursor will be visible. If
+ the image doesn't have a mask, make a block cursor visible by
+ drawing a rectangle around the image. I believe it's looking
+ better if we do nothing here for mouse-face. */
+ if (s->hl == DRAW_CURSOR && !s->img->mask)
+ {
+ int relief = eabs (s->img->relief);
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ x - relief, y - relief,
+ s->slice.width + relief*2 - 1,
+ s->slice.height + relief*2 - 1);
+ }
+
+ android_set_clip_mask (s->gc, ANDROID_NONE);
+ }
+ else
+ /* Draw a rectangle if image could not be loaded. */
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc, x, y,
+ s->slice.width - 1, s->slice.height - 1);
+}
+
+static void
+android_draw_image_glyph_string (struct glyph_string *s)
+{
+ int box_line_hwidth = max (s->face->box_vertical_line_width, 0);
+ int box_line_vwidth = max (s->face->box_horizontal_line_width, 0);
+ int height;
+
+ height = s->height;
+ if (s->slice.y == 0)
+ height -= box_line_vwidth;
+ if (s->slice.y + s->slice.height >= s->img->height)
+ height -= box_line_vwidth;
+
+ /* Fill background with face under the image. Do it only if row is
+ taller than image or if image has a clip mask to reduce
+ flickering. */
+ s->stippled_p = s->face->stipple != 0;
+ if (height > s->slice.height
+ || s->img->hmargin
+ || s->img->vmargin
+ || s->img->mask
+ || s->img->pixmap == 0
+ || s->width != s->background_width)
+ {
+ if (s->stippled_p)
+ s->row->stipple_p = true;
+
+ int x = s->x;
+ int y = s->y;
+ int width = s->background_width;
+
+ if (s->first_glyph->left_box_line_p
+ && s->slice.x == 0)
+ {
+ x += box_line_hwidth;
+ width -= box_line_hwidth;
+ }
+
+ if (s->slice.y == 0)
+ y += box_line_vwidth;
+
+ android_draw_glyph_string_bg_rect (s, x, y, width, height);
+
+ s->background_filled_p = true;
+ }
+
+ /* Draw the foreground. */
+ android_draw_image_foreground (s);
+ android_set_glyph_string_clipping (s);
+
+ /* If we must draw a relief around the image, do it. */
+ if (s->img->relief
+ || s->hl == DRAW_IMAGE_RAISED
+ || s->hl == DRAW_IMAGE_SUNKEN)
+ android_draw_image_relief (s);
+}
+
+static void
+android_draw_stretch_glyph_string (struct glyph_string *s)
+{
+ eassert (s->first_glyph->type == STRETCH_GLYPH);
+
+ if (s->hl == DRAW_CURSOR && !x_stretch_cursor_p)
+ {
+ /* If `x-stretch-cursor' is nil, don't draw a block cursor as
+ wide as the stretch glyph. */
+ int width, background_width = s->background_width;
+ int x = s->x;
+
+ if (!s->row->reversed_p)
+ {
+ int left_x = window_box_left_offset (s->w, TEXT_AREA);
+
+ if (x < left_x)
+ {
+ background_width -= left_x - x;
+ x = left_x;
+ }
+ }
+ else
+ {
+ /* In R2L rows, draw the cursor on the right edge of the
+ stretch glyph. */
+ int right_x = window_box_right (s->w, TEXT_AREA);
+
+ if (x + background_width > right_x)
+ background_width -= x - right_x;
+ x += background_width;
+ }
+ width = min (FRAME_COLUMN_WIDTH (s->f), background_width);
+ if (s->row->reversed_p)
+ x -= width;
+
+ /* Draw cursor. */
+ android_draw_glyph_string_bg_rect (s, x, s->y, width, s->height);
+
+ /* Clear rest using the GC of the original non-cursor face. */
+ if (width < background_width)
+ {
+ int y = s->y;
+ int w = background_width - width, h = s->height;
+ struct android_rectangle r;
+ struct android_gc *gc;
+
+ if (!s->row->reversed_p)
+ x += width;
+ else
+ x = s->x;
+ if (s->row->mouse_face_p
+ && cursor_in_mouse_face_p (s->w))
+ {
+ android_set_mouse_face_gc (s);
+ gc = s->gc;
+ }
+ else
+ gc = s->face->gc;
+
+ get_glyph_string_clip_rect (s, &r);
+ android_set_clip_rectangles (gc, 0, 0, &r, 1);
+
+ if (s->face->stipple)
+ {
+ /* Fill background with a stipple pattern. */
+ android_set_fill_style (gc, ANDROID_FILL_OPAQUE_STIPPLED);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ gc, x, y, w, h);
+ android_set_fill_style (gc, ANDROID_FILL_SOLID);
+
+ s->row->stipple_p = true;
+ }
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (gc, (ANDROID_GC_FOREGROUND
+ | ANDROID_GC_BACKGROUND),
+ &xgcv);
+ android_set_foreground (gc, xgcv.background);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ gc, x, y, w, h);
+ android_set_foreground (gc, xgcv.foreground);
+ }
+
+ android_reset_clip_rectangles (s->f, gc);
+ }
+ }
+ else if (!s->background_filled_p)
+ {
+ int background_width = s->background_width;
+ int x = s->x, text_left_x = window_box_left (s->w, TEXT_AREA);
+
+ /* Don't draw into left fringe or scrollbar area except for
+ header line and mode line. */
+ if (s->area == TEXT_AREA
+ && x < text_left_x && !s->row->mode_line_p)
+ {
+ background_width -= text_left_x - x;
+ x = text_left_x;
+ }
+
+ if (!s->row->stipple_p)
+ s->row->stipple_p = s->stippled_p;
+
+ if (background_width > 0)
+ android_draw_glyph_string_bg_rect (s, x, s->y,
+ background_width,
+ s->height);
+ }
+
+ s->background_filled_p = true;
+}
+
+static void
+android_get_scale_factor (int *scale_x, int *scale_y)
+{
+ /* This is 96 everywhere else, but 160 on Android. */
+ const int base_res = 160;
+ struct android_display_info *dpyinfo;
+
+ dpyinfo = x_display_list;
+ *scale_x = *scale_y = 1;
+
+ if (dpyinfo)
+ {
+ if (dpyinfo->resx > base_res)
+ *scale_x = floor (dpyinfo->resx / base_res);
+ if (dpyinfo->resy > base_res)
+ *scale_y = floor (dpyinfo->resy / base_res);
+ }
+}
+
+static void
+android_draw_underwave (struct glyph_string *s, int decoration_width)
+{
+ int scale_x, scale_y;
+
+ android_get_scale_factor (&scale_x, &scale_y);
+
+ int wave_height = 3 * scale_y, wave_length = 2 * scale_x;
+
+ int dx, dy, x0, y0, width, x1, y1, x2, y2, xmax;
+ bool odd;
+ struct android_rectangle wave_clip, string_clip, final_clip;
+
+ dx = wave_length;
+ dy = wave_height - 1;
+ x0 = s->x;
+ y0 = s->ybase + wave_height / 2;
+ width = decoration_width;
+ xmax = x0 + width;
+
+ /* Find and set clipping rectangle */
+
+ wave_clip.x = x0;
+ wave_clip.y = y0;
+ wave_clip.width = width;
+ wave_clip.height = wave_height;
+ get_glyph_string_clip_rect (s, &string_clip);
+
+ if (!gui_intersect_rectangles (&wave_clip, &string_clip, &final_clip))
+ return;
+
+ android_set_clip_rectangles (s->gc, 0, 0, &final_clip, 1);
+
+ /* Draw the waves */
+
+ x1 = x0 - (x0 % dx);
+ x2 = x1 + dx;
+ odd = (x1 / dx) & 1;
+ y1 = y2 = y0;
+
+ if (odd)
+ y1 += dy;
+ else
+ y2 += dy;
+
+ if (INT_MAX - dx < xmax)
+ emacs_abort ();
+
+ while (x1 <= xmax)
+ {
+ android_draw_line (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ x1, y1, x2, y2);
+ x1 = x2, y1 = y2;
+ x2 += dx, y2 = y0 + odd*dy;
+ odd = !odd;
+ }
+
+ /* Restore previous clipping rectangle(s) */
+ android_set_clip_rectangles (s->gc, 0, 0, s->clip, s->num_clips);
+}
+
+static void
+android_draw_glyph_string_foreground (struct glyph_string *s)
+{
+ int i, x;
+
+ /* If first glyph of S has a left box line, start drawing the text
+ of S to the right of that box line. */
+ if (s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p)
+ x = s->x + max (s->face->box_vertical_line_width, 0);
+ else
+ x = s->x;
+
+ /* Draw characters of S as rectangles if S's font could not be
+ loaded. */
+ if (s->font_not_found_p)
+ {
+ for (i = 0; i < s->nchars; ++i)
+ {
+ struct glyph *g = s->first_glyph + i;
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ s->gc, x, s->y,
+ g->pixel_width - 1,
+ s->height - 1);
+ x += g->pixel_width;
+ }
+ }
+ else
+ {
+ struct font *font = s->font;
+ int boff = font->baseline_offset;
+ int y;
+
+ if (font->vertical_centering)
+ boff = VCENTER_BASELINE_OFFSET (font, s->f) - boff;
+
+ y = s->ybase - boff;
+ if (s->for_overlaps
+ || (s->background_filled_p && s->hl != DRAW_CURSOR))
+ font->driver->draw (s, 0, s->nchars, x, y, false);
+ else
+ font->driver->draw (s, 0, s->nchars, x, y, true);
+ if (s->face->overstrike)
+ font->driver->draw (s, 0, s->nchars, x + 1, y, false);
+ }
+}
+
+static void
+android_draw_composite_glyph_string_foreground (struct glyph_string *s)
+{
+ int i, j, x;
+ struct font *font = s->font;
+
+ /* If first glyph of S has a left box line, start drawing the text
+ of S to the right of that box line. */
+ if (s->face && s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p)
+ x = s->x + max (s->face->box_vertical_line_width, 0);
+ else
+ x = s->x;
+
+ /* S is a glyph string for a composition. S->cmp_from is the index
+ of the first character drawn for glyphs of this composition.
+ S->cmp_from == 0 means we are drawing the very first character of
+ this composition. */
+
+ /* Draw a rectangle for the composition if the font for the very
+ first character of the composition could not be loaded. */
+ if (s->font_not_found_p)
+ {
+ if (s->cmp_from == 0)
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ s->gc, x, s->y,
+ s->width - 1, s->height - 1);
+ }
+ else if (! s->first_glyph->u.cmp.automatic)
+ {
+ int y = s->ybase;
+
+ for (i = 0, j = s->cmp_from; i < s->nchars; i++, j++)
+ /* TAB in a composition means display glyphs with
+ padding space on the left or right. */
+ if (COMPOSITION_GLYPH (s->cmp, j) != '\t')
+ {
+ int xx = x + s->cmp->offsets[j * 2];
+ int yy = y - s->cmp->offsets[j * 2 + 1];
+
+ font->driver->draw (s, j, j + 1, xx, yy, false);
+ if (s->face->overstrike)
+ font->driver->draw (s, j, j + 1, xx + 1, yy, false);
+ }
+ }
+ else
+ {
+ Lisp_Object gstring = composition_gstring_from_id (s->cmp_id);
+ Lisp_Object glyph;
+ int y = s->ybase;
+ int width = 0;
+
+ for (i = j = s->cmp_from; i < s->cmp_to; i++)
+ {
+ glyph = LGSTRING_GLYPH (gstring, i);
+ if (NILP (LGLYPH_ADJUSTMENT (glyph)))
+ width += LGLYPH_WIDTH (glyph);
+ else
+ {
+ int xoff, yoff, wadjust;
+
+ if (j < i)
+ {
+ font->driver->draw (s, j, i, x, y, false);
+ if (s->face->overstrike)
+ font->driver->draw (s, j, i, x + 1, y, false);
+ x += width;
+ }
+ xoff = LGLYPH_XOFF (glyph);
+ yoff = LGLYPH_YOFF (glyph);
+ wadjust = LGLYPH_WADJUST (glyph);
+ font->driver->draw (s, i, i + 1, x + xoff, y + yoff, false);
+ if (s->face->overstrike)
+ font->driver->draw (s, i, i + 1, x + xoff + 1, y + yoff,
+ false);
+ x += wadjust;
+ j = i + 1;
+ width = 0;
+ }
+ }
+ if (j < i)
+ {
+ font->driver->draw (s, j, i, x, y, false);
+ if (s->face->overstrike)
+ font->driver->draw (s, j, i, x + 1, y, false);
+ }
+ }
+}
+
+static void
+android_draw_glyphless_glyph_string_foreground (struct glyph_string *s)
+{
+ struct glyph *glyph = s->first_glyph;
+ unsigned char2b[8];
+ int x, i, j;
+
+ /* If first glyph of S has a left box line, start drawing the text
+ of S to the right of that box line. */
+ if (s->face && s->face->box != FACE_NO_BOX
+ && s->first_glyph->left_box_line_p)
+ x = s->x + max (s->face->box_vertical_line_width, 0);
+ else
+ x = s->x;
+
+ s->char2b = char2b;
+
+ for (i = 0; i < s->nchars; i++, glyph++)
+ {
+#ifdef GCC_LINT
+ enum { PACIFY_GCC_BUG_81401 = 1 };
+#else
+ enum { PACIFY_GCC_BUG_81401 = 0 };
+#endif
+ char buf[7 + PACIFY_GCC_BUG_81401];
+ char *str = NULL;
+ int len = glyph->u.glyphless.len;
+
+ if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_ACRONYM)
+ {
+ if (len > 0
+ && CHAR_TABLE_P (Vglyphless_char_display)
+ && (CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (Vglyphless_char_display))
+ >= 1))
+ {
+ Lisp_Object acronym
+ = (! glyph->u.glyphless.for_no_font
+ ? CHAR_TABLE_REF (Vglyphless_char_display,
+ glyph->u.glyphless.ch)
+ : XCHAR_TABLE (Vglyphless_char_display)->extras[0]);
+ if (CONSP (acronym))
+ acronym = XCAR (acronym);
+ if (STRINGP (acronym))
+ str = SSDATA (acronym);
+ }
+ }
+ else if (glyph->u.glyphless.method == GLYPHLESS_DISPLAY_HEX_CODE)
+ {
+ unsigned int ch = glyph->u.glyphless.ch;
+ eassume (ch <= MAX_CHAR);
+ sprintf (buf, "%0*X", ch < 0x10000 ? 4 : 6, ch);
+ str = buf;
+ }
+
+ if (str)
+ {
+ int upper_len = (len + 1) / 2;
+
+ /* It is assured that all LEN characters in STR is ASCII. */
+ for (j = 0; j < len; j++)
+ char2b[j] = s->font->driver->encode_char (s->font, str[j]) & 0xFFFF;
+ s->font->driver->draw (s, 0, upper_len,
+ x + glyph->slice.glyphless.upper_xoff,
+ s->ybase + glyph->slice.glyphless.upper_yoff,
+ false);
+ s->font->driver->draw (s, upper_len, len,
+ x + glyph->slice.glyphless.lower_xoff,
+ s->ybase + glyph->slice.glyphless.lower_yoff,
+ false);
+ }
+ if (glyph->u.glyphless.method != GLYPHLESS_DISPLAY_THIN_SPACE)
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ x, s->ybase - glyph->ascent,
+ glyph->pixel_width - 1,
+ glyph->ascent + glyph->descent - 1);
+ x += glyph->pixel_width;
+ }
+
+ /* Defend against hypothetical bad code elsewhere that uses
+ s->char2b after this function returns. */
+ s->char2b = NULL;
+}
+
+static void
+android_draw_glyph_string (struct glyph_string *s)
+{
+ bool relief_drawn_p = false;
+
+ /* If S draws into the background of its successors, draw the
+ background of the successors first so that S can draw into it.
+ This makes S->next use XDrawString instead of XDrawImageString. */
+ if (s->next && s->right_overhang && !s->for_overlaps)
+ {
+ int width;
+ struct glyph_string *next;
+
+ for (width = 0, next = s->next;
+ next && width < s->right_overhang;
+ width += next->width, next = next->next)
+ if (next->first_glyph->type != IMAGE_GLYPH)
+ {
+ android_set_glyph_string_gc (next);
+ android_set_glyph_string_clipping (next);
+ if (next->first_glyph->type == STRETCH_GLYPH)
+ android_draw_stretch_glyph_string (next);
+ else
+ android_draw_glyph_string_background (next, true);
+ next->num_clips = 0;
+ }
+ }
+
+ /* Set up S->gc, set clipping and draw S. */
+ android_set_glyph_string_gc (s);
+
+ /* Draw relief (if any) in advance for char/composition so that the
+ glyph string can be drawn over it. */
+ if (!s->for_overlaps
+ && s->face->box != FACE_NO_BOX
+ && (s->first_glyph->type == CHAR_GLYPH
+ || s->first_glyph->type == COMPOSITE_GLYPH))
+
+ {
+ android_set_glyph_string_clipping (s);
+ android_draw_glyph_string_background (s, true);
+ android_draw_glyph_string_box (s);
+ android_set_glyph_string_clipping (s);
+ relief_drawn_p = true;
+ }
+ else if (!s->clip_head /* draw_glyphs didn't specify a clip mask. */
+ && !s->clip_tail
+ && ((s->prev && s->prev->hl != s->hl && s->left_overhang)
+ || (s->next && s->next->hl != s->hl && s->right_overhang)))
+ /* We must clip just this glyph. left_overhang part has already
+ drawn when s->prev was drawn, and right_overhang part will be
+ drawn later when s->next is drawn. */
+ android_set_glyph_string_clipping_exactly (s, s);
+ else
+ android_set_glyph_string_clipping (s);
+
+ switch (s->first_glyph->type)
+ {
+ case IMAGE_GLYPH:
+ android_draw_image_glyph_string (s);
+ break;
+
+ case XWIDGET_GLYPH:
+ emacs_abort ();
+ break;
+
+ case STRETCH_GLYPH:
+ android_draw_stretch_glyph_string (s);
+ break;
+
+ case CHAR_GLYPH:
+ if (s->for_overlaps)
+ s->background_filled_p = true;
+ else
+ android_draw_glyph_string_background (s, false);
+ android_draw_glyph_string_foreground (s);
+ break;
+
+ case COMPOSITE_GLYPH:
+ if (s->for_overlaps || (s->cmp_from > 0
+ && ! s->first_glyph->u.cmp.automatic))
+ s->background_filled_p = true;
+ else
+ android_draw_glyph_string_background (s, true);
+ android_draw_composite_glyph_string_foreground (s);
+ break;
+
+ case GLYPHLESS_GLYPH:
+ if (s->for_overlaps)
+ s->background_filled_p = true;
+ else
+ android_draw_glyph_string_background (s, true);
+ android_draw_glyphless_glyph_string_foreground (s);
+ break;
+
+ default:
+ emacs_abort ();
+ }
+
+ if (!s->for_overlaps)
+ {
+ int area_x, area_y, area_width, area_height;
+ int area_max_x, decoration_width;
+
+ /* Prevent the underline from overwriting surrounding areas
+ and the fringe. */
+ window_box (s->w, s->area, &area_x, &area_y,
+ &area_width, &area_height);
+ area_max_x = area_x + area_width - 1;
+
+ decoration_width = s->width;
+ if (!s->row->mode_line_p
+ && !s->row->tab_line_p
+ && area_max_x < (s->x + decoration_width - 1))
+ decoration_width -= (s->x + decoration_width - 1) - area_max_x;
+
+ /* Draw relief if not yet drawn. */
+ if (!relief_drawn_p && s->face->box != FACE_NO_BOX)
+ android_draw_glyph_string_box (s);
+
+ /* Draw underline. */
+ if (s->face->underline)
+ {
+ if (s->face->underline == FACE_UNDER_WAVE)
+ {
+ if (s->face->underline_defaulted_p)
+ android_draw_underwave (s, decoration_width);
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->underline_color);
+ android_draw_underwave (s, decoration_width);
+ android_set_foreground (s->gc, xgcv.foreground);
+ }
+ }
+ else if (s->face->underline == FACE_UNDER_LINE)
+ {
+ unsigned long thickness, position;
+ int y;
+
+ if (s->prev
+ && s->prev->face->underline == FACE_UNDER_LINE
+ && (s->prev->face->underline_at_descent_line_p
+ == s->face->underline_at_descent_line_p)
+ && (s->prev->face->underline_pixels_above_descent_line
+ == s->face->underline_pixels_above_descent_line))
+ {
+ /* We use the same underline style as the previous one. */
+ thickness = s->prev->underline_thickness;
+ position = s->prev->underline_position;
+ }
+ else
+ {
+ struct font *font = font_for_underline_metrics (s);
+ unsigned long minimum_offset;
+ bool underline_at_descent_line;
+ bool use_underline_position_properties;
+ Lisp_Object val = (WINDOW_BUFFER_LOCAL_VALUE
+ (Qunderline_minimum_offset, s->w));
+
+ if (FIXNUMP (val))
+ minimum_offset = max (0, XFIXNUM (val));
+ else
+ minimum_offset = 1;
+
+ val = (WINDOW_BUFFER_LOCAL_VALUE
+ (Qx_underline_at_descent_line, s->w));
+ underline_at_descent_line
+ = (!(NILP (val) || BASE_EQ (val, Qunbound))
+ || s->face->underline_at_descent_line_p);
+
+ val = (WINDOW_BUFFER_LOCAL_VALUE
+ (Qx_use_underline_position_properties, s->w));
+ use_underline_position_properties
+ = !(NILP (val) || BASE_EQ (val, Qunbound));
+
+ /* Get the underline thickness. Default is 1 pixel. */
+ if (font && font->underline_thickness > 0)
+ thickness = font->underline_thickness;
+ else
+ thickness = 1;
+ if (underline_at_descent_line)
+ position = ((s->height - thickness)
+ - (s->ybase - s->y)
+ - s->face->underline_pixels_above_descent_line);
+ else
+ {
+ /* Get the underline position. This is the
+ recommended vertical offset in pixels from
+ the baseline to the top of the underline.
+ This is a signed value according to the
+ specs, and its default is
+
+ ROUND ((maximum descent) / 2), with
+ ROUND(x) = floor (x + 0.5) */
+
+ if (use_underline_position_properties
+ && font && font->underline_position >= 0)
+ position = font->underline_position;
+ else if (font)
+ position = (font->descent + 1) / 2;
+ else
+ position = minimum_offset;
+ }
+
+ /* Ignore minimum_offset if the amount of pixels was
+ explicitly specified. */
+ if (!s->face->underline_pixels_above_descent_line)
+ position = max (position, minimum_offset);
+ }
+ /* Check the sanity of thickness and position. We should
+ avoid drawing underline out of the current line area. */
+ if (s->y + s->height <= s->ybase + position)
+ position = (s->height - 1) - (s->ybase - s->y);
+ if (s->y + s->height < s->ybase + position + thickness)
+ thickness = (s->y + s->height) - (s->ybase + position);
+ s->underline_thickness = thickness;
+ s->underline_position = position;
+ y = s->ybase + position;
+ if (s->face->underline_defaulted_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, y, decoration_width, thickness);
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->underline_color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, y, decoration_width, thickness);
+ android_set_foreground (s->gc, xgcv.foreground);
+ }
+ }
+ }
+ /* Draw overline. */
+ if (s->face->overline_p)
+ {
+ unsigned long dy = 0, h = 1;
+
+ if (s->face->overline_color_defaulted_p)
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f),
+ s->gc, s->x, s->y + dy,
+ decoration_width, h);
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->overline_color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, s->y + dy, decoration_width, h);
+ android_set_foreground (s->gc, xgcv.foreground);
+ }
+ }
+
+ /* Draw strike-through. */
+ if (s->face->strike_through_p)
+ {
+ /* Y-coordinate and height of the glyph string's first
+ glyph. We cannot use s->y and s->height because those
+ could be larger if there are taller display elements
+ (e.g., characters displayed with a larger font) in the
+ same glyph row. */
+ int glyph_y = s->ybase - s->first_glyph->ascent;
+ int glyph_height = s->first_glyph->ascent + s->first_glyph->descent;
+ /* Strike-through width and offset from the glyph string's
+ top edge. */
+ unsigned long h = 1;
+ unsigned long dy = (glyph_height - h) / 2;
+
+ if (s->face->strike_through_color_defaulted_p)
+ android_fill_rectangle (FRAME_ANDROID_WINDOW (s->f),
+ s->gc, s->x, glyph_y + dy,
+ s->width, h);
+ else
+ {
+ struct android_gc_values xgcv;
+ android_get_gc_values (s->gc, ANDROID_GC_FOREGROUND, &xgcv);
+ android_set_foreground (s->gc, s->face->strike_through_color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (s->f), s->gc,
+ s->x, glyph_y + dy, decoration_width,
+ h);
+ android_set_foreground (s->gc, xgcv.foreground);
+ }
+ }
+
+ if (s->prev)
+ {
+ struct glyph_string *prev;
+
+ for (prev = s->prev; prev; prev = prev->prev)
+ if (prev->hl != s->hl
+ && prev->x + prev->width + prev->right_overhang > s->x)
+ {
+ /* As prev was drawn while clipped to its own area, we
+ must draw the right_overhang part using s->hl now. */
+ enum draw_glyphs_face save = prev->hl;
+
+ prev->hl = s->hl;
+ android_set_glyph_string_gc (prev);
+ android_set_glyph_string_clipping_exactly (s, prev);
+ if (prev->first_glyph->type == CHAR_GLYPH)
+ android_draw_glyph_string_foreground (prev);
+ else
+ android_draw_composite_glyph_string_foreground (prev);
+ android_reset_clip_rectangles (prev->f, prev->gc);
+ prev->hl = save;
+ prev->num_clips = 0;
+ }
+ }
+
+ if (s->next)
+ {
+ struct glyph_string *next;
+
+ for (next = s->next; next; next = next->next)
+ if (next->hl != s->hl
+ && next->x - next->left_overhang < s->x + s->width)
+ {
+ /* As next will be drawn while clipped to its own area,
+ we must draw the left_overhang part using s->hl now. */
+ enum draw_glyphs_face save = next->hl;
+
+ next->hl = s->hl;
+ android_set_glyph_string_gc (next);
+ android_set_glyph_string_clipping_exactly (s, next);
+ if (next->first_glyph->type == CHAR_GLYPH)
+ android_draw_glyph_string_foreground (next);
+ else
+ android_draw_composite_glyph_string_foreground (next);
+ android_reset_clip_rectangles (next->f, next->gc);
+ next->hl = save;
+ next->num_clips = 0;
+ next->clip_head = s->next;
+ }
+ }
+ }
+
+ /* Reset clipping. */
+ android_reset_clip_rectangles (s->f, s->gc);
+ s->num_clips = 0;
+
+ /* Set the stippled flag that tells redisplay whether or not a
+ stipple was actually draw. */
+
+ if (s->first_glyph->type != STRETCH_GLYPH
+ && s->first_glyph->type != IMAGE_GLYPH
+ && !s->row->stipple_p)
+ s->row->stipple_p = s->stippled_p;
+}
+
+static void
+android_define_frame_cursor (struct frame *f, Emacs_Cursor cursor)
+{
+ if (!f->pointer_invisible
+ && !FRAME_ANDROID_OUTPUT (f)->hourglass
+ && f->output_data.android->current_cursor != cursor)
+ android_define_cursor (FRAME_ANDROID_WINDOW (f), cursor);
+
+ f->output_data.android->current_cursor = cursor;
+}
+
+static void
+android_clear_frame_area (struct frame *f, int x, int y,
+ int width, int height)
+{
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f),
+ x, y, width, height);
+}
+
+void
+android_clear_under_internal_border (struct frame *f)
+{
+ if (FRAME_INTERNAL_BORDER_WIDTH (f) > 0)
+ {
+ int border = FRAME_INTERNAL_BORDER_WIDTH (f);
+ int width = FRAME_PIXEL_WIDTH (f);
+ int height = FRAME_PIXEL_HEIGHT (f);
+ int margin = FRAME_TOP_MARGIN_HEIGHT (f);
+ int face_id =
+ (FRAME_PARENT_FRAME (f)
+ ? (!NILP (Vface_remapping_alist)
+ ? lookup_basic_face (NULL, f, CHILD_FRAME_BORDER_FACE_ID)
+ : CHILD_FRAME_BORDER_FACE_ID)
+ : (!NILP (Vface_remapping_alist)
+ ? lookup_basic_face (NULL, f, INTERNAL_BORDER_FACE_ID)
+ : INTERNAL_BORDER_FACE_ID));
+ struct face *face = FACE_FROM_ID_OR_NULL (f, face_id);
+
+ if (face)
+ {
+ unsigned long color = face->background;
+ struct android_gc *gc = f->output_data.android->normal_gc;
+
+ android_set_foreground (gc, color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, margin,
+ width, border);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0, 0,
+ border, height);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, width - border,
+ 0, border, height);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, 0,
+ height - border, width, border);
+ android_set_foreground (gc, FRAME_FOREGROUND_PIXEL (f));
+ }
+ else
+ {
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0, 0,
+ border, height);
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
+ margin, width, border);
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), width - border,
+ 0, border, height);
+ android_clear_area (FRAME_ANDROID_DRAWABLE (f), 0,
+ height - border, width, border);
+ }
+ }
+}
+
+static void
+android_draw_hollow_cursor (struct window *w, struct glyph_row *row)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct android_display_info *dpyinfo = FRAME_DISPLAY_INFO (f);
+ int x, y, wd, h;
+ struct android_gc_values xgcv;
+ struct glyph *cursor_glyph;
+ struct android_gc *gc;
+
+ /* Get the glyph the cursor is on. If we can't tell because
+ the current matrix is invalid or such, give up. */
+ cursor_glyph = get_phys_cursor_glyph (w);
+ if (cursor_glyph == NULL)
+ return;
+
+ /* Compute frame-relative coordinates for phys cursor. */
+ get_phys_cursor_geometry (w, row, cursor_glyph, &x, &y, &h);
+ wd = w->phys_cursor_width - 1;
+
+ /* The foreground of cursor_gc is typically the same as the normal
+ background color, which can cause the cursor box to be invisible. */
+ xgcv.foreground = f->output_data.android->cursor_pixel;
+ if (dpyinfo->scratch_cursor_gc)
+ android_change_gc (dpyinfo->scratch_cursor_gc,
+ ANDROID_GC_FOREGROUND, &xgcv);
+ else
+ dpyinfo->scratch_cursor_gc
+ = android_create_gc (ANDROID_GC_FOREGROUND, &xgcv);
+ gc = dpyinfo->scratch_cursor_gc;
+
+ /* When on R2L character, show cursor at the right edge of the
+ glyph, unless the cursor box is as wide as the glyph or wider
+ (the latter happens when x-stretch-cursor is non-nil). */
+ if ((cursor_glyph->resolved_level & 1) != 0
+ && cursor_glyph->pixel_width > wd)
+ {
+ x += cursor_glyph->pixel_width - wd;
+ if (wd > 0)
+ wd -= 1;
+ }
+ /* Set clipping, draw the rectangle, and reset clipping again. */
+ android_clip_to_row (w, row, TEXT_AREA, gc);
+ android_draw_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x, y, wd, h - 1);
+ android_reset_clip_rectangles (f, gc);
+}
+
+static void
+android_draw_bar_cursor (struct window *w, struct glyph_row *row, int width,
+ enum text_cursor_kinds kind)
+{
+ struct frame *f = XFRAME (w->frame);
+ struct glyph *cursor_glyph;
+ int cursor_start_y;
+
+ /* If cursor is out of bounds, don't draw garbage. This can happen
+ in mini-buffer windows when switching between echo area glyphs
+ and mini-buffer. */
+ cursor_glyph = get_phys_cursor_glyph (w);
+ if (cursor_glyph == NULL)
+ return;
+
+ /* Experimental avoidance of cursor on xwidget. */
+ if (cursor_glyph->type == XWIDGET_GLYPH)
+ return;
+
+ /* If on an image, draw like a normal cursor. That's usually better
+ visible than drawing a bar, esp. if the image is large so that
+ the bar might not be in the window. */
+ if (cursor_glyph->type == IMAGE_GLYPH)
+ {
+ struct glyph_row *r;
+ r = MATRIX_ROW (w->current_matrix, w->phys_cursor.vpos);
+ draw_phys_cursor_glyph (w, r, DRAW_CURSOR);
+ }
+ else
+ {
+ struct android_gc *gc = FRAME_DISPLAY_INFO (f)->scratch_cursor_gc;
+ unsigned long mask = ANDROID_GC_FOREGROUND | ANDROID_GC_BACKGROUND;
+ struct face *face = FACE_FROM_ID (f, cursor_glyph->face_id);
+ struct android_gc_values xgcv;
+
+ /* If the glyph's background equals the color we normally draw
+ the bars cursor in, the bar cursor in its normal color is
+ invisible. Use the glyph's foreground color instead in this
+ case, on the assumption that the glyph's colors are chosen so
+ that the glyph is legible. */
+ if (face->background == f->output_data.android->cursor_pixel)
+ xgcv.background = xgcv.foreground = face->foreground;
+ else
+ xgcv.background = xgcv.foreground = f->output_data.android->cursor_pixel;
+
+ if (gc)
+ android_change_gc (gc, mask, &xgcv);
+ else
+ {
+ gc = android_create_gc (mask, &xgcv);
+ FRAME_DISPLAY_INFO (f)->scratch_cursor_gc = gc;
+ }
+
+ android_clip_to_row (w, row, TEXT_AREA, gc);
+
+ if (kind == BAR_CURSOR)
+ {
+ int x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
+
+ if (width < 0)
+ width = FRAME_CURSOR_WIDTH (f);
+ width = min (cursor_glyph->pixel_width, width);
+
+ w->phys_cursor_width = width;
+
+ /* If the character under cursor is R2L, draw the bar cursor
+ on the right of its glyph, rather than on the left. */
+ if ((cursor_glyph->resolved_level & 1) != 0)
+ x += cursor_glyph->pixel_width - width;
+
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
+ WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y),
+ width, row->height);
+ }
+ else /* HBAR_CURSOR */
+ {
+ int dummy_x, dummy_y, dummy_h;
+ int x = WINDOW_TEXT_TO_FRAME_PIXEL_X (w, w->phys_cursor.x);
+
+ if (width < 0)
+ width = row->height;
+
+ width = min (row->height, width);
+
+ get_phys_cursor_geometry (w, row, cursor_glyph, &dummy_x,
+ &dummy_y, &dummy_h);
+
+ cursor_start_y = WINDOW_TO_FRAME_PIXEL_Y (w, w->phys_cursor.y
+ + row->height - width);
+
+ if ((cursor_glyph->resolved_level & 1) != 0
+ && cursor_glyph->pixel_width > w->phys_cursor_width - 1)
+ x += cursor_glyph->pixel_width - w->phys_cursor_width + 1;
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f), gc, x,
+ cursor_start_y,
+ w->phys_cursor_width - 1, width);
+ }
+
+ android_reset_clip_rectangles (f, gc);
+ }
+}
+
+static void
+android_draw_window_cursor (struct window *w, struct glyph_row *glyph_row,
+ int x, int y, enum text_cursor_kinds cursor_type,
+ int cursor_width, bool on_p, bool active_p)
+{
+ struct frame *f;
+
+ f = WINDOW_XFRAME (w);
+
+ if (on_p)
+ {
+ w->phys_cursor_type = cursor_type;
+ w->phys_cursor_on_p = true;
+
+ if (glyph_row->exact_window_width_line_p
+ && (glyph_row->reversed_p
+ ? (w->phys_cursor.hpos < 0)
+ : (w->phys_cursor.hpos >= glyph_row->used[TEXT_AREA])))
+ {
+ glyph_row->cursor_in_fringe_p = true;
+ draw_fringe_bitmap (w, glyph_row, glyph_row->reversed_p);
+ }
+ else
+ {
+ switch (cursor_type)
+ {
+ case HOLLOW_BOX_CURSOR:
+ android_draw_hollow_cursor (w, glyph_row);
+ break;
+
+ case FILLED_BOX_CURSOR:
+ draw_phys_cursor_glyph (w, glyph_row, DRAW_CURSOR);
+ break;
+
+ case BAR_CURSOR:
+ android_draw_bar_cursor (w, glyph_row, cursor_width, BAR_CURSOR);
+ break;
+
+ case HBAR_CURSOR:
+ android_draw_bar_cursor (w, glyph_row, cursor_width, HBAR_CURSOR);
+ break;
+
+ case NO_CURSOR:
+ w->phys_cursor_width = 0;
+ break;
+
+ default:
+ emacs_abort ();
+ }
+ }
+
+ /* Now proceed to tell the input method the current position of
+ the cursor, if required. */
+
+ if (FRAME_OUTPUT_DATA (f)->need_cursor_updates
+ && w == XWINDOW (f->selected_window))
+ android_set_preeditarea (w, x, y);
+ }
+}
+
+static void
+android_draw_vertical_window_border (struct window *w, int x, int y0, int y1)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct face *face;
+
+ face = FACE_FROM_ID_OR_NULL (f, VERTICAL_BORDER_FACE_ID);
+ if (face)
+ android_set_foreground (f->output_data.android->normal_gc,
+ face->foreground);
+
+ android_draw_line (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x, y0, x, y1);
+}
+
+static void
+android_draw_window_divider (struct window *w, int x0, int x1, int y0, int y1)
+{
+ struct frame *f = XFRAME (WINDOW_FRAME (w));
+ struct face *face = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FACE_ID);
+ struct face *face_first
+ = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_FIRST_PIXEL_FACE_ID);
+ struct face *face_last
+ = FACE_FROM_ID_OR_NULL (f, WINDOW_DIVIDER_LAST_PIXEL_FACE_ID);
+ unsigned long color = face ? face->foreground : FRAME_FOREGROUND_PIXEL (f);
+ unsigned long color_first = (face_first
+ ? face_first->foreground
+ : FRAME_FOREGROUND_PIXEL (f));
+ unsigned long color_last = (face_last
+ ? face_last->foreground
+ : FRAME_FOREGROUND_PIXEL (f));
+
+ if ((y1 - y0 > x1 - x0) && (x1 - x0 >= 3))
+ /* A vertical divider, at least three pixels wide: Draw first and
+ last pixels differently. */
+ {
+ android_set_foreground (f->output_data.android->normal_gc,
+ color_first);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y0, 1, y1 - y0);
+ android_set_foreground (f->output_data.android->normal_gc,
+ color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0 + 1, y0, x1 - x0 - 2, y1 - y0);
+ android_set_foreground (f->output_data.android->normal_gc,
+ color_last);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x1 - 1, y0, 1, y1 - y0);
+ }
+ else if ((x1 - x0 > y1 - y0) && (y1 - y0 >= 3))
+ /* A horizontal divider, at least three pixels high: Draw first
+ and last pixels differently. */
+ {
+ android_set_foreground (f->output_data.android->normal_gc,
+ color_first);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y0, x1 - x0, 1);
+ android_set_foreground (f->output_data.android->normal_gc, color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y0 + 1, x1 - x0, y1 - y0 - 2);
+ android_set_foreground (f->output_data.android->normal_gc,
+ color_last);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y1 - 1, x1 - x0, 1);
+ }
+ else
+ {
+ /* In any other case do not draw the first and last pixels
+ differently. */
+ android_set_foreground (f->output_data.android->normal_gc, color);
+ android_fill_rectangle (FRAME_ANDROID_DRAWABLE (f),
+ f->output_data.android->normal_gc,
+ x0, y0, x1 - x0, y1 - y0);
+ }
+}
+
+
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-prototypes"
+#else
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+#endif
+
+/* Input method related functions. Some of these are called from Java
+ within the UI thread. */
+
+/* A counter used to decide when an editing request completes. */
+static unsigned long edit_counter;
+
+/* The last counter known to have completed. */
+static unsigned long last_edit_counter;
+
+/* Semaphore posted every time the counter increases. */
+static sem_t edit_sem;
+
+/* Try to synchronize with the UI thread, waiting a certain amount of
+ time for outstanding editing requests to complete.
+
+ Every time one of the text retrieval functions is called and an
+ editing request is made, Emacs gives the main thread approximately
+ 100 ms to process it, in order to mostly keep the input method in
+ sync with the buffer contents. */
+
+static void
+android_sync_edit (void)
+{
+ struct timespec start, end, rem;
+
+ if (__atomic_load_n (&last_edit_counter,
+ __ATOMIC_SEQ_CST)
+ == edit_counter)
+ return;
+
+ start = current_timespec ();
+ end = timespec_add (start, make_timespec (0, 100000000));
+
+ while (true)
+ {
+ rem = timespec_sub (end, current_timespec ());
+
+ /* Timeout. */
+ if (timespec_sign (rem) < 0)
+ break;
+
+ if (__atomic_load_n (&last_edit_counter,
+ __ATOMIC_SEQ_CST)
+ == edit_counter)
+ break;
+
+ sem_timedwait (&edit_sem, &end);
+ }
+}
+
+/* Return a copy of the specified Java string and its length in
+ *LENGTH. Use the JNI environment ENV. Value is NULL if copying
+ *the string fails. */
+
+static unsigned short *
+android_copy_java_string (JNIEnv *env, jstring string, size_t *length)
+{
+ jsize size, i;
+ const jchar *java;
+ unsigned short *buffer;
+
+ size = (*env)->GetStringLength (env, string);
+ buffer = malloc (size * sizeof *buffer);
+
+ if (!buffer)
+ return NULL;
+
+ java = (*env)->GetStringChars (env, string, NULL);
+
+ if (!java)
+ {
+ free (buffer);
+ return NULL;
+ }
+
+ for (i = 0; i < size; ++i)
+ buffer[i] = java[i];
+
+ *length = size;
+ (*env)->ReleaseStringChars (env, string, java);
+ return buffer;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (beginBatchEdit) (JNIEnv *env, jobject object, jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_START_BATCH_EDIT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (endBatchEdit) (JNIEnv *env, jobject object, jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_END_BATCH_EDIT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (commitCompletion) (JNIEnv *env, jobject object, jshort window,
+ jstring completion_text, jint position)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+ unsigned short *text;
+ size_t length;
+
+ /* First, obtain a copy of the Java string. */
+ text = android_copy_java_string (env, completion_text, &length);
+
+ if (!text)
+ return;
+
+ /* Next, populate the event. Events will always eventually be
+ delivered on Android, so handle_one_android_event can be relied
+ on to free text. */
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_COMMIT_TEXT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = min (length, PTRDIFF_MAX);
+ event.ime.position = position;
+ event.ime.text = text;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (commitText) (JNIEnv *env, jobject object, jshort window,
+ jstring commit_text, jint position)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+ unsigned short *text;
+ size_t length;
+
+ /* First, obtain a copy of the Java string. */
+ text = android_copy_java_string (env, commit_text, &length);
+
+ if (!text)
+ return;
+
+ /* Next, populate the event. Events will always eventually be
+ delivered on Android, so handle_one_android_event can be relied
+ on to free text. */
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_COMMIT_TEXT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = min (length, PTRDIFF_MAX);
+ event.ime.position = position;
+ event.ime.text = text;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (deleteSurroundingText) (JNIEnv *env, jobject object,
+ jshort window, jint left_length,
+ jint right_length)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_DELETE_SURROUNDING_TEXT;
+ event.ime.start = left_length;
+ event.ime.end = right_length;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (finishComposingText) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_FINISH_COMPOSING_TEXT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+/* Structure describing the context used for a text query. */
+
+struct android_conversion_query_context
+{
+ /* The conversion request. */
+ struct textconv_callback_struct query;
+
+ /* The window the request is being made on. */
+ android_window window;
+
+ /* Whether or not the request was successful. */
+ bool success;
+};
+
+/* Obtain the text from the frame whose window is that specified in
+ DATA using the text conversion query specified there.
+
+ Set ((struct android_conversion_query_context *) DATA)->success on
+ success. */
+
+static void
+android_perform_conversion_query (void *data)
+{
+ struct android_conversion_query_context *context;
+ struct frame *f;
+
+ context = data;
+
+ /* Find the frame associated with the window. */
+ f = android_window_to_frame (NULL, context->window);
+
+ if (!f)
+ return;
+
+ textconv_query (f, &context->query, 0);
+
+ /* context->query.text will have been set even if textconv_query
+ returns 1. */
+
+ context->success = true;
+}
+
+/* Convert a string BUFFERS containing N characters in Emacs's
+ internal multibyte encoding to a Java string utilizing the
+ specified JNI environment.
+
+ If N is equal to BYTES, then BUFFER is a single byte buffer.
+ Otherwise, BUFFER is a multibyte buffer.
+
+ Make sure N and BYTES are absolutely correct, or you are asking for
+ trouble.
+
+ Value is the string upon success, NULL otherwise. Any exceptions
+ generated are not cleared. */
+
+static jstring
+android_text_to_string (JNIEnv *env, char *buffer, ptrdiff_t n,
+ ptrdiff_t bytes)
+{
+ jchar *utf16;
+ size_t size, index;
+ jstring string;
+ int encoded;
+
+ if (n == bytes)
+ {
+ /* This buffer holds no multibyte characters. */
+
+ if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size))
+ return NULL;
+
+ utf16 = malloc (size);
+ index = 0;
+
+ if (!utf16)
+ return NULL;
+
+ while (n--)
+ {
+ utf16[index] = buffer[index];
+ index++;
+ }
+
+ string = (*env)->NewString (env, utf16, bytes);
+ free (utf16);
+
+ return string;
+ }
+
+ /* Allocate enough to hold N characters. */
+
+ if (INT_MULTIPLY_WRAPV (n, sizeof *utf16, &size))
+ return NULL;
+
+ utf16 = malloc (size);
+ index = 0;
+
+ if (!utf16)
+ return NULL;
+
+ while (n--)
+ {
+ eassert (CHAR_HEAD_P (*buffer));
+ encoded = STRING_CHAR ((unsigned char *) buffer);
+
+ /* Now figure out how to save ENCODED into the string.
+ Emacs operates on multibyte characters, not UTF-16
+ characters with surrogate pairs as Android does.
+
+ However, character positions in Java are represented in 2
+ byte units, meaning that the text position reported to
+ Android can become out of sync if characters are found in a
+ buffer that require surrogate pairs.
+
+ The hack used by Emacs is to simply replace each multibyte
+ character that doesn't fit in a jchar with the NULL
+ character. */
+
+ if (encoded >= 65536)
+ encoded = 0;
+
+ utf16[index++] = encoded;
+ buffer += BYTES_BY_CHAR_HEAD (*buffer);
+ }
+
+ /* Create the string. */
+ string = (*env)->NewString (env, utf16, index);
+ free (utf16);
+ return string;
+}
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getTextAfterCursor) (JNIEnv *env, jobject object, jshort window,
+ jint length, jint flags)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_conversion_query_context context;
+ jstring string;
+
+ /* First, set up the conversion query. */
+ context.query.position = EMACS_INT_MAX;
+ context.query.direction = TEXTCONV_FORWARD_CHAR;
+ context.query.factor = min (length, 65535);
+ context.query.operation = TEXTCONV_RETRIEVAL;
+
+ /* Next, set the rest of the context. */
+ context.window = window;
+ context.success = false;
+
+ /* Now try to perform the query. */
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_perform_conversion_query,
+ &context))
+ return NULL;
+
+ if (!context.success)
+ return NULL;
+
+ /* context->query.text now contains the text in Emacs's internal
+ UTF-8 based encoding.
+
+ Convert it to Java's UTF-16 encoding, which is the same as
+ UTF-16, except that NULL bytes are encoded as surrogate pairs.
+
+ This assumes that `free' can free data allocated with xmalloc. */
+
+ string = android_text_to_string (env, context.query.text.text,
+ context.query.text.length,
+ context.query.text.bytes);
+ free (context.query.text.text);
+
+ return string;
+}
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getTextBeforeCursor) (JNIEnv *env, jobject object, jshort window,
+ jint length, jint flags)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_conversion_query_context context;
+ jstring string;
+
+ /* First, set up the conversion query. */
+ context.query.position = TYPE_MINIMUM (EMACS_INT);
+ context.query.direction = TEXTCONV_BACKWARD_CHAR;
+ context.query.factor = min (length, 65535);
+ context.query.operation = TEXTCONV_RETRIEVAL;
+
+ /* Next, set the rest of the context. */
+ context.window = window;
+ context.success = false;
+
+ /* Now try to perform the query. */
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_perform_conversion_query,
+ &context))
+ return NULL;
+
+ if (!context.success)
+ return NULL;
+
+ /* context->query.text now contains the text in Emacs's internal
+ UTF-8 based encoding.
+
+ Convert it to Java's UTF-16 encoding, which is the same as
+ UTF-16, except that NULL bytes are encoded as surrogate pairs.
+
+ This assumes that `free' can free data allocated with xmalloc. */
+
+ string = android_text_to_string (env, context.query.text.text,
+ context.query.text.length,
+ context.query.text.bytes);
+ free (context.query.text.text);
+
+ return string;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setComposingText) (JNIEnv *env, jobject object, jshort window,
+ jstring composing_text,
+ jint new_cursor_position)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+ unsigned short *text;
+ size_t length;
+
+ /* First, obtain a copy of the Java string. */
+ text = android_copy_java_string (env, composing_text, &length);
+
+ if (!text)
+ return;
+
+ /* Next, populate the event. Events will always eventually be
+ delivered on Android, so handle_one_android_event can be relied
+ on to free text. */
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_SET_COMPOSING_TEXT;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = min (length, PTRDIFF_MAX);
+ event.ime.position = new_cursor_position;
+ event.ime.text = text;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setComposingRegion) (JNIEnv *env, jobject object, jshort window,
+ jint start, jint end)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_SET_COMPOSING_REGION;
+ event.ime.start = start + 1;
+ event.ime.end = end + 1;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (setSelection) (JNIEnv *env, jobject object, jshort window,
+ jint start, jint end)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ /* While IMEs want access to the entire selection, Emacs only
+ supports setting the point. */
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_SET_POINT;
+ event.ime.start = start + 1;
+ event.ime.end = end + 1;
+ event.ime.length = 0;
+ event.ime.position = start;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+/* Structure describing the context for `getSelection'. */
+
+struct android_get_selection_context
+{
+ /* The window in question. */
+ android_window window;
+
+ /* The position of the window's point when it was last
+ redisplayed, and its last mark if active. */
+ ptrdiff_t point, mark;
+};
+
+/* Function run on the main thread by `getSelection'.
+ Place the character position of point in PT. */
+
+static void
+android_get_selection (void *data)
+{
+ struct android_get_selection_context *context;
+ struct frame *f;
+ struct window *w;
+ struct buffer *b;
+
+ context = data;
+
+ /* Look up the associated frame and its selected window. */
+ f = android_window_to_frame (NULL, context->window);
+
+ if (!f)
+ context->point = -1;
+ else
+ {
+ w = XWINDOW (f->selected_window);
+
+ /* Return W's point at the time of the last redisplay. This is
+ rather important to keep the input method consistent with the
+ contents of the display. */
+ context->point = w->ephemeral_last_point;
+
+ /* Default context->mark to w->last_point too. */
+ context->mark = context->point;
+
+ /* If the mark is active, then set it properly. */
+ b = XBUFFER (w->contents);
+ if (!NILP (BVAR (b, mark_active)) && w->last_mark != -1)
+ context->mark = w->last_mark;
+ }
+}
+
+JNIEXPORT jintArray JNICALL
+NATIVE_NAME (getSelection) (JNIEnv *env, jobject object, jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_get_selection_context context;
+ jintArray array;
+ jint contents[2];
+
+ context.window = window;
+
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_get_selection,
+ &context))
+ return NULL;
+
+ if (context.point == -1)
+ return NULL;
+
+ /* Wraparound actually makes more sense than truncation; at least
+ editing will sort of work. Convert the positions to start from
+ index 0, as that is what Android expects. */
+ contents[0] = (unsigned int) min (context.point,
+ context.mark) - 1;
+ contents[1] = (unsigned int) max (context.point,
+ context.mark) - 1;
+
+ /* Now create the array. */
+ array = (*env)->NewIntArray (env, 2);
+
+ if (!array)
+ return NULL;
+
+ /* Set its contents. */
+ (*env)->SetIntArrayRegion (env, array, 0, 2, contents);
+ return array;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (performEditorAction) (JNIEnv *env, jobject object,
+ jshort window, int action)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ /* Undocumented behavior: performEditorAction is apparently expected
+ to finish composing any text. */
+
+ NATIVE_NAME (finishComposingText) (env, object, window);
+
+ event.xkey.type = ANDROID_KEY_PRESS;
+ event.xkey.serial = ++event_serial;
+ event.xkey.window = window;
+ event.xkey.time = 0;
+ event.xkey.state = 0;
+ event.xkey.keycode = 66;
+ event.xkey.unicode_char = 0;
+
+ android_write_event (&event);
+}
+
+
+
+/* Text extraction. */
+
+struct android_get_extracted_text_context
+{
+ /* The parameters of the request. */
+ int hint_max_chars;
+
+ /* Token for the request. */
+ int token;
+
+ /* Flags associated with the request. */
+ int flags;
+
+ /* The returned text, or NULL. */
+ char *text;
+
+ /* The size of that text in characters and bytes. */
+ ptrdiff_t length, bytes;
+
+ /* Offsets into that text. */
+ ptrdiff_t start, offset;
+
+ /* The window. */
+ android_window window;
+};
+
+/* Return the extracted text in the extracted text context specified
+ by DATA. */
+
+static void
+android_get_extracted_text (void *data)
+{
+ struct android_get_extracted_text_context *request;
+ struct frame *f;
+
+ request = data;
+
+ /* Find the frame associated with the window. */
+ f = android_window_to_frame (NULL, request->window);
+
+ if (!f)
+ return;
+
+ /* Now get the extracted text. */
+ request->text
+ = get_extracted_text (f, min (request->hint_max_chars, 600),
+ &request->start, &request->offset,
+ &request->length, &request->bytes);
+
+ /* See if request->flags & GET_EXTRACTED_TEXT_MONITOR. If so, then
+ the input method has asked to monitor changes to the extracted
+ text until the next IM context reset. */
+
+ FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = request->flags;
+ FRAME_ANDROID_OUTPUT (f)->extracted_text_token = request->token;
+ FRAME_ANDROID_OUTPUT (f)->extracted_text_hint = request->hint_max_chars;
+}
+
+/* Structure describing the `ExtractedTextRequest' class.
+ Valid only on the UI thread. */
+
+struct android_extracted_text_request_class
+{
+ bool initialized;
+ jfieldID hint_max_chars;
+ jfieldID token;
+};
+
+/* Structure describing the `ExtractedText' class.
+ Valid only on the UI thread. */
+
+struct android_extracted_text_class
+{
+ jclass class;
+ jmethodID constructor;
+ jfieldID partial_start_offset;
+ jfieldID partial_end_offset;
+ jfieldID selection_start;
+ jfieldID selection_end;
+ jfieldID start_offset;
+ jfieldID text;
+};
+
+/* Fields and methods associated with the `ExtractedTextRequest'
+ class. */
+struct android_extracted_text_request_class request_class;
+
+/* Fields and methods associated with the `ExtractedText' class. */
+struct android_extracted_text_class text_class;
+
+/* Return an ExtractedText object corresponding to the extracted text
+ TEXT. START is a character position describing the offset of the
+ first character in TEXT. OFFSET is the offset of point relative to
+ START.
+
+ Assume that request_class and text_class have already been
+ initialized.
+
+ Value is NULL if an error occurs; the exception is not cleared,
+ else a local reference to the ExtractedText object. */
+
+static jobject
+android_build_extracted_text (jstring text, ptrdiff_t start,
+ ptrdiff_t offset)
+{
+ JNIEnv *env;
+ jobject object;
+
+ env = android_java_env;
+
+ /* Return NULL if the class has not yet been obtained. */
+ if (!text_class.class)
+ return NULL;
+
+ /* Create an ExtractedText object containing this information. */
+ object = (*env)->NewObject (env, text_class.class,
+ text_class.constructor);
+ if (!object)
+ return NULL;
+
+ (*env)->SetIntField (env, object, text_class.partial_start_offset, -1);
+ (*env)->SetIntField (env, object, text_class.partial_end_offset, -1);
+ (*env)->SetIntField (env, object, text_class.selection_start,
+ min (offset, TYPE_MAXIMUM (jint)));
+ (*env)->SetIntField (env, object, text_class.selection_end,
+ min (offset, TYPE_MAXIMUM (jint)));
+
+ /* Subtract 1 from start: point indices in Emacs start from 1, but
+ Android expects 0. */
+ (*env)->SetIntField (env, object, text_class.start_offset,
+ min (start - 1, TYPE_MAXIMUM (jint)));
+ (*env)->SetObjectField (env, object, text_class.text, text);
+ return object;
+}
+
+JNIEXPORT jobject JNICALL
+NATIVE_NAME (getExtractedText) (JNIEnv *env, jobject ignored_object,
+ jshort window, jobject request,
+ jint flags)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_get_extracted_text_context context;
+ jstring string;
+ jclass class;
+ jobject object;
+
+ /* Initialize both classes if necessary. */
+
+ if (!request_class.initialized)
+ {
+ class
+ = (*env)->FindClass (env, ("android/view/inputmethod"
+ "/ExtractedTextRequest"));
+ assert (class);
+
+ request_class.hint_max_chars
+ = (*env)->GetFieldID (env, class, "hintMaxChars", "I");
+ assert (request_class.hint_max_chars);
+
+ request_class.token
+ = (*env)->GetFieldID (env, class, "token", "I");
+ assert (request_class.token);
+
+ request_class.initialized = true;
+ }
+
+ if (!text_class.class)
+ {
+ text_class.class
+ = (*env)->FindClass (env, ("android/view/inputmethod"
+ "/ExtractedText"));
+ assert (text_class.class);
+
+ class
+ = text_class.class
+ = (*env)->NewGlobalRef (env, text_class.class);
+ assert (text_class.class);
+
+ text_class.partial_start_offset
+ = (*env)->GetFieldID (env, class, "partialStartOffset", "I");
+ text_class.partial_end_offset
+ = (*env)->GetFieldID (env, class, "partialEndOffset", "I");
+ text_class.selection_start
+ = (*env)->GetFieldID (env, class, "selectionStart", "I");
+ text_class.selection_end
+ = (*env)->GetFieldID (env, class, "selectionEnd", "I");
+ text_class.start_offset
+ = (*env)->GetFieldID (env, class, "startOffset", "I");
+ text_class.text
+ = (*env)->GetFieldID (env, class, "text", "Ljava/lang/CharSequence;");
+ text_class.constructor
+ = (*env)->GetMethodID (env, class, "<init>", "()V");
+ }
+
+ context.hint_max_chars
+ = (*env)->GetIntField (env, request, request_class.hint_max_chars);
+ context.token
+ = (*env)->GetIntField (env, request, request_class.token);
+ context.flags = flags;
+ context.text = NULL;
+ context.window = window;
+
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_get_extracted_text,
+ &context))
+ return NULL;
+
+ if (!context.text)
+ return NULL;
+
+ /* Encode the returned text. */
+ string = android_text_to_string (env, context.text, context.length,
+ context.bytes);
+ free (context.text);
+
+ if (!string)
+ return NULL;
+
+ /* Create an ExtractedText object containing this information. */
+ object = (*env)->NewObject (env, text_class.class,
+ text_class.constructor);
+ if (!object)
+ return NULL;
+
+ (*env)->SetIntField (env, object, text_class.partial_start_offset, -1);
+ (*env)->SetIntField (env, object, text_class.partial_end_offset, -1);
+ (*env)->SetIntField (env, object, text_class.selection_start,
+ min (context.offset, TYPE_MAXIMUM (jint)));
+ (*env)->SetIntField (env, object, text_class.selection_end,
+ min (context.offset, TYPE_MAXIMUM (jint)));
+
+ /* Subtract 1 from start: point indices in Emacs start from 1, but
+ Android expects 0. */
+ (*env)->SetIntField (env, object, text_class.start_offset,
+ min (context.start - 1, TYPE_MAXIMUM (jint)));
+ (*env)->SetObjectField (env, object, text_class.text, string);
+ return object;
+}
+
+
+
+JNIEXPORT jstring JNICALL
+NATIVE_NAME (getSelectedText) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ struct android_get_extracted_text_context context;
+ jstring string;
+
+ context.hint_max_chars = -1;
+ context.token = 0;
+ context.text = NULL;
+ context.window = window;
+
+ android_sync_edit ();
+ if (android_run_in_emacs_thread (android_get_extracted_text,
+ &context))
+ return NULL;
+
+ if (!context.text)
+ return NULL;
+
+ /* Encode the returned text. */
+ string = android_text_to_string (env, context.text, context.length,
+ context.bytes);
+ free (context.text);
+
+ return string;
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (requestSelectionUpdate) (JNIEnv *env, jobject object,
+ jshort window)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_REQUEST_SELECTION_UPDATE;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = 0;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+JNIEXPORT void JNICALL
+NATIVE_NAME (requestCursorUpdates) (JNIEnv *env, jobject object,
+ jshort window, jint mode)
+{
+ JNI_STACK_ALIGNMENT_PROLOGUE;
+
+ union android_event event;
+
+ event.ime.type = ANDROID_INPUT_METHOD;
+ event.ime.serial = ++event_serial;
+ event.ime.window = window;
+ event.ime.operation = ANDROID_IME_REQUEST_CURSOR_UPDATES;
+ event.ime.start = 0;
+ event.ime.end = 0;
+ event.ime.length = mode;
+ event.ime.position = 0;
+ event.ime.text = NULL;
+ event.ime.counter = ++edit_counter;
+
+ android_write_event (&event);
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#else
+#pragma GCC diagnostic pop
+#endif
+
+
+
+/* Tell the input method where the composing region and selection of
+ F's selected window is located. W should be F's selected window;
+ if it is NULL, then F->selected_window is used in its place. */
+
+static void
+android_update_selection (struct frame *f, struct window *w)
+{
+ ptrdiff_t start, end, point, mark, offset, length, bytes;
+ struct buffer *b;
+ int hint, token;
+ char *text;
+ jobject extracted;
+ jstring string;
+
+ if (MARKERP (f->conversion.compose_region_start))
+ {
+ eassert (MARKERP (f->conversion.compose_region_end));
+
+ /* Indexing in android starts from 0 instead of 1. */
+ start = marker_position (f->conversion.compose_region_start) - 1;
+ end = marker_position (f->conversion.compose_region_end) - 1;
+ }
+ else
+ start = -1, end = -1;
+
+ /* Now constrain START and END to the maximium size of a Java
+ integer. */
+ start = min (start, TYPE_MAXIMUM (jint));
+ end = min (end, TYPE_MAXIMUM (jint));
+
+ if (!w)
+ w = XWINDOW (f->selected_window);
+
+ /* Figure out where the point and mark are. If the mark is not
+ active, then point is set to equal mark. */
+ b = XBUFFER (w->contents);
+ point = min (w->ephemeral_last_point,
+ TYPE_MAXIMUM (jint));
+ mark = ((!NILP (BVAR (b, mark_active))
+ && w->last_mark != -1)
+ ? min (w->last_mark, TYPE_MAXIMUM (jint))
+ : point);
+
+ /* Send the update. Android doesn't have a concept of ``point'' and
+ ``mark''; instead, it only has a selection, where the start of
+ the selection is less than or equal to the end. Also, convert
+ the indices from 1-based Emacs indices to 0-based Android
+ ones. */
+ android_update_ic (FRAME_ANDROID_WINDOW (f), min (point, mark) - 1,
+ max (point, mark) - 1, start, end);
+
+ /* Update the extracted text as well, if the input method has asked
+ for updates. 1 is
+ InputConnection.GET_EXTRACTED_TEXT_MONITOR. */
+
+ if (FRAME_ANDROID_OUTPUT (f)->extracted_text_flags & 1)
+ {
+ hint = FRAME_ANDROID_OUTPUT (f)->extracted_text_hint;
+ token = FRAME_ANDROID_OUTPUT (f)->extracted_text_token;
+ text = get_extracted_text (f, min (hint, 600), &start,
+ &offset, &length, &bytes);
+
+ if (text)
+ {
+ /* Make a string out of the extracted text. */
+ string = android_text_to_string (android_java_env,
+ text, length, bytes);
+ xfree (text);
+ android_exception_check ();
+
+ /* Make extracted text out of that string. */
+ extracted = android_build_extracted_text (string, start,
+ offset);
+ android_exception_check_1 (string);
+ ANDROID_DELETE_LOCAL_REF (string);
+
+ if (extracted)
+ {
+ /* extracted is now an associated ExtractedText object.
+ Perform the update. */
+ android_update_extracted_text (FRAME_ANDROID_WINDOW (f),
+ extracted, token);
+ ANDROID_DELETE_LOCAL_REF (extracted);
+ }
+ }
+ }
+}
+
+/* Return whether or not EVENT is an input method event destined for
+ the frame (struct frame *) ARG. */
+
+static bool
+android_event_is_for_frame (union android_event *event, void *arg)
+{
+ struct frame *f;
+
+ f = arg;
+ return (event->type == ANDROID_INPUT_METHOD
+ && event->ime.window == FRAME_ANDROID_WINDOW (f));
+}
+
+/* Notice that the input method connection to F should be reset as a
+ result of a change to its contents. */
+
+static void
+android_reset_conversion (struct frame *f)
+{
+ enum android_ic_mode mode;
+ struct window *w;
+ struct buffer *buffer;
+ Lisp_Object style;
+ union android_event event;
+
+ /* Reset the input method.
+
+ Pick an appropriate ``input mode'' based on whether or not the
+ minibuffer window is selected; this controls whether or not
+ ``RET'' inserts a newline or sends an actual key event. */
+
+ w = XWINDOW (f->selected_window);
+ buffer = XBUFFER (WINDOW_BUFFER (w));
+
+ style = (EQ (find_symbol_value (Qoverriding_text_conversion_style),
+ Qlambda)
+ ? BVAR (buffer, text_conversion_style)
+ : find_symbol_value (Qoverriding_text_conversion_style));
+
+ if (NILP (style) || conversion_disabled_p ())
+ mode = ANDROID_IC_MODE_NULL;
+ else if (EQ (style, Qaction) || EQ (f->selected_window,
+ f->minibuffer_window))
+ mode = ANDROID_IC_MODE_ACTION;
+ else
+ mode = ANDROID_IC_MODE_TEXT;
+
+ /* Remove any existing input method events that apply to FRAME from
+ the event queue.
+
+ There's a small window between this and the call to
+ android_reset_ic between which more events can be generated. */
+
+ while (android_check_if_event (&event, android_event_is_for_frame, f))
+ {
+ switch (event.ime.operation)
+ {
+ case ANDROID_IME_COMMIT_TEXT:
+ case ANDROID_IME_FINISH_COMPOSING_TEXT:
+ case ANDROID_IME_SET_COMPOSING_TEXT:
+ xfree (event.ime.text);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ android_reset_ic (FRAME_ANDROID_WINDOW (f), mode);
+
+ /* Clear extracted text flags. Since the IM has been reinitialised,
+ it should no longer be displaying extracted text. */
+ FRAME_ANDROID_OUTPUT (f)->extracted_text_flags = 0;
+
+ /* Move its selection to the specified position. */
+ android_update_selection (f, NULL);
+}
+
+/* Notice that point has moved in the F's selected window's selected
+ buffer. W is the window, and BUFFER is that buffer. */
+
+static void
+android_set_point (struct frame *f, struct window *w,
+ struct buffer *buffer)
+{
+ android_update_selection (f, w);
+}
+
+/* Notice that the composition region on F's old selected window has
+ changed. */
+
+static void
+android_compose_region_changed (struct frame *f)
+{
+ android_update_selection (f, XWINDOW (f->old_selected_window));
+}
+
+/* Notice that the text conversion has completed. */
+
+static void
+android_notify_conversion (unsigned long counter)
+{
+ int sval;
+
+ if (last_edit_counter < counter)
+ __atomic_store_n (&last_edit_counter, counter,
+ __ATOMIC_SEQ_CST);
+
+ sem_getvalue (&edit_sem, &sval);
+
+ if (sval <= 0)
+ sem_post (&edit_sem);
+}
+
+/* Android text conversion interface. */
+
+static struct textconv_interface text_conversion_interface =
+ {
+ android_reset_conversion,
+ android_set_point,
+ android_compose_region_changed,
+ android_notify_conversion,
+ };
+
+
+
+extern frame_parm_handler android_frame_parm_handlers[];
+
+#endif /* !ANDROID_STUBIFY */
+
+static struct redisplay_interface android_redisplay_interface =
+ {
+#ifndef ANDROID_STUBIFY
+ android_frame_parm_handlers,
+ gui_produce_glyphs,
+ gui_write_glyphs,
+ gui_insert_glyphs,
+ gui_clear_end_of_line,
+ android_scroll_run,
+ android_after_update_window_line,
+ NULL, /* update_window_begin */
+ NULL, /* update_window_end */
+ android_flip_and_flush,
+ gui_clear_window_mouse_face,
+ gui_get_glyph_overhangs,
+ gui_fix_overlapping_area,
+ android_draw_fringe_bitmap,
+ NULL, /* define_fringe_bitmap */
+ NULL, /* destroy_fringe_bitmap */
+ android_compute_glyph_string_overhangs,
+ android_draw_glyph_string,
+ android_define_frame_cursor,
+ android_clear_frame_area,
+ android_clear_under_internal_border,
+ android_draw_window_cursor,
+ android_draw_vertical_window_border,
+ android_draw_window_divider,
+ NULL,
+ android_show_hourglass,
+ android_hide_hourglass,
+ android_default_font_parameter,
+#endif
+ };
+
+
+
+void
+frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y)
+{
+ /* This cannot be implemented on Android, and as such is left
+ blank. */
+}
+
+char *
+get_keysym_name (int keysym)
+{
+ static char buffer[64];
+
+#ifndef ANDROID_STUBIFY
+ android_get_keysym_name (keysym, buffer, 64);
+#else
+ emacs_abort ();
+#endif
+ return buffer;
+}
+
+
+
+/* Create a struct terminal, initialize it with the Android specific
+ functions and make DISPLAY->TERMINAL point to it. */
+
+static struct terminal *
+android_create_terminal (struct android_display_info *dpyinfo)
+{
+ struct terminal *terminal;
+
+ terminal = create_terminal (output_android,
+ &android_redisplay_interface);
+ terminal->display_info.android = dpyinfo;
+ dpyinfo->terminal = terminal;
+
+ /* kboard is initialized in android_term_init. */
+
+#ifndef ANDROID_STUBIFY
+
+ terminal->clear_frame_hook = android_clear_frame;
+ terminal->ring_bell_hook = android_ring_bell;
+ terminal->toggle_invisible_pointer_hook
+ = android_toggle_invisible_pointer;
+ terminal->update_begin_hook = android_update_begin;
+ terminal->update_end_hook = android_update_end;
+ terminal->read_socket_hook = android_read_socket;
+ terminal->frame_up_to_date_hook = android_frame_up_to_date;
+ terminal->buffer_flipping_unblocked_hook
+ = android_buffer_flipping_unblocked_hook;
+ terminal->defined_color_hook = android_defined_color;
+ terminal->query_frame_background_color
+ = android_query_frame_background_color;
+ terminal->query_colors = android_query_colors;
+ terminal->mouse_position_hook = android_mouse_position;
+ terminal->get_focus_frame = android_get_focus_frame;
+ terminal->focus_frame_hook = android_focus_frame;
+ terminal->frame_rehighlight_hook = android_frame_rehighlight_hook;
+ terminal->frame_raise_lower_hook = android_frame_raise_lower;
+ terminal->frame_visible_invisible_hook
+ = android_make_frame_visible_invisible;
+ terminal->fullscreen_hook = android_fullscreen_hook;
+ terminal->iconify_frame_hook = android_iconify_frame;
+ terminal->set_window_size_hook = android_set_window_size;
+ terminal->set_frame_offset_hook = android_set_offset;
+ terminal->set_frame_alpha_hook = android_set_alpha;
+ terminal->set_new_font_hook = android_new_font;
+ terminal->set_bitmap_icon_hook = android_bitmap_icon;
+ terminal->implicit_set_name_hook = android_implicitly_set_name;
+ terminal->menu_show_hook = android_menu_show;
+ terminal->popup_dialog_hook = android_popup_dialog;
+ terminal->change_tab_bar_height_hook = android_change_tab_bar_height;
+ terminal->change_tool_bar_height_hook = android_change_tool_bar_height;
+ terminal->set_scroll_bar_default_width_hook
+ = android_set_scroll_bar_default_width;
+ terminal->set_scroll_bar_default_height_hook
+ = android_set_scroll_bar_default_height;
+ terminal->free_pixmap = android_free_pixmap_hook;
+ terminal->delete_frame_hook = android_delete_frame;
+ terminal->delete_terminal_hook = android_delete_terminal;
+
+#else
+ emacs_abort ();
+#endif
+
+ return terminal;
+}
+
+/* Initialize the Android terminal interface. The display connection
+ has already been set up by the system at this point. */
+
+void
+android_term_init (void)
+{
+ struct terminal *terminal;
+ struct android_display_info *dpyinfo;
+ Lisp_Object color_file, color_map;
+
+ dpyinfo = xzalloc (sizeof *dpyinfo);
+ terminal = android_create_terminal (dpyinfo);
+ terminal->kboard = allocate_kboard (Qandroid);
+ terminal->kboard->reference_count++;
+
+ dpyinfo->n_planes = 24;
+
+ /* This function should only be called once at startup. */
+ eassert (!x_display_list);
+ x_display_list = dpyinfo;
+
+ dpyinfo->name_list_element
+ = Fcons (build_pure_c_string ("android"), Qnil);
+
+ color_file = Fexpand_file_name (build_string ("rgb.txt"),
+ Vdata_directory);
+ color_map = Fx_load_color_file (color_file);
+
+ if (NILP (color_map))
+ fatal ("Could not read %s.\n", SDATA (color_file));
+
+ dpyinfo->color_map = color_map;
+
+#ifndef ANDROID_STUBIFY
+
+ dpyinfo->resx = android_pixel_density_x;
+ dpyinfo->resy = android_pixel_density_y;
+
+#endif
+
+ /* https://lists.gnu.org/r/emacs-devel/2015-11/msg00194.html */
+ dpyinfo->smallest_font_height = 1;
+ dpyinfo->smallest_char_width = 1;
+
+ terminal->name = xstrdup ("android");
+
+ /* The display "connection" is now set up, and it must never go
+ away. */
+ terminal->reference_count = 30000;
+
+ /* Set the baud rate to the same value it gets set to on X. */
+ baud_rate = 19200;
+
+#ifndef ANDROID_STUBIFY
+ sem_init (&edit_sem, false, 0);
+ register_textconv_interface (&text_conversion_interface);
+#endif
+}
+
+
+
+/* Set Vandroid_build_fingerprint to a reasonable value, and also
+ Vandroid_build_manufacturer. */
+
+static void
+android_set_build_fingerprint (void)
+{
+#ifdef ANDROID_STUBIFY
+ Vandroid_build_fingerprint = Qnil;
+#else
+ jclass class;
+ jfieldID field;
+ jobject string;
+ const char *data;
+
+ /* Set class to NULL so freeing an uninitialized local ref can be
+ avoided. */
+ class = NULL;
+
+ /* Likewise for string. */
+ string = NULL;
+
+ if (!android_init_gui)
+ goto fail;
+ else
+ {
+ /* Obtain Build.FINGERPRINT. Clear exceptions after each query;
+ JNI can't find Build.FINGERPRIN on some systems. */
+
+ class = (*android_java_env)->FindClass (android_java_env,
+ "android/os/Build");
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!class)
+ goto fail;
+
+ field = (*android_java_env)->GetStaticFieldID (android_java_env,
+ class,
+ "FINGERPRINT",
+ "Ljava/lang/String;");
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!field)
+ goto fail;
+
+ string
+ = (*android_java_env)->GetStaticObjectField (android_java_env,
+ class, field);
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!string)
+ goto fail;
+
+ data = (*android_java_env)->GetStringUTFChars (android_java_env,
+ string, NULL);
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!data)
+ goto fail;
+
+ Vandroid_build_fingerprint = build_string_from_utf8 (data);
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ string, data);
+
+ /* Now obtain Build.MANUFACTURER. */
+
+ ANDROID_DELETE_LOCAL_REF (string);
+ string = NULL;
+
+ field = (*android_java_env)->GetStaticFieldID (android_java_env,
+ class,
+ "MANUFACTURER",
+ "Ljava/lang/String;");
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!field)
+ goto fail;
+
+ string
+ = (*android_java_env)->GetStaticObjectField (android_java_env,
+ class, field);
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!string)
+ goto fail;
+
+ data = (*android_java_env)->GetStringUTFChars (android_java_env,
+ string, NULL);
+ (*android_java_env)->ExceptionClear (android_java_env);
+
+ if (!data)
+ goto fail;
+
+ Vandroid_build_manufacturer = build_string_from_utf8 (data);
+ (*android_java_env)->ReleaseStringUTFChars (android_java_env,
+ string, data);
+ }
+
+ if (string)
+ ANDROID_DELETE_LOCAL_REF (string);
+
+ ANDROID_DELETE_LOCAL_REF (class);
+
+ return;
+
+ fail:
+ if (class)
+ ANDROID_DELETE_LOCAL_REF (class);
+
+ Vandroid_build_fingerprint = Qnil;
+ Vandroid_build_manufacturer = Qnil;
+#endif
+}
+
+void
+syms_of_androidterm (void)
+{
+ Fprovide (Qandroid, Qnil);
+
+ DEFVAR_LISP ("android-wait-for-event-timeout",
+ Vandroid_wait_for_event_timeout,
+ doc: /* How long to wait for Android events.
+
+Emacs will wait up to this many seconds to receive events after
+making changes which affect the state of the graphical interface.
+Under some situations this can take an indefinite amount of time,
+so it is important to limit the wait.
+
+If set to a non-float value, there will be no wait at all. */);
+ Vandroid_wait_for_event_timeout = make_float (0.1);
+
+ DEFVAR_BOOL ("x-use-underline-position-properties",
+ x_use_underline_position_properties,
+ doc: /* SKIP: real doc in xterm.c. */);
+ x_use_underline_position_properties = true;
+ DEFSYM (Qx_use_underline_position_properties,
+ "x-use-underline-position-properties");
+
+ DEFVAR_BOOL ("x-underline-at-descent-line",
+ x_underline_at_descent_line,
+ doc: /* SKIP: real doc in xterm.c. */);
+ x_underline_at_descent_line = false;
+
+ DEFVAR_LISP ("android-build-fingerprint", Vandroid_build_fingerprint,
+ doc: /* String identifying the device's OS version.
+This is a string that uniquely identifies the version of Android
+Emacs is running on. */);
+ Vandroid_build_fingerprint = Qnil;
+
+ DEFVAR_LISP ("android-build-manufacturer", Vandroid_build_manufacturer,
+ doc: /* Name of the developer of the running version of Android. */);
+ Vandroid_build_manufacturer = Qnil;
+
+ /* Only defined so loadup.el loads scroll-bar.el. */
+ DEFVAR_LISP ("x-toolkit-scroll-bars", Vx_toolkit_scroll_bars,
+ doc: /* SKIP: real doc in xterm.c. */);
+ Vx_toolkit_scroll_bars = Qnil;
+
+ /* Avoid dumping Vandroid_build_fingerprint. */
+ pdumper_do_now_and_after_load (android_set_build_fingerprint);
+
+ DEFSYM (Qx_underline_at_descent_line, "x-underline-at-descent-line");
+}
+
+void
+mark_androidterm (void)
+{
+ if (x_display_list)
+ mark_object (x_display_list->color_map);
+}
diff --git a/src/androidterm.h b/src/androidterm.h
new file mode 100644
index 00000000000..e3738fb2192
--- /dev/null
+++ b/src/androidterm.h
@@ -0,0 +1,476 @@
+/* Communication module for Android terminals.
+
+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/>. */
+
+#ifndef _ANDROID_TERM_H_
+#define _ANDROID_TERM_H_
+
+#include "androidgui.h"
+#include "frame.h"
+#include "character.h"
+#include "dispextern.h"
+#include "font.h"
+
+struct android_bitmap_record
+{
+ /* The image backing the bitmap and its mask. */
+ android_pixmap pixmap, mask;
+
+ /* The file from which it comes. */
+ char *file;
+
+ /* The number of references to it. */
+ int refcount;
+
+ /* The height and width and the depth. */
+ int height, width, depth;
+
+ /* Whether or not there is a mask. */
+ bool have_mask;
+};
+
+struct android_display_info
+{
+ /* Chain of all struct android_display_info structures. */
+ struct android_display_info *next;
+
+ /* The terminal. */
+ struct terminal *terminal;
+
+ /* The root window. This field is unused. */
+ Emacs_Window root_window;
+
+ /* List possibly used only for the font cache but probably used for
+ something else too. */
+ Lisp_Object name_list_element;
+
+ /* List of predefined X colors. */
+ Lisp_Object color_map;
+
+ /* DPI of the display. */
+ double resx, resy;
+
+ /* Scratch GC for drawing a cursor in a non-default face. */
+ struct android_gc *scratch_cursor_gc;
+
+ /* Mouse highlight information. */
+ Mouse_HLInfo mouse_highlight;
+
+ /* Number of planes on this screen. Always 24. */
+ int n_planes;
+
+ /* Mask of things causing the mouse to be grabbed. */
+ int grabbed;
+
+ /* Minimum width over all characters in all fonts in font_table. */
+ int smallest_char_width;
+
+ /* Minimum font height over all fonts in font_table. */
+ int smallest_font_height;
+
+ /* The number of fonts opened for this display. */
+ int n_fonts;
+
+ /* Pointer to bitmap records. */
+ struct android_bitmap_record *bitmaps;
+
+ /* Allocated size of bitmaps field. */
+ ptrdiff_t bitmaps_size;
+
+ /* Last used bitmap index. */
+ ptrdiff_t bitmaps_last;
+
+ /* The frame currently with the input focus. */
+ struct frame *focus_frame;
+
+ /* The last frame mentioned in a focus event. */
+ struct frame *x_focus_event_frame;
+
+ /* The frame which currently has the visual highlight, and should
+ get keyboard input. It points to the focus frame's selected
+ window's frame, but can differ. */
+ struct frame *highlight_frame;
+
+ /* The frame waiting to be auto-raised in android_read_socket. */
+ struct frame *pending_autoraise_frame;
+
+ /* The frame where the mouse was the last time a button event
+ happened. */
+ struct frame *last_mouse_frame;
+
+ /* The frame where the mouse was the last time the mouse glyph
+ changed. */
+ struct frame *last_mouse_glyph_frame;
+
+ /* The frame where the mouse was the last time mouse motion
+ happened. */
+ struct frame *last_mouse_motion_frame;
+
+ /* Position where the mouse was last time we reported a motion.
+ This is a position on last_mouse_motion_frame. It is used in to
+ report the mouse position as well: see
+ android_mouse_position. */
+ int last_mouse_motion_x, last_mouse_motion_y;
+
+ /* Where the mouse was the last time the mouse moved. */
+ Emacs_Rectangle last_mouse_glyph;
+
+ /* The time of the last mouse movement. */
+ Time last_mouse_movement_time;
+
+ /* ID of the last menu event received. -1 means Emacs is waiting
+ for a context menu event. */
+ int menu_event_id;
+
+ /* The invisible cursor used for pointer blanking. */
+ android_cursor invisible_cursor;
+};
+
+/* Structure representing a single tool (finger or stylus) pressed
+ onto a frame. */
+
+struct android_touch_point
+{
+ /* The next tool on this list. */
+ struct android_touch_point *next;
+
+ /* The tool ID and the last known X and Y positions. */
+ int tool_id, x, y;
+
+ /* Whether or not the tool is pressed on the tool bar. */
+ bool tool_bar_p;
+};
+
+struct android_output
+{
+ /* Graphics contexts for the default font. */
+ struct android_gc *normal_gc, *reverse_gc, *cursor_gc;
+
+ /* The window used for this frame. */
+ Emacs_Window window;
+
+ /* Unused field. */
+ Emacs_Window parent_desc;
+
+ /* Default ASCII font of this frame. */
+ struct font *font;
+
+ /* The baseline offset of the default ASCII font. */
+ int baseline_offset;
+
+ /* If a fontset is specified for this frame instead of font, this
+ value contains an ID of the fontset, else -1. */
+ int fontset;
+
+ /* Various colors. */
+ unsigned long cursor_pixel;
+ unsigned long mouse_pixel;
+ unsigned long cursor_foreground_pixel;
+
+ /* Foreground color for scroll bars. A value of -1 means use the
+ default (black for non-toolkit scroll bars). */
+ unsigned long scroll_bar_foreground_pixel;
+
+ /* Background color for scroll bars. A value of -1 means use the
+ default (background color of the frame for non-toolkit scroll
+ bars). */
+ unsigned long scroll_bar_background_pixel;
+
+ /* Cursors associated with this frame. */
+ Emacs_Cursor text_cursor;
+ Emacs_Cursor nontext_cursor;
+ Emacs_Cursor modeline_cursor;
+ Emacs_Cursor hand_cursor;
+ Emacs_Cursor hourglass_cursor;
+ Emacs_Cursor horizontal_drag_cursor;
+ Emacs_Cursor vertical_drag_cursor;
+ Emacs_Cursor current_cursor;
+ Emacs_Cursor left_edge_cursor;
+ Emacs_Cursor top_left_corner_cursor;
+ Emacs_Cursor top_edge_cursor;
+ Emacs_Cursor top_right_corner_cursor;
+ Emacs_Cursor right_edge_cursor;
+ Emacs_Cursor bottom_right_corner_cursor;
+ Emacs_Cursor bottom_edge_cursor;
+ Emacs_Cursor bottom_left_corner_cursor;
+
+ /* Whether or not the hourglass cursor is being displayed. */
+ bool hourglass;
+
+ /* This is the Emacs structure for the display this frame is on. */
+ struct android_display_info *display_info;
+
+ /* True if this frame was ever previously visible. */
+ bool_bf has_been_visible : 1;
+
+ /* True if this frame's alpha value is the same for both the active
+ and inactive states. */
+ bool_bf alpha_identical_p : 1;
+
+ /* Flag that indicates whether or not the frame contents are
+ complete and can be safely flushed while handling async
+ input. */
+ bool_bf complete : 1;
+
+ /* True that indicates whether or not a buffer flip is required
+ because the frame contents have been dirtied. */
+ bool_bf need_buffer_flip : 1;
+
+ /* Whether or not the input method should be notified every time the
+ position of this frame's selected window changes. */
+ bool_bf need_cursor_updates : 1;
+
+ /* Relief GCs, colors etc. */
+ struct relief {
+ struct android_gc *gc;
+ unsigned long pixel;
+ } black_relief, white_relief;
+
+ /* The background for which the above relief GCs were set up.
+ They are changed only when a different background is involved. */
+ unsigned long relief_background;
+
+ /* Focus state. Only present for consistency with X; it is actually
+ a boolean. */
+ int focus_state;
+
+ /* List of all tools (either styluses or fingers) pressed onto the
+ frame. */
+ struct android_touch_point *touch_points;
+
+ /* Flags associated with the last request to obtain ``extracted
+ text''. */
+ int extracted_text_flags;
+
+ /* Token asssociated with that request. */
+ int extracted_text_token;
+
+ /* The number of characters of extracted text wanted by the IM. */
+ int extracted_text_hint;
+};
+
+enum
+ {
+ /* Values for focus_state, used as bit mask. EXPLICIT means we
+ received a FocusIn for the frame and know it has the focus.
+ IMPLICIT means we received an EnterNotify and the frame may
+ have the focus if no window manager is running. FocusOut and
+ LeaveNotify clears EXPLICIT/IMPLICIT. */
+ FOCUS_NONE = 0,
+ FOCUS_IMPLICIT = 1,
+ FOCUS_EXPLICIT = 2
+ };
+
+/* Return the Android output data for frame F. */
+#define FRAME_ANDROID_OUTPUT(f) ((f)->output_data.android)
+#define FRAME_OUTPUT_DATA(f) ((f)->output_data.android)
+
+/* Return the Android window used for displaying data in frame F. */
+#define FRAME_ANDROID_WINDOW(f) ((f)->output_data.android->window)
+#define FRAME_NATIVE_WINDOW(f) ((f)->output_data.android->window)
+
+/* Return the need-buffer-flip flag for frame F. */
+#define FRAME_ANDROID_NEED_BUFFER_FLIP(f) \
+ ((f)->output_data.android->need_buffer_flip)
+
+/* Return the drawable used for rendering to frame F and mark the
+ frame as needing a buffer flip later. There's no easy way to run
+ code after any drawing command, but code can be run whenever
+ someone asks for the handle necessary to draw. */
+#define FRAME_ANDROID_DRAWABLE(f) \
+ (((f))->output_data.android->need_buffer_flip = true, \
+ FRAME_ANDROID_WINDOW ((f)))
+
+/* Return whether or not the frame F has been completely drawn. Used
+ while handling async input. */
+#define FRAME_ANDROID_COMPLETE_P(f) \
+ ((f)->output_data.android->complete)
+
+#define FRAME_FONT(f) ((f)->output_data.android->font)
+#define FRAME_FONTSET(f) ((f)->output_data.android->fontset)
+
+#define FRAME_BASELINE_OFFSET(f) \
+ ((f)->output_data.android->baseline_offset)
+
+/* This gives the android_display_info structure for the display F is
+ on. */
+#define FRAME_DISPLAY_INFO(f) ((f)->output_data.android->display_info)
+
+/* Some things for X compatibility. */
+#define BLACK_PIX_DEFAULT(f) 0
+#define WHITE_PIX_DEFAULT(f) 0xffffffff
+
+/* Android-specific scroll bar stuff. */
+
+/* We represent scroll bars as lisp vectors. This allows us to place
+ references to them in windows without worrying about whether we'll
+ end up with windows referring to dead scroll bars; the garbage
+ collector will free it when its time comes.
+
+ We use struct scroll_bar as a template for accessing fields of the
+ vector. */
+
+struct scroll_bar
+{
+ /* These fields are shared by all vectors. */
+ union vectorlike_header header;
+
+ /* The window we're a scroll bar for. */
+ Lisp_Object window;
+
+ /* The next and previous in the chain of scroll bars in this frame. */
+ Lisp_Object next, prev;
+
+ /* Fields after 'prev' are not traced by the GC. */
+
+ /* The X window representing this scroll bar. */
+ Emacs_Window x_window;
+
+ /* The position and size of the scroll bar in pixels, relative to the
+ frame. */
+ int top, left, width, height;
+
+ /* The starting and ending positions of the handle, relative to the
+ handle area (i.e. zero is the top position, not
+ SCROLL_BAR_TOP_BORDER). If they're equal, that means the handle
+ hasn't been drawn yet.
+
+ These are not actually the locations where the beginning and end
+ are drawn; in order to keep handles from becoming invisible when
+ editing large files, we establish a minimum height by always
+ drawing handle bottoms VERTICAL_SCROLL_BAR_MIN_HANDLE pixels below
+ where they would be normally; the bottom and top are in a
+ different coordinate system. */
+ int start, end;
+
+ /* If the scroll bar handle is currently being dragged by the user,
+ this is the number of pixels from the top of the handle to the
+ place where the user grabbed it. If the handle isn't currently
+ being dragged, this is -1. */
+ int dragging;
+
+ /* True if the scroll bar is horizontal. */
+ bool horizontal;
+};
+
+/* Turning a lisp vector value into a pointer to a struct scroll_bar. */
+#define XSCROLL_BAR(vec) ((struct scroll_bar *) XVECTOR (vec))
+
+
+
+/* This is a chain of structures for all the Android displays
+ currently in use. There is only ever one, but the rest of Emacs is
+ written with systems on which there can be many in mind. */
+extern struct android_display_info *x_display_list;
+
+
+
+/* Start of function definitions. These should be a neat subset of
+ the same ones in xterm.h, and come in the same order. */
+
+/* From androidfns.c. */
+
+extern void android_free_gcs (struct frame *);
+extern void android_default_font_parameter (struct frame *, Lisp_Object);
+extern void android_set_preeditarea (struct window *, int, int);
+
+/* Defined in androidterm.c. */
+
+extern void android_term_init (void);
+extern void android_set_window_size (struct frame *, bool, int, int);
+extern void android_iconify_frame (struct frame *);
+extern void android_make_frame_visible (struct frame *);
+extern void android_make_frame_invisible (struct frame *);
+extern void android_free_frame_resources (struct frame *);
+
+extern int android_parse_color (struct frame *, const char *,
+ Emacs_Color *);
+extern bool android_alloc_nearest_color (struct frame *, Emacs_Color *);
+extern void android_query_colors (struct frame *, Emacs_Color *, int);
+extern void android_clear_under_internal_border (struct frame *);
+
+extern void syms_of_androidterm (void);
+extern void mark_androidterm (void);
+
+/* Defined in androidfns.c. */
+
+extern void android_change_tab_bar_height (struct frame *, int);
+extern void android_change_tool_bar_height (struct frame *, int);
+extern void android_set_scroll_bar_default_width (struct frame *);
+extern void android_set_scroll_bar_default_height (struct frame *);
+extern bool android_defined_color (struct frame *, const char *,
+ Emacs_Color *, bool, bool);
+extern void android_implicitly_set_name (struct frame *, Lisp_Object,
+ Lisp_Object);
+extern void android_explicitly_set_name (struct frame *, Lisp_Object,
+ Lisp_Object);
+
+extern void syms_of_androidfns (void);
+
+/* Defined in androidfont.c. */
+
+extern struct font_driver androidfont_driver;
+
+extern void init_androidfont (void);
+extern void syms_of_androidfont (void);
+
+extern void android_finalize_font_entity (struct font_entity *);
+
+/* Defined in androidmenu.c. */
+
+#ifndef ANDROID_STUBIFY
+
+extern unsigned int current_menu_serial;
+
+#endif
+
+extern Lisp_Object android_menu_show (struct frame *, int, int, int,
+ Lisp_Object, const char **);
+extern Lisp_Object android_popup_dialog (struct frame *, Lisp_Object,
+ Lisp_Object);
+
+extern void init_androidmenu (void);
+extern void syms_of_androidmenu (void);
+
+/* Defined in sfntfont-android.c. */
+
+extern const struct font_driver android_sfntfont_driver;
+
+extern void sfntfont_android_shrink_scanline_buffer (void);
+extern void init_sfntfont_android (void);
+extern void syms_of_sfntfont_android (void);
+
+/* Defined in androidselect.c */
+
+#ifndef ANDROID_STUBIFY
+
+extern void init_androidselect (void);
+extern void syms_of_androidselect (void);
+
+#endif
+
+
+
+#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color) ((color) & 0xff)
+
+
+
+#endif /* _ANDROID_TERM_H_ */
diff --git a/src/buffer.c b/src/buffer.c
index 7951b7ac322..668c7035724 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -4719,6 +4719,7 @@ init_buffer_once (void)
#ifdef HAVE_TREE_SITTER
XSETFASTINT (BVAR (&buffer_local_flags, ts_parser_list), idx); ++idx;
#endif
+ XSETFASTINT (BVAR (&buffer_local_flags, text_conversion_style), idx); ++idx;
XSETFASTINT (BVAR (&buffer_local_flags, cursor_in_non_selected_windows), idx); ++idx;
/* buffer_local_flags contains no pointers, so it's safe to treat it
@@ -4790,6 +4791,9 @@ init_buffer_once (void)
#ifdef HAVE_TREE_SITTER
bset_ts_parser_list (&buffer_defaults, Qnil);
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ bset_text_conversion_style (&buffer_defaults, Qnil);
+#endif
bset_cursor_in_non_selected_windows (&buffer_defaults, Qt);
bset_enable_multibyte_characters (&buffer_defaults, Qt);
@@ -5866,6 +5870,26 @@ If t, displays a cursor related to the usual cursor type
You can also specify the cursor type as in the `cursor-type' variable.
Use Custom to set this variable and update the display. */);
+ /* While this is defined here, each *term.c module must implement
+ the logic itself. */
+
+ DEFVAR_PER_BUFFER ("text-conversion-style", &BVAR (current_buffer,
+ text_conversion_style),
+ Qnil,
+ doc: /* How the on screen keyboard's input method should insert in this buffer.
+When nil, the input method will be disabled and an ordinary keyboard
+will be displayed in its place.
+When the symbol `action', the input method will insert text directly, but
+will send `return' key events instead of inserting new line characters.
+Any other value means that the input method will insert text directly.
+
+If you need to make non-buffer local changes to this variable, use
+`overriding-text-conversion-style', which see.
+
+This variable does not take immediate effect when set; rather, it
+takes effect upon the next redisplay after the selected window or
+buffer changes. */);
+
DEFVAR_LISP ("kill-buffer-query-functions", Vkill_buffer_query_functions,
doc: /* List of functions called with no args to query before killing a buffer.
The buffer being killed will be current while the functions are running.
diff --git a/src/buffer.h b/src/buffer.h
index e700297a264..e71ffe28045 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -566,6 +566,11 @@ struct buffer
/* A list of tree-sitter parsers for this buffer. */
Lisp_Object ts_parser_list_;
#endif
+
+ /* What type of text conversion the input method should apply to
+ this buffer. */
+ Lisp_Object text_conversion_style_;
+
/* Cursor type to display in non-selected windows.
t means to use hollow box cursor.
See `cursor-type' for other values. */
@@ -842,6 +847,12 @@ bset_width_table (struct buffer *b, Lisp_Object val)
b->width_table_ = val;
}
+INLINE void
+bset_text_conversion_style (struct buffer *b, Lisp_Object val)
+{
+ b->text_conversion_style_ = val;
+}
+
/* BUFFER_CEILING_OF (resp. BUFFER_FLOOR_OF), when applied to n, return
the max (resp. min) p such that
diff --git a/src/callproc.c b/src/callproc.c
index 6f3d4fad9be..ee5195385de 100644
--- a/src/callproc.c
+++ b/src/callproc.c
@@ -92,6 +92,10 @@ extern char **environ;
#include "pgtkterm.h"
#endif
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif /* HAVE_ANDROID */
+
/* Pattern used by call-process-region to make temp files. */
static Lisp_Object Vtemp_file_name_pattern;
@@ -144,7 +148,11 @@ static CHILD_SETUP_TYPE child_setup (int, int, int, char **, char **,
directory if it's unreachable. If ENCODE is true, return as a string
suitable for a system call; otherwise, return a string in its
internal representation. Signal an error if the result would not be
- an accessible directory. */
+ an accessible directory.
+
+ If the default directory lies inside a special directory which
+ cannot be made the current working directory, and ENCODE is also
+ set, simply return the home directory. */
Lisp_Object
get_current_directory (bool encode)
@@ -157,6 +165,19 @@ get_current_directory (bool encode)
if (NILP (dir))
dir = build_string ("~");
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+
+ /* If DIR is an asset directory or a content directory, return
+ the home directory instead. */
+
+ if (encode && (!strcmp (SSDATA (dir), "/assets")
+ || !strncmp (SSDATA (dir), "/assets/", 8)
+ || !strcmp (SSDATA (dir), "/content")
+ || !strncmp (SSDATA (dir), "/content/", 9)))
+ dir = build_string ("~");
+
+#endif /* HAVE_ANDROID && ANDROID_STUBIFY */
+
dir = expand_and_dir_to_file (dir);
Lisp_Object encoded_dir = ENCODE_FILE (remove_slash_colon (dir));
@@ -499,7 +520,7 @@ call_process (ptrdiff_t nargs, Lisp_Object *args, int filefd,
int ok;
ok = openp (Vexec_path, args[0], Vexec_suffixes, &path,
- make_fixnum (X_OK), false, false);
+ make_fixnum (X_OK), false, false, NULL);
if (ok < 0)
report_file_error ("Searching for program", args[0]);
}
@@ -1421,6 +1442,18 @@ emacs_spawn (pid_t *newpid, int std_in, int std_out, int std_err,
const char *pty_name, bool pty_in, bool pty_out,
const sigset_t *oldset)
{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Android 10 and later don't allow directly executing programs
+ installed in the application data directory. Emacs provides a
+ loader binary which replaces the `execve' system call for it and
+ all its children. On these systems, rewrite the command line to
+ call that loader binary instead. */
+
+ if (android_rewrite_spawn_argv ((const char ***) &argv))
+ return 1;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+
#if USABLE_POSIX_SPAWN
/* Prefer the simpler `posix_spawn' if available. `posix_spawn'
doesn't yet support setting up pseudoterminals, so we fall back
@@ -1988,7 +2021,12 @@ init_callproc (void)
dir_warning ("arch-independent data dir", Vdata_directory);
sh = getenv ("SHELL");
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* The Android shell is found under /system/bin, not /bin. */
+ Vshell_file_name = build_string (sh ? sh : "/system/bin/sh");
+#else
Vshell_file_name = build_string (sh ? sh : "/bin/sh");
+#endif
Lisp_Object gamedir = Qnil;
if (PATH_GAME)
@@ -2111,6 +2149,72 @@ use.
See `setenv' and `getenv'. */);
Vprocess_environment = Qnil;
+ DEFVAR_LISP ("ctags-program-name", Vctags_program_name,
+ doc: /* Name of the `ctags' program distributed with Emacs.
+Use this instead of calling `ctags' directly, as `ctags' may have been
+renamed to comply with executable naming restrictions on the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vctags_program_name = build_pure_c_string ("ctags");
+#else
+ Vctags_program_name = build_pure_c_string ("libctags.so");
+#endif
+
+ DEFVAR_LISP ("etags-program-name", Vetags_program_name,
+ doc: /* Name of the `etags' program distributed with Emacs.
+Use this instead of calling `etags' directly, as `etags' may have been
+renamed to comply with executable naming restrictions on the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vetags_program_name = build_pure_c_string ("etags");
+#else
+ Vetags_program_name = build_pure_c_string ("libetags.so");
+#endif
+
+ DEFVAR_LISP ("hexl-program-name", Vhexl_program_name,
+ doc: /* Name of the `hexl' program distributed with Emacs.
+Use this instead of calling `hexl' directly, as `hexl' may have been
+renamed to comply with executable naming restrictions on the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vhexl_program_name = build_pure_c_string ("hexl");
+#else
+ Vhexl_program_name = build_pure_c_string ("libhexl.so");
+#endif
+
+ DEFVAR_LISP ("emacsclient-program-name", Vemacsclient_program_name,
+ doc: /* Name of the `emacsclient' program distributed with Emacs.
+Use this instead of calling `emacsclient' directly, as `emacsclient'
+may have been renamed to comply with executable naming restrictions on
+the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vemacsclient_program_name = build_pure_c_string ("emacsclient");
+#else
+ Vemacsclient_program_name = build_pure_c_string ("libemacsclient.so");
+#endif
+
+ DEFVAR_LISP ("movemail-program-name", Vmovemail_program_name,
+ doc: /* Name of the `movemail' program distributed with Emacs.
+Use this instead of calling `movemail' directly, as `movemail'
+may have been renamed to comply with executable naming restrictions on
+the system. */);
+ /* Don't change the name of `movemail' if Emacs is being built to
+ use movemail from another source. */
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY \
+ || defined HAVE_MAILUTILS
+ Vmovemail_program_name = build_pure_c_string ("movemail");
+#else
+ Vmovemail_program_name = build_pure_c_string ("libmovemail.so");
+#endif
+
+ DEFVAR_LISP ("ebrowse-program-name", Vebrowse_program_name,
+ doc: /* Name of the `ebrowse' program distributed with Emacs.
+Use this instead of calling `ebrowse' directly, as `ebrowse'
+may have been renamed to comply with executable naming restrictions on
+the system. */);
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+ Vebrowse_program_name = build_pure_c_string ("ebrowse");
+#else
+ Vebrowse_program_name = build_pure_c_string ("libebrowse.so");
+#endif
+
defsubr (&Scall_process);
defsubr (&Sgetenv_internal);
defsubr (&Scall_process_region);
diff --git a/src/charset.c b/src/charset.c
index 7987ffa0c5e..c532f79d282 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -486,7 +486,8 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile,
specpdl_ref count = SPECPDL_INDEX ();
record_unwind_protect_nothing ();
specbind (Qfile_name_handler_alist, Qnil);
- fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false);
+ fd = openp (Vcharset_map_path, mapfile, suffixes, NULL, Qnil, false, false,
+ NULL);
fp = fd < 0 ? 0 : fdopen (fd, "r");
if (!fp)
{
@@ -544,7 +545,7 @@ load_charset_map_from_file (struct charset *charset, Lisp_Object mapfile,
entries->entry[idx].c = c;
n_entries++;
}
- fclose (fp);
+ emacs_fclose (fp);
clear_unwind_protect (count);
load_charset_map (charset, head, n_entries, control_flag);
diff --git a/src/coding.c b/src/coding.c
index a2e0d7040f8..fbf10188819 100644
--- a/src/coding.c
+++ b/src/coding.c
@@ -8495,7 +8495,7 @@ preferred_coding_system (void)
return CODING_ID_NAME (id);
}
-#if defined (WINDOWSNT) || defined (CYGWIN)
+#if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID
Lisp_Object
from_unicode (Lisp_Object str)
@@ -8513,10 +8513,31 @@ from_unicode (Lisp_Object str)
Lisp_Object
from_unicode_buffer (const wchar_t *wstr)
{
+#if defined WINDOWSNT || defined CYGWIN
/* We get one of the two final null bytes for free. */
ptrdiff_t len = 1 + sizeof (wchar_t) * wcslen (wstr);
AUTO_STRING_WITH_LEN (str, (char *) wstr, len);
return from_unicode (str);
+#else
+ /* This code is used only on Android, where little endian UTF-16
+ strings are extended to 32-bit wchar_t. */
+
+ uint16_t *words;
+ size_t length, i;
+
+ length = wcslen (wstr) + 1;
+
+ USE_SAFE_ALLOCA;
+ SAFE_NALLOCA (words, sizeof *words, length);
+
+ for (i = 0; i < length - 1; ++i)
+ words[i] = wstr[i];
+
+ words[i] = '\0';
+ AUTO_STRING_WITH_LEN (str, (char *) words,
+ (length - 1) * sizeof *words);
+ return unbind_to (sa_count, from_unicode (str));
+#endif
}
wchar_t *
@@ -8536,7 +8557,7 @@ to_unicode (Lisp_Object str, Lisp_Object *buf)
return WCSDATA (*buf);
}
-#endif /* WINDOWSNT || CYGWIN */
+#endif /* WINDOWSNT || CYGWIN || HAVE_ANDROID */
/*** 8. Emacs Lisp library functions ***/
@@ -11735,7 +11756,7 @@ syms_of_coding (void)
DEFSYM (Qutf_8_unix, "utf-8-unix");
DEFSYM (Qutf_8_emacs, "utf-8-emacs");
-#if defined (WINDOWSNT) || defined (CYGWIN)
+#if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID
/* No, not utf-16-le: that one has a BOM. */
DEFSYM (Qutf_16le, "utf-16le");
#endif
diff --git a/src/coding.h b/src/coding.h
index b8d4f5f27e1..08c29c884a5 100644
--- a/src/coding.h
+++ b/src/coding.h
@@ -709,7 +709,7 @@ extern void encode_coding_object (struct coding_system *,
/* Defined in this file. */
INLINE int surrogates_to_codepoint (int, int);
-#if defined (WINDOWSNT) || defined (CYGWIN)
+#if defined (WINDOWSNT) || defined (CYGWIN) || defined HAVE_ANDROID
/* These functions use Lisp string objects to store the UTF-16LE
strings that modern versions of Windows expect. These strings are
@@ -732,7 +732,7 @@ extern Lisp_Object from_unicode (Lisp_Object str);
/* Convert WSTR to an Emacs string. */
extern Lisp_Object from_unicode_buffer (const wchar_t *wstr);
-#endif /* WINDOWSNT || CYGWIN */
+#endif /* WINDOWSNT || CYGWIN || HAVE_ANDROID */
/* Macros for backward compatibility. */
diff --git a/src/conf_post.h b/src/conf_post.h
index 0d5f90a6910..f31e012dc6e 100644
--- a/src/conf_post.h
+++ b/src/conf_post.h
@@ -461,3 +461,13 @@ extern int emacs_setenv_TZ (char const *);
#else
# define UNINIT /* empty */
#endif
+
+/* MB_CUR_MAX is often broken on systems which copy-paste LLVM
+ headers, so replace its definition with a working one if
+ necessary. */
+
+#ifdef REPLACEMENT_MB_CUR_MAX
+#include <stdlib.h>
+#undef MB_CUR_MAX
+#define MB_CUR_MAX REPLACEMENT_MB_CUR_MAX
+#endif /* REPLACEMENT_MB_CUR_MAX */
diff --git a/src/dired.c b/src/dired.c
index 3f55c4c3830..93487d552e2 100644
--- a/src/dired.c
+++ b/src/dired.c
@@ -44,6 +44,21 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "msdos.h" /* for fstatat */
#endif
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+typedef DIR emacs_dir;
+#define emacs_readdir readdir
+#define emacs_closedir closedir
+#else
+
+#include "android.h"
+
+/* The Android emulation of dirent stuff is required to be able to
+ list the /assets special directory. */
+typedef struct android_dir emacs_dir;
+#define emacs_readdir android_readdir
+#define emacs_closedir android_closedir
+#endif
+
#ifdef WINDOWSNT
extern int is_slow_fs (const char *);
#endif
@@ -78,19 +93,33 @@ dirent_type (struct dirent *dp)
#endif
}
-static DIR *
+static emacs_dir *
open_directory (Lisp_Object dirname, Lisp_Object encoded_dirname, int *fdp)
{
char *name = SSDATA (encoded_dirname);
- DIR *d;
+ emacs_dir *d;
int fd, opendir_errno;
-#ifdef DOS_NT
- /* Directories cannot be opened. The emulation assumes that any
- file descriptor other than AT_FDCWD corresponds to the most
- recently opened directory. This hack is good enough for Emacs. */
+#if defined DOS_NT || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ /* On DOS_NT, directories cannot be opened. The emulation assumes
+ that any file descriptor other than AT_FDCWD corresponds to the
+ most recently opened directory. This hack is good enough for
+ Emacs.
+
+ This code is also used on Android for a different reason: a
+ special `assets' directory outside the normal file system is used
+ to open assets inside the Android application package, and must
+ be listed using the opendir-like interface provided in
+ android.h. */
fd = 0;
+#ifndef HAVE_ANDROID
d = opendir (name);
+#else
+ d = android_opendir (name);
+
+ if (d)
+ fd = android_dirfd (d);
+#endif
opendir_errno = errno;
#else
fd = emacs_open (name, O_RDONLY | O_DIRECTORY, 0);
@@ -125,7 +154,7 @@ directory_files_internal_w32_unwind (Lisp_Object arg)
static void
directory_files_internal_unwind (void *d)
{
- closedir (d);
+ emacs_closedir (d);
}
/* Return the next directory entry from DIR; DIR's name is DIRNAME.
@@ -133,12 +162,12 @@ directory_files_internal_unwind (void *d)
Signal any unrecoverable errors. */
static struct dirent *
-read_dirent (DIR *dir, Lisp_Object dirname)
+read_dirent (emacs_dir *dir, Lisp_Object dirname)
{
while (true)
{
errno = 0;
- struct dirent *dp = readdir (dir);
+ struct dirent *dp = emacs_readdir (dir);
if (dp || errno == 0)
return dp;
if (! (errno == EAGAIN || errno == EINTR))
@@ -190,7 +219,10 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
Lisp_Object encoded_dirfilename = ENCODE_FILE (dirfilename);
int fd;
- DIR *d = open_directory (dirfilename, encoded_dirfilename, &fd);
+
+ /* Keep in mind that FD is not always a real file descriptor on
+ Android. */
+ emacs_dir *d = open_directory (dirfilename, encoded_dirfilename, &fd);
/* Unfortunately, we can now invoke expand-file-name and
file-attributes on filenames, both of which can throw, so we must
@@ -300,7 +332,7 @@ directory_files_internal (Lisp_Object directory, Lisp_Object full,
list = Fcons (attrs ? Fcons (finalname, fileattrs) : finalname, list);
}
- closedir (d);
+ emacs_closedir (d);
#ifdef WINDOWSNT
if (attrs)
Vw32_get_true_file_attributes = w32_save;
@@ -514,7 +546,7 @@ file_name_completion (Lisp_Object file, Lisp_Object dirname, bool all_flag,
}
}
int fd;
- DIR *d = open_directory (dirname, encoded_dir, &fd);
+ emacs_dir *d = open_directory (dirname, encoded_dir, &fd);
record_unwind_protect_ptr (directory_files_internal_unwind, d);
/* Loop reading directory entries. */
@@ -855,7 +887,9 @@ file_name_completion_dirp (int fd, struct dirent *dp, ptrdiff_t len)
char *subdir_name = SAFE_ALLOCA (len + 2);
memcpy (subdir_name, dp->d_name, len);
strcpy (subdir_name + len, "/");
- bool dirp = faccessat (fd, subdir_name, F_OK, AT_EACCESS) == 0;
+
+ bool dirp = sys_faccessat (fd, subdir_name,
+ F_OK, AT_EACCESS) == 0;
SAFE_FREE ();
return dirp;
}
@@ -979,14 +1013,15 @@ file_attributes (int fd, char const *name,
int err = EINVAL;
-#if defined O_PATH && !defined HAVE_CYGWIN_O_PATH_BUG
+#if defined O_PATH && !defined HAVE_CYGWIN_O_PATH_BUG \
+ && !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
int namefd = emacs_openat (fd, name, O_PATH | O_CLOEXEC | O_NOFOLLOW, 0);
if (namefd < 0)
err = errno;
else
{
record_unwind_protect_int (close_file_unwind, namefd);
- if (fstat (namefd, &s) != 0)
+ if (sys_fstat (namefd, &s) != 0)
{
err = errno;
/* The Linux kernel before version 3.6 does not support
diff --git a/src/dispextern.h b/src/dispextern.h
index ece128949f5..36e998070a4 100644
--- a/src/dispextern.h
+++ b/src/dispextern.h
@@ -53,8 +53,15 @@ typedef struct
unsigned short red, green, blue;
} Emacs_Color;
+#ifndef HAVE_ANDROID
/* Accommodate X's usage of None as a null resource ID. */
#define No_Cursor (NULL)
+#else
+/* Android doesn't support cursors and also uses handles. */
+#define No_Cursor 0
+#endif
+
+#ifndef HAVE_ANDROID
/* XRectangle-like struct used by non-X GUI code. */
typedef struct
@@ -63,6 +70,12 @@ typedef struct
unsigned width, height;
} Emacs_Rectangle;
+#else
+
+typedef struct android_rectangle Emacs_Rectangle;
+
+#endif
+
/* XGCValues-like struct used by non-X GUI code. */
typedef struct
{
@@ -144,6 +157,13 @@ typedef Emacs_Pixmap Emacs_Pix_Container;
typedef Emacs_Pixmap Emacs_Pix_Context;
#endif
+#ifdef HAVE_ANDROID
+#include "androidgui.h"
+typedef struct android_display_info Display_Info;
+typedef struct android_image *Emacs_Pix_Container;
+typedef struct android_image *Emacs_Pix_Context;
+#endif
+
#ifdef HAVE_WINDOW_SYSTEM
# include <time.h>
# include "fontset.h"
@@ -157,6 +177,22 @@ typedef void *Emacs_Cursor;
#define NativeRectangle int
#endif
+#ifdef HAVE_WINDOW_SYSTEM
+
+/* ``box'' structure similar to that found in the X sample server,
+ meaning that X2 and Y2 are not actually the end of the box, but one
+ pixel past the end of the box, which makes checking for overlaps
+ less necessary. This is convenient to use in every GUI port. */
+
+struct gui_box
+{
+ /* Bounds of the box. */
+ int x1, y1;
+ int x2, y2;
+};
+
+#endif
+
/* Text cursor types. */
enum text_cursor_kinds
@@ -1401,6 +1437,8 @@ struct glyph_string
/* The GC to use for drawing this glyph string. */
#if defined (HAVE_X_WINDOWS)
GC gc;
+#elif defined HAVE_ANDROID
+ struct android_gc *gc;
#endif
#if defined (HAVE_NTGUI)
Emacs_GC *gc;
@@ -1681,6 +1719,8 @@ struct face
drawing the characters in this face. */
# ifdef HAVE_X_WINDOWS
GC gc;
+# elif defined HAVE_ANDROID
+ struct android_gc *gc;
# else
Emacs_GC *gc;
# endif
@@ -3057,8 +3097,9 @@ struct redisplay_interface
#ifdef HAVE_WINDOW_SYSTEM
-# if (defined USE_CAIRO || defined HAVE_XRENDER \
- || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU)
+# if (defined USE_CAIRO || defined HAVE_XRENDER \
+ || defined HAVE_NS || defined HAVE_NTGUI || defined HAVE_HAIKU \
+ || defined HAVE_ANDROID)
# define HAVE_NATIVE_TRANSFORMS
# endif
@@ -3094,6 +3135,13 @@ struct image
int original_width, original_height;
# endif
#endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_ANDROID
+ /* Android images of the image, corresponding to the above Pixmaps.
+ Non-NULL means it and its Pixmap counterpart may be out of sync
+ and the latter is outdated. NULL means the X image has been
+ synchronized to Pixmap. */
+ struct android_image *ximg, *mask_img;
+#endif /* HAVE_ANDROID */
#ifdef HAVE_NTGUI
XFORM xform;
#endif
@@ -3491,6 +3539,9 @@ extern void expose_frame (struct frame *, int, int, int, int);
extern bool gui_intersect_rectangles (const Emacs_Rectangle *,
const Emacs_Rectangle *,
Emacs_Rectangle *);
+extern void gui_union_rectangles (const Emacs_Rectangle *,
+ const Emacs_Rectangle *,
+ Emacs_Rectangle *);
extern void gui_consider_frame_title (Lisp_Object);
#endif /* HAVE_WINDOW_SYSTEM */
@@ -3499,9 +3550,11 @@ extern void gui_clear_window_mouse_face (struct window *);
extern void cancel_mouse_face (struct frame *);
extern bool clear_mouse_face (Mouse_HLInfo *);
extern bool cursor_in_mouse_face_p (struct window *w);
+#ifndef HAVE_ANDROID
extern void tty_draw_row_with_mouse_face (struct window *, struct glyph_row *,
int, int, enum draw_glyphs_face);
extern void display_tty_menu_item (const char *, int, int, int, int, bool);
+#endif
extern struct glyph *x_y_to_hpos_vpos (struct window *, int, int, int *, int *,
int *, int *, int *);
/* Flags passed to try_window. */
@@ -3563,7 +3616,7 @@ void prepare_image_for_display (struct frame *, struct image *);
ptrdiff_t lookup_image (struct frame *, Lisp_Object, int);
#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_NS \
- || defined HAVE_HAIKU
+ || defined HAVE_HAIKU || defined HAVE_ANDROID
#define RGB_PIXEL_COLOR unsigned long
#endif
@@ -3644,6 +3697,9 @@ void gamma_correct (struct frame *, COLORREF *);
#ifdef HAVE_HAIKU
void gamma_correct (struct frame *, Emacs_Color *);
#endif
+#ifdef HAVE_ANDROID
+extern void gamma_correct (struct frame *, Emacs_Color *);
+#endif
#ifdef HAVE_WINDOW_SYSTEM
diff --git a/src/dispnew.c b/src/dispnew.c
index a928a5d1b14..54948af975b 100644
--- a/src/dispnew.c
+++ b/src/dispnew.c
@@ -43,6 +43,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "xwidget.h"
#include "pdumper.h"
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
#ifdef HAVE_WINDOW_SYSTEM
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
@@ -788,7 +792,7 @@ clear_current_matrices (register struct frame *f)
if (f->current_matrix)
clear_glyph_matrix (f->current_matrix);
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Clear the matrix of the menu bar window, if such a window exists.
The menu bar window is currently used to display menus on X when
no toolkit support is compiled in. */
@@ -822,7 +826,7 @@ clear_desired_matrices (register struct frame *f)
if (f->desired_matrix)
clear_glyph_matrix (f->desired_matrix);
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
if (WINDOWP (f->menu_bar_window))
clear_glyph_matrix (XWINDOW (f->menu_bar_window)->desired_matrix);
#endif
@@ -1156,6 +1160,7 @@ prepare_desired_row (struct window *w, struct glyph_row *row, bool mode_line_p)
}
}
+#ifndef HAVE_ANDROID
/* Return a hash code for glyph row ROW, which may
be from current or desired matrix of frame F. */
@@ -1248,6 +1253,7 @@ line_draw_cost (struct frame *f, struct glyph_matrix *matrix, int vpos)
return len;
}
+#endif
/* Return true if the glyph rows A and B have equal contents.
MOUSE_FACE_P means compare the mouse_face_p flags of A and B, too. */
@@ -2160,7 +2166,7 @@ adjust_frame_glyphs_for_window_redisplay (struct frame *f)
/* Allocate/reallocate window matrices. */
allocate_matrices_for_window_redisplay (XWINDOW (FRAME_ROOT_WINDOW (f)));
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Allocate/ reallocate matrices of the dummy window used to display
the menu bar under X when no X toolkit support is available. */
{
@@ -2302,7 +2308,7 @@ free_glyphs (struct frame *f)
if (!NILP (f->root_window))
free_window_matrices (XWINDOW (f->root_window));
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Free the dummy window for menu bars without X toolkit and its
glyph matrices. */
if (!NILP (f->menu_bar_window))
@@ -3175,6 +3181,7 @@ redraw_frame (struct frame *f)
its redisplay done. */
mark_window_display_accurate (FRAME_ROOT_WINDOW (f), 0);
set_window_update_flags (XWINDOW (FRAME_ROOT_WINDOW (f)), true);
+
f->garbaged = false;
}
@@ -3240,7 +3247,7 @@ update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p)
when pending input is detected. */
update_begin (f);
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Update the menu bar on X frames that don't have toolkit
support. */
if (WINDOWP (f->menu_bar_window))
@@ -5077,6 +5084,10 @@ update_frame_1 (struct frame *f, bool force_p, bool inhibit_id_p,
static bool
scrolling (struct frame *frame)
{
+ /* In fact this code should never be reached at all under
+ Android. */
+
+#ifndef HAVE_ANDROID
int unchanged_at_top, unchanged_at_bottom;
int window_size;
int changed_lines;
@@ -5167,6 +5178,7 @@ scrolling (struct frame *frame)
free_at_end_vpos - unchanged_at_top);
SAFE_FREE ();
+#endif
return false;
}
@@ -5208,7 +5220,9 @@ count_match (struct glyph *str1, struct glyph *end1, struct glyph *str2, struct
/* Char insertion/deletion cost vector, from term.c */
+#ifndef HAVE_ANDROID
#define char_ins_del_cost(f) (&char_ins_del_vector[FRAME_TOTAL_COLS ((f))])
+#endif
/* Perform a frame-based update on line VPOS in frame FRAME. */
@@ -5413,7 +5427,10 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p)
tem = (nlen - nsp) - (olen - osp);
if (endmatch && tem
&& (!FRAME_CHAR_INS_DEL_OK (f)
- || endmatch <= char_ins_del_cost (f)[tem]))
+#ifndef HAVE_ANDROID
+ || endmatch <= char_ins_del_cost (f)[tem]
+#endif
+ ))
endmatch = 0;
/* nsp - osp is the distance to insert or delete.
@@ -5423,7 +5440,10 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p)
if (nsp != osp
&& (!FRAME_CHAR_INS_DEL_OK (f)
- || begmatch + endmatch <= char_ins_del_cost (f)[nsp - osp]))
+#ifndef HAVE_ANDROID
+ || begmatch + endmatch <= char_ins_del_cost (f)[nsp - osp]
+#endif
+ ))
{
begmatch = 0;
endmatch = 0;
@@ -6052,7 +6072,7 @@ FILE = nil means just close any termscript file currently open. */)
if (tty->termscript != 0)
{
block_input ();
- fclose (tty->termscript);
+ emacs_fclose (tty->termscript);
tty->termscript = 0;
unblock_input ();
}
@@ -6546,6 +6566,15 @@ init_display_interactive (void)
}
#endif /* HAVE_X_WINDOWS */
+#ifdef HAVE_ANDROID
+ if (!inhibit_window_system && android_init_gui)
+ {
+ Vinitial_window_system = Qandroid;
+ android_term_init ();
+ return;
+ }
+#endif
+
#ifdef HAVE_NTGUI
if (!inhibit_window_system)
{
@@ -6600,6 +6629,7 @@ init_display_interactive (void)
exit (1);
}
+#ifndef HAVE_ANDROID
{
struct terminal *t;
struct frame *f = XFRAME (selected_frame);
@@ -6642,6 +6672,11 @@ init_display_interactive (void)
: Qnil));
Fmodify_frame_parameters (selected_frame, tty_arg);
}
+#else
+ fatal ("Could not establish a connection to the Android application.\n"
+ "Emacs does not work on text terminals when built to run as"
+ " part of an Android application package.");
+#endif
{
struct frame *sf = SELECTED_FRAME ();
diff --git a/src/editfns.c b/src/editfns.c
index d02cce4aef3..21c2629ee41 100644
--- a/src/editfns.c
+++ b/src/editfns.c
@@ -33,6 +33,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <sys/utsname.h>
#endif
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
#include "lisp.h"
#include <float.h>
@@ -1264,7 +1268,11 @@ is in general a comma-separated list. */)
if (!pw)
return Qnil;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ p = android_user_full_name (pw);
+#else
p = USER_FULL_NAME;
+#endif
/* Chop off everything after the first comma, since 'pw_gecos' is a
comma-separated list. */
q = strchr (p, ',');
diff --git a/src/emacs-module.c b/src/emacs-module.c
index d158e243139..3d06ef0020a 100644
--- a/src/emacs-module.c
+++ b/src/emacs-module.c
@@ -206,7 +206,7 @@ static void module_non_local_exit_signal_1 (emacs_env *,
static void module_non_local_exit_throw_1 (emacs_env *,
Lisp_Object, Lisp_Object);
static void module_out_of_memory (emacs_env *);
-static void module_reset_handlerlist (struct handler **);
+static void module_reset_handlerlist (struct handler *);
static bool value_storage_contains_p (const struct emacs_value_storage *,
emacs_value, ptrdiff_t *);
@@ -246,10 +246,6 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
of `internal_condition_case' etc., and to avoid worrying about
passing information to the handler functions. */
-#if !HAS_ATTRIBUTE (cleanup)
- #error "__attribute__ ((cleanup)) not supported by this compiler; try GCC"
-#endif
-
/* Place this macro at the beginning of a function returning a number
or a pointer to handle non-local exits. The function must have an
ENV parameter. The function will return the specified value if a
@@ -257,8 +253,8 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
/* It is very important that pushing the handler doesn't itself raise
a signal. Install the cleanup only after the handler has been
- pushed. Use __attribute__ ((cleanup)) to avoid
- non-local-exit-prone manual cleanup.
+ pushed. All code following this point should use
+ MODULE_INTERNAL_CLEANUP before each return.
The do-while forces uses of the macro to be followed by a semicolon.
This macro cannot enclose its entire body inside a do-while, as the
@@ -278,17 +274,20 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
return retval; \
} \
struct handler *internal_cleanup \
- __attribute__ ((cleanup (module_reset_handlerlist))) \
= internal_handler; \
if (sys_setjmp (internal_cleanup->jmp)) \
{ \
module_handle_nonlocal_exit (env, \
internal_cleanup->nonlocal_exit, \
internal_cleanup->val); \
+ module_reset_handlerlist (internal_cleanup); \
return retval; \
} \
do { } while (false)
+#define MODULE_INTERNAL_CLEANUP() \
+ module_reset_handlerlist (internal_cleanup)
+
/* Implementation of runtime and environment functions.
@@ -315,7 +314,10 @@ module_decode_utf_8 (const char *str, ptrdiff_t len)
Emacs functions, by placing the macro
MODULE_HANDLE_NONLOCAL_EXIT right after the above 2 tests.
- 5. Do NOT use 'eassert' for checking validity of user code in the
+ 5. Finally, any code which expands MODULE_HANDLE_NONLOCAL_EXIT
+ should use MODULE_INTERNAL_CLEANUP prior to returning.
+
+ 6. Do NOT use 'eassert' for checking validity of user code in the
module. Instead, make those checks part of the code, and if the
check fails, call 'module_non_local_exit_signal_1' or
'module_non_local_exit_throw_1' to report the error. This is
@@ -438,6 +440,7 @@ module_make_global_ref (emacs_env *env, emacs_value value)
bool overflow = INT_ADD_WRAPV (ref->refcount, 1, &ref->refcount);
if (overflow)
overflow_error ();
+ MODULE_INTERNAL_CLEANUP ();
return &ref->value;
}
else
@@ -450,6 +453,7 @@ module_make_global_ref (emacs_env *env, emacs_value value)
Lisp_Object value;
XSETPSEUDOVECTOR (value, ref, PVEC_OTHER);
hash_put (h, new_obj, value, hashcode);
+ MODULE_INTERNAL_CLEANUP ();
return &ref->value;
}
}
@@ -481,6 +485,8 @@ module_free_global_ref (emacs_env *env, emacs_value global_value)
if (--ref->refcount == 0)
hash_remove_from_table (h, obj);
}
+
+ MODULE_INTERNAL_CLEANUP ();
}
static enum emacs_funcall_exit
@@ -574,6 +580,8 @@ static emacs_value
module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity,
emacs_function func, const char *docstring, void *data)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
if (! (0 <= min_arity
@@ -598,7 +606,9 @@ module_make_function (emacs_env *env, ptrdiff_t min_arity, ptrdiff_t max_arity,
XSET_MODULE_FUNCTION (result, function);
eassert (MODULE_FUNCTIONP (result));
- return lisp_to_value (env, result);
+ value = lisp_to_value (env, result);
+ MODULE_INTERNAL_CLEANUP ();
+ return value;
}
static emacs_finalizer
@@ -607,6 +617,7 @@ module_get_function_finalizer (emacs_env *env, emacs_value arg)
MODULE_FUNCTION_BEGIN (NULL);
Lisp_Object lisp = value_to_lisp (arg);
CHECK_MODULE_FUNCTION (lisp);
+ MODULE_INTERNAL_CLEANUP ();
return XMODULE_FUNCTION (lisp)->finalizer;
}
@@ -618,6 +629,7 @@ module_set_function_finalizer (emacs_env *env, emacs_value arg,
Lisp_Object lisp = value_to_lisp (arg);
CHECK_MODULE_FUNCTION (lisp);
XMODULE_FUNCTION (lisp)->finalizer = fin;
+ MODULE_INTERNAL_CLEANUP ();
}
void
@@ -637,6 +649,7 @@ module_make_interactive (emacs_env *env, emacs_value function, emacs_value spec)
/* Normalize (interactive nil) to (interactive). */
XMODULE_FUNCTION (lisp_fun)->interactive_form
= NILP (lisp_spec) ? list1 (Qinteractive) : list2 (Qinteractive, lisp_spec);
+ MODULE_INTERNAL_CLEANUP ();
}
Lisp_Object
@@ -670,21 +683,30 @@ module_funcall (emacs_env *env, emacs_value func, ptrdiff_t nargs,
newargs[1 + i] = value_to_lisp (args[i]);
emacs_value result = lisp_to_value (env, Ffuncall (nargs1, newargs));
SAFE_FREE ();
+ MODULE_INTERNAL_CLEANUP ();
return result;
}
static emacs_value
module_intern (emacs_env *env, const char *name)
{
+ emacs_value tem;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, intern (name));
+ tem = lisp_to_value (env, intern (name));
+ MODULE_INTERNAL_CLEANUP ();
+ return tem;
}
static emacs_value
module_type_of (emacs_env *env, emacs_value arg)
{
+ emacs_value tem;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, Ftype_of (value_to_lisp (arg)));
+ tem = lisp_to_value (env, Ftype_of (value_to_lisp (arg)));
+ MODULE_INTERNAL_CLEANUP ();
+ return tem;
}
static bool
@@ -710,14 +732,20 @@ module_extract_integer (emacs_env *env, emacs_value arg)
intmax_t i;
if (! integer_to_intmax (lisp, &i))
xsignal1 (Qoverflow_error, lisp);
+ MODULE_INTERNAL_CLEANUP ();
return i;
}
static emacs_value
module_make_integer (emacs_env *env, intmax_t n)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, make_int (n));
+ value = lisp_to_value (env, make_int (n));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static double
@@ -726,14 +754,21 @@ module_extract_float (emacs_env *env, emacs_value arg)
MODULE_FUNCTION_BEGIN (0);
Lisp_Object lisp = value_to_lisp (arg);
CHECK_TYPE (FLOATP (lisp), Qfloatp, lisp);
+ MODULE_INTERNAL_CLEANUP ();
+
return XFLOAT_DATA (lisp);
}
static emacs_value
module_make_float (emacs_env *env, double d)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, make_float (d));
+ value = lisp_to_value (env, make_float (d));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static bool
@@ -765,6 +800,7 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buf,
if (buf == NULL)
{
*len = required_buf_size;
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
@@ -780,36 +816,51 @@ module_copy_string_contents (emacs_env *env, emacs_value value, char *buf,
*len = required_buf_size;
memcpy (buf, SDATA (lisp_str_utf8), raw_size + 1);
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
static emacs_value
module_make_string (emacs_env *env, const char *str, ptrdiff_t len)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
if (! (0 <= len && len <= STRING_BYTES_BOUND))
overflow_error ();
Lisp_Object lstr
= len == 0 ? empty_multibyte_string : module_decode_utf_8 (str, len);
- return lisp_to_value (env, lstr);
+ value = lisp_to_value (env, lstr);
+ MODULE_INTERNAL_CLEANUP ();
+ return value;
}
static emacs_value
module_make_unibyte_string (emacs_env *env, const char *str, ptrdiff_t length)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
if (! (0 <= length && length <= STRING_BYTES_BOUND))
overflow_error ();
Lisp_Object lstr
= length == 0 ? empty_unibyte_string : make_unibyte_string (str, length);
- return lisp_to_value (env, lstr);
+ value = lisp_to_value (env, lstr);
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static emacs_value
module_make_user_ptr (emacs_env *env, emacs_finalizer fin, void *ptr)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, make_user_ptr (fin, ptr));
+ value = lisp_to_value (env, make_user_ptr (fin, ptr));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static void *
@@ -818,6 +869,8 @@ module_get_user_ptr (emacs_env *env, emacs_value arg)
MODULE_FUNCTION_BEGIN (NULL);
Lisp_Object lisp = value_to_lisp (arg);
CHECK_USER_PTR (lisp);
+ MODULE_INTERNAL_CLEANUP ();
+
return XUSER_PTR (lisp)->p;
}
@@ -828,6 +881,7 @@ module_set_user_ptr (emacs_env *env, emacs_value arg, void *ptr)
Lisp_Object lisp = value_to_lisp (arg);
CHECK_USER_PTR (lisp);
XUSER_PTR (lisp)->p = ptr;
+ MODULE_INTERNAL_CLEANUP ();
}
static emacs_finalizer
@@ -836,6 +890,7 @@ module_get_user_finalizer (emacs_env *env, emacs_value arg)
MODULE_FUNCTION_BEGIN (NULL);
Lisp_Object lisp = value_to_lisp (arg);
CHECK_USER_PTR (lisp);
+ MODULE_INTERNAL_CLEANUP ();
return XUSER_PTR (lisp)->finalizer;
}
@@ -847,6 +902,7 @@ module_set_user_finalizer (emacs_env *env, emacs_value arg,
Lisp_Object lisp = value_to_lisp (arg);
CHECK_USER_PTR (lisp);
XUSER_PTR (lisp)->finalizer = fin;
+ MODULE_INTERNAL_CLEANUP ();
}
static void
@@ -866,15 +922,21 @@ module_vec_set (emacs_env *env, emacs_value vector, ptrdiff_t index,
Lisp_Object lisp = value_to_lisp (vector);
check_vec_index (lisp, index);
ASET (lisp, index, value_to_lisp (value));
+ MODULE_INTERNAL_CLEANUP ();
}
static emacs_value
module_vec_get (emacs_env *env, emacs_value vector, ptrdiff_t index)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
Lisp_Object lisp = value_to_lisp (vector);
check_vec_index (lisp, index);
- return lisp_to_value (env, AREF (lisp, index));
+ value = lisp_to_value (env, AREF (lisp, index));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static ptrdiff_t
@@ -883,6 +945,8 @@ module_vec_size (emacs_env *env, emacs_value vector)
MODULE_FUNCTION_BEGIN (0);
Lisp_Object lisp = value_to_lisp (vector);
CHECK_VECTOR (lisp);
+ MODULE_INTERNAL_CLEANUP ();
+
return ASIZE (lisp);
}
@@ -898,23 +962,37 @@ module_should_quit (emacs_env *env)
static enum emacs_process_input_result
module_process_input (emacs_env *env)
{
+ enum emacs_process_input_result rc;
+
MODULE_FUNCTION_BEGIN (emacs_process_input_quit);
maybe_quit ();
- return emacs_process_input_continue;
+ rc = emacs_process_input_continue;
+ MODULE_INTERNAL_CLEANUP ();
+ return rc;
}
static struct timespec
module_extract_time (emacs_env *env, emacs_value arg)
{
+ struct timespec value;
+
MODULE_FUNCTION_BEGIN ((struct timespec) {0});
- return lisp_time_argument (value_to_lisp (arg));
+ value = lisp_time_argument (value_to_lisp (arg));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
static emacs_value
module_make_time (emacs_env *env, struct timespec time)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
- return lisp_to_value (env, timespec_to_lisp (time));
+ value = lisp_to_value (env, timespec_to_lisp (time));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return value;
}
/*
@@ -991,7 +1069,10 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
EMACS_INT x = XFIXNUM (o);
*sign = (0 < x) - (x < 0);
if (x == 0 || count == NULL)
- return true;
+ {
+ MODULE_INTERNAL_CLEANUP ();
+ return true;
+ }
/* As a simplification we don't check how many array elements
are exactly required, but use a reasonable static upper
bound. For most architectures exactly one element should
@@ -1002,6 +1083,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
if (magnitude == NULL)
{
*count = required;
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
if (*count < required)
@@ -1020,12 +1102,16 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
verify (required * bits < PTRDIFF_MAX);
for (ptrdiff_t i = 0; i < required; ++i)
magnitude[i] = (emacs_limb_t) (u >> (i * bits));
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
const mpz_t *x = xbignum_val (o);
*sign = mpz_sgn (*x);
if (count == NULL)
- return true;
+ {
+ MODULE_INTERNAL_CLEANUP ();
+ return true;
+ }
size_t required_size = (mpz_sizeinbase (*x, 2) + numb - 1) / numb;
eassert (required_size <= PTRDIFF_MAX);
ptrdiff_t required = (ptrdiff_t) required_size;
@@ -1033,6 +1119,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
if (magnitude == NULL)
{
*count = required;
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
if (*count < required)
@@ -1045,6 +1132,7 @@ module_extract_big_integer (emacs_env *env, emacs_value arg, int *sign,
size_t written;
mpz_export (magnitude, &written, order, size, endian, nails, *x);
eassert (written == required_size);
+ MODULE_INTERNAL_CLEANUP ();
return true;
}
@@ -1052,21 +1140,34 @@ static emacs_value
module_make_big_integer (emacs_env *env, int sign,
ptrdiff_t count, const emacs_limb_t *magnitude)
{
+ emacs_value value;
+
MODULE_FUNCTION_BEGIN (NULL);
if (sign == 0)
- return lisp_to_value (env, make_fixed_natnum (0));
+ {
+ value = lisp_to_value (env, make_fixed_natnum (0));
+ MODULE_INTERNAL_CLEANUP ();
+ return value;
+ }
enum { order = -1, size = sizeof *magnitude, endian = 0, nails = 0 };
mpz_import (mpz[0], count, order, size, endian, nails, magnitude);
if (sign < 0)
mpz_neg (mpz[0], mpz[0]);
- return lisp_to_value (env, make_integer_mpz ());
+ value = lisp_to_value (env, make_integer_mpz ());
+ MODULE_INTERNAL_CLEANUP ();
+ return value;
}
static int
module_open_channel (emacs_env *env, emacs_value pipe_process)
{
+ int rc;
+
MODULE_FUNCTION_BEGIN (-1);
- return open_channel_for_module (value_to_lisp (pipe_process));
+ rc = open_channel_for_module (value_to_lisp (pipe_process));
+ MODULE_INTERNAL_CLEANUP ();
+
+ return rc;
}
@@ -1519,12 +1620,13 @@ finalize_runtime_unwind (void *raw_ert)
/* Must be called after setting up a handler immediately before
returning from the function. See the comments in lisp.h and the
code in eval.c for details. The macros below arrange for this
- function to be called automatically. PHANDLERLIST points to a word
- containing the handler list, for sanity checking. */
+ function to be called automatically. IHANDLERLIST points to the
+ handler list. */
+
static void
-module_reset_handlerlist (struct handler **phandlerlist)
+module_reset_handlerlist (struct handler *ihandlerlist)
{
- eassert (handlerlist == *phandlerlist);
+ eassert (handlerlist == ihandlerlist);
handlerlist = handlerlist->next;
}
diff --git a/src/emacs.c b/src/emacs.c
index 80a013b68df..d75a83ab9d8 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -33,6 +33,14 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "lisp.h"
#include "sysstdio.h"
+#ifdef HAVE_ANDROID
+#include "androidterm.h"
+#endif
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
#ifdef WINDOWSNT
#include <fcntl.h>
#include <sys/socket.h>
@@ -137,6 +145,10 @@ extern char etext;
#include <sys/resource.h>
#endif
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "android.h"
+#endif
+
/* We don't guard this with HAVE_TREE_SITTER because treesit.o is
always compiled (to provide treesit-available-p). */
#include "treesit.h"
@@ -411,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;
@@ -511,7 +531,8 @@ init_cmdargs (int argc, char **argv, int skip_args, char const *original_pwd)
{
Lisp_Object found;
int yes = openp (Vexec_path, Vinvocation_name, Vexec_suffixes,
- &found, make_fixnum (X_OK), false, false);
+ &found, make_fixnum (X_OK), false, false,
+ NULL);
if (yes == 1)
{
/* Add /: to the front of the name
@@ -724,6 +745,8 @@ argmatch (char **argv, int argc, const char *sstr, const char *lstr,
}
}
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
/* Find a name (absolute or relative) of the Emacs executable whose
name (as passed into this program) is ARGV0. Called early in
initialization by portable dumper loading code, so avoid Lisp and
@@ -823,6 +846,8 @@ find_emacs_executable (char const *argv0, ptrdiff_t *candidate_size)
#endif /* !WINDOWSNT */
}
+#endif
+
#ifdef HAVE_PDUMPER
static const char *
@@ -851,10 +876,38 @@ dump_error_to_string (int result)
}
}
-/* This function returns the Emacs executable. */
+/* This function returns the Emacs executable. DUMP_FILE is ignored
+ outside of Android. Otherwise, it is the name of the dump file to
+ use, or NULL if Emacs should look for a ``--dump-file'' argument
+ instead. */
+
static char *
-load_pdump (int argc, char **argv)
+load_pdump (int argc, char **argv, char *dump_file)
{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ int skip_args = 0, result;
+
+ while (skip_args < argc - 1)
+ {
+ if (argmatch (argv, argc, "-dump-file", "--dump-file",
+ 6, &dump_file, &skip_args)
+ || argmatch (argv, argc, "--", NULL, 2, NULL,
+ &skip_args))
+ break;
+ skip_args++;
+ }
+
+ if (!dump_file)
+ return argv[0];
+
+ result = pdumper_load (dump_file, argv[0]);
+
+ if (result != PDUMPER_LOAD_SUCCESS)
+ fatal ("could not load dump file \"%s\": %s",
+ dump_file, dump_error_to_string (result));
+ return argv[0];
+#else
+
const char *const suffix = ".pdmp";
int result;
char *emacs_executable = argv[0];
@@ -885,7 +938,7 @@ load_pdump (int argc, char **argv)
/* Look for an explicitly-specified dump file. */
const char *path_exec = PATH_EXEC;
- char *dump_file = NULL;
+ dump_file = NULL;
int skip_args = 0;
while (skip_args < argc - 1)
{
@@ -1047,6 +1100,7 @@ load_pdump (int argc, char **argv)
xfree (dump_file);
return emacs_executable;
+#endif
}
#endif /* HAVE_PDUMPER */
@@ -1123,7 +1177,7 @@ load_seccomp (const char *file)
goto out;
}
struct stat stat;
- if (fstat (fd, &stat) != 0)
+ if (sys_fstat (fd, &stat) != 0)
{
emacs_perror ("fstat");
goto out;
@@ -1225,12 +1279,24 @@ maybe_load_seccomp (int argc, char **argv)
#endif /* SECCOMP_USABLE */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+int
+android_emacs_init (int argc, char **argv, char *dump_file)
+#else
int
main (int argc, char **argv)
+#endif
{
/* Variable near the bottom of the stack, and aligned appropriately
for pointers. */
void *stack_bottom_variable;
+ int old_argc;
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ char *dump_file;
+
+ /* This is just a dummy argument used to avoid extra defines. */
+ dump_file = NULL;
+#endif
/* First, check whether we should apply a seccomp filter. This
should come at the very beginning to allow the filter to protect
@@ -1360,7 +1426,7 @@ main (int argc, char **argv)
#ifdef HAVE_PDUMPER
if (attempt_load_pdump)
- initial_emacs_executable = load_pdump (argc, argv);
+ initial_emacs_executable = load_pdump (argc, argv, dump_file);
#else
ptrdiff_t bufsize;
initial_emacs_executable = find_emacs_executable (argv[0], &bufsize);
@@ -1425,8 +1491,9 @@ main (int argc, char **argv)
bool only_version = false;
sort_args (argc, argv);
- argc = 0;
- while (argv[argc]) argc++;
+ old_argc = argc, argc = 0;
+ /* Don't allow going past argv. */
+ while (argc < old_argc && argv[argc]) argc++;
skip_args = 0;
if (argmatch (argv, argc, "-version", "--version", 3, NULL, &skip_args))
@@ -1934,6 +2001,9 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#ifdef HAVE_WINDOW_SYSTEM
init_fringe_once (); /* Swap bitmaps if necessary. */
#endif /* HAVE_WINDOW_SYSTEM */
+#ifdef HAVE_TEXT_CONVERSION
+ syms_of_textconv ();
+#endif
}
init_alloc ();
@@ -2375,6 +2445,18 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
#endif
syms_of_fontset ();
#endif /* HAVE_HAIKU */
+#ifdef HAVE_ANDROID
+ syms_of_androidterm ();
+ syms_of_androidfns ();
+ syms_of_androidmenu ();
+ syms_of_fontset ();
+#if !defined ANDROID_STUBIFY
+ syms_of_androidfont ();
+ syms_of_androidselect ();
+ syms_of_sfntfont ();
+ syms_of_sfntfont_android ();
+#endif /* !ANDROID_STUBIFY */
+#endif /* HAVE_ANDROID */
syms_of_gnutls ();
@@ -2471,6 +2553,17 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
init_window ();
init_font ();
+#ifdef HAVE_ANDROID
+ init_androidmenu ();
+#endif
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ init_androidfont ();
+ init_androidselect ();
+ init_sfntfont ();
+ init_sfntfont_android ();
+#endif
+
if (!initialized)
{
char *file;
@@ -2525,6 +2618,16 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem
safe_run_hooks (Qafter_pdump_load_hook);
#endif
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && 0
+ /* This comes very late in the startup process because it requires
+ most of lisp/international to be loaded. This approach doesn't
+ work because normal-top-level runs and creates the initial frame
+ before fonts are initialized. So this is done in
+ normal-top-level instead. */
+ Vtop_level = list3 (Qprogn, Vtop_level,
+ list1 (Qandroid_enumerate_fonts));
+#endif
+
/* Enter editor command loop. This never returns. */
set_initial_minibuffer_mode ();
Frecursive_edit ();
@@ -2850,7 +2953,14 @@ killed. */
#ifndef WINDOWSNT
/* Do some checking before shutting down Emacs, because errors
can't be meaningfully reported afterwards. */
- if (!NILP (restart))
+ if (!NILP (restart)
+ /* Don't perform the following checks when Emacs is running as
+ an Android GUI application, because there the system is
+ relied on to restart Emacs. */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ && !android_init_gui
+#endif
+ )
{
/* This is very unlikely, but it's possible to execute a binary
(on some systems) with no argv. */
@@ -2912,6 +3022,13 @@ killed. */
if (!NILP (restart))
{
turn_on_atimers (false);
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Re-executing the Emacs process created by the system doesn't
+ work. Instead, schedule a restart for a few hundered
+ milliseconds and exit Emacs. */
+ if (android_init_gui)
+ android_restart_emacs ();
+#endif
#ifdef WINDOWSNT
if (w32_reexec_emacs (initial_cmdline, initial_wd) < 0)
#else
@@ -2952,7 +3069,7 @@ shut_down_emacs (int sig, Lisp_Object stuff)
Vinhibit_redisplay = Qt;
/* If we are controlling the terminal, reset terminal modes. */
-#ifndef DOS_NT
+#if !defined DOS_NT && !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
pid_t tpgrp = tcgetpgrp (STDIN_FILENO);
if (tpgrp != -1 && tpgrp == getpgrp ())
{
@@ -3471,6 +3588,7 @@ Special values:
`windows-nt' compiled as a native W32 application.
`cygwin' compiled using the Cygwin library.
`haiku' compiled for a Haiku system.
+ `android' compiled for Android.
Anything else (in Emacs 26, the possibilities are: aix, berkeley-unix,
hpux, usg-unix-v) indicates some sort of Unix system. */);
Vsystem_type = intern_c_string (SYSTEM_TYPE);
diff --git a/src/epaths.in b/src/epaths.in
index b290f0243bc..afddd57763a 100644
--- a/src/epaths.in
+++ b/src/epaths.in
@@ -18,6 +18,7 @@ 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/>. */
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
/* Together with PATH_SITELOADSEARCH, this gives the default value of
load-path, which is the search path for the Lisp function "load".
@@ -79,3 +80,24 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
/* Where Emacs should look for the application default file. */
#define PATH_X_DEFAULTS "/usr/lib/X11/%L/%T/%N%C%S:/usr/lib/X11/%l/%T/%N%C%S:/usr/lib/X11/%T/%N%C%S:/usr/lib/X11/%L/%T/%N%S:/usr/lib/X11/%l/%T/%N%S:/usr/lib/X11/%T/%N%S"
+
+#else
+
+/* Replace the defines above with links to files in the assets
+ pseudo-directory. Preserve the extra spaces, or epaths.in will not
+ be generated correctly. */
+
+ # define PATH_EXEC (android_lib_dir)
+ # define PATH_LOADSEARCH "/assets/lisp/"
+ # define PATH_SITELOADSEARCH (android_site_load_path)
+ # define PATH_DUMPLOADSEARCH "/assets/lisp/"
+ # define PATH_DATA "/assets/etc/"
+ # define PATH_DOC "/assets/etc/"
+ # define PATH_INFO "/assets/info/"
+ # define PATH_GAME ""
+ # define PATH_BITMAPS ""
+
+extern char *android_site_load_path;
+extern char *android_lib_dir;
+
+#endif
diff --git a/src/fileio.c b/src/fileio.c
index a364aa2ea33..76b278b4083 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -56,6 +56,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "region-cache.h"
#include "frame.h"
+#if defined HAVE_ANDROID
+#include "android.h"
+#endif
+
#ifdef HAVE_LINUX_FS_H
# include <sys/ioctl.h>
# include <linux/fs.h>
@@ -109,6 +113,42 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "commands.h"
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
+/* Type describing a file descriptor used by functions such as
+ `insert-file-contents'. */
+
+typedef int emacs_fd;
+
+/* Function used to read and open from such a file descriptor. */
+
+#define emacs_fd_open emacs_open
+#define emacs_fd_close emacs_close
+#define emacs_fd_read emacs_read_quit
+#define emacs_fd_lseek lseek
+#define emacs_fd_fstat sys_fstat
+#define emacs_fd_valid_p(fd) ((fd) >= 0)
+
+/* This is not used on MS Windows. */
+
+#ifndef WINDOWSNT
+#define emacs_fd_to_int(fds) (fds)
+#endif /* WINDOWSNT */
+
+#else /* HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+typedef struct android_fd_or_asset emacs_fd;
+
+#define emacs_fd_open android_open_asset
+#define emacs_fd_close android_close_asset
+#define emacs_fd_read android_asset_read_quit
+#define emacs_fd_lseek android_asset_lseek
+#define emacs_fd_fstat android_asset_fstat
+#define emacs_fd_valid_p(fd) ((fd).asset != ((void *) -1))
+#define emacs_fd_to_int(fds) ((fds).asset ? -1 : (fds).fd)
+
+#endif /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+
/* True during writing of auto-save files. */
static bool auto_saving;
@@ -135,13 +175,44 @@ static dev_t timestamp_file_system;
static Lisp_Object Vwrite_region_annotation_buffers;
static Lisp_Object emacs_readlinkat (int, char const *);
-static Lisp_Object file_name_directory (Lisp_Object);
static bool a_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
Lisp_Object *, struct coding_system *);
static bool e_write (int, Lisp_Object, ptrdiff_t, ptrdiff_t,
struct coding_system *);
+
+/* Check that ENCODED does not lie on any special directory whose
+ contents are read only. Signal a `file-error' if it does.
+
+ If WRITE, then don't check that the file lies on `/content' on
+ Android. This special exception allows writing to content
+ provider-supplied files. */
+
+static void
+check_mutable_filename (Lisp_Object encoded, bool write)
+{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (!strcmp (SSDATA (encoded), "/assets")
+ || !strncmp (SSDATA (encoded), "/assets/",
+ sizeof "/assets/" - 1))
+ xsignal2 (Qfile_error,
+ build_string ("File lies on read-only directory"),
+ encoded);
+
+ if (write)
+ return;
+
+ if (!strcmp (SSDATA (encoded), "/content")
+ || !strncmp (SSDATA (encoded), "/content/",
+ sizeof "/content/" - 1))
+ xsignal2 (Qfile_error,
+ build_string ("File lies on read-only directory"),
+ encoded);
+#endif
+}
+
+
/* Test whether FILE is accessible for AMODE.
Return true if successful, false (setting errno) otherwise. */
@@ -160,7 +231,7 @@ file_access_p (char const *file, int amode)
}
#endif
- if (faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
+ if (sys_faccessat (AT_FDCWD, file, amode, AT_EACCESS) == 0)
return true;
#ifdef CYGWIN
@@ -264,11 +335,20 @@ close_file_unwind (int fd)
emacs_close (fd);
}
+static void
+close_file_unwind_emacs_fd (void *ptr)
+{
+ emacs_fd *fd;
+
+ fd = ptr;
+ emacs_fd_close (*fd);
+}
+
void
fclose_unwind (void *arg)
{
FILE *stream = arg;
- fclose (stream);
+ emacs_fclose (stream);
}
/* Restore point, having saved it as a marker. */
@@ -370,7 +450,7 @@ Given a Unix syntax file name, returns a string ending in slash. */)
/* Return the directory component of FILENAME, or nil if FILENAME does
not contain a directory component. */
-static Lisp_Object
+Lisp_Object
file_name_directory (Lisp_Object filename)
{
char *beg = SSDATA (filename);
@@ -889,6 +969,10 @@ user_homedir (char const *name)
p[length] = 0;
struct passwd *pw = getpwnam (p);
SAFE_FREE ();
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (pw && !pw->pw_dir && pw->pw_uid == getuid ())
+ return (char *) android_get_home_directory ();
+#endif
if (!pw || (pw->pw_dir && !IS_ABSOLUTE_FILE_NAME (pw->pw_dir)))
return NULL;
return pw->pw_dir;
@@ -1879,6 +1963,11 @@ get_homedir (void)
pw = getpwuid (getuid ());
if (pw)
home = pw->pw_dir;
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (!home && pw && pw->pw_uid == getuid ())
+ return android_get_home_directory ();
+#endif
if (!home)
return "";
}
@@ -2177,7 +2266,8 @@ permissions. */)
#else
bool already_exists = false;
mode_t new_mask;
- int ifd, ofd;
+ emacs_fd ifd;
+ int ofd;
struct stat st;
#endif
@@ -2197,6 +2287,7 @@ permissions. */)
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
+ check_mutable_filename (encoded_newname, true);
#ifdef WINDOWSNT
if (NILP (ok_if_already_exists)
@@ -2220,22 +2311,24 @@ permissions. */)
report_file_error ("Copying permissions to", newname);
}
#else /* not WINDOWSNT */
- ifd = emacs_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0);
+ ifd = emacs_fd_open (SSDATA (encoded_file), O_RDONLY | O_NONBLOCK, 0);
- if (ifd < 0)
+ if (!emacs_fd_valid_p (ifd))
report_file_error ("Opening input file", file);
- record_unwind_protect_int (close_file_unwind, ifd);
+ record_unwind_protect_ptr (close_file_unwind_emacs_fd, &ifd);
- if (fstat (ifd, &st) != 0)
+ if (emacs_fd_fstat (ifd, &st) != 0)
report_file_error ("Input file status", file);
if (!NILP (preserve_permissions))
{
#if HAVE_LIBSELINUX
- if (is_selinux_enabled ())
+ if (is_selinux_enabled ()
+ && emacs_fd_to_int (ifd) != -1)
{
- conlength = fgetfilecon (ifd, &con);
+ conlength = fgetfilecon (emacs_fd_to_int (ifd),
+ &con);
if (conlength == -1)
report_file_error ("Doing fgetfilecon", file);
}
@@ -2273,7 +2366,7 @@ permissions. */)
if (already_exists)
{
struct stat out_st;
- if (fstat (ofd, &out_st) != 0)
+ if (sys_fstat (ofd, &out_st) != 0)
report_file_error ("Output file status", newname);
if (st.st_dev == out_st.st_dev && st.st_ino == out_st.st_ino)
report_file_errno ("Input and output files are the same",
@@ -2284,7 +2377,8 @@ permissions. */)
maybe_quit ();
- if (clone_file (ofd, ifd))
+ if (emacs_fd_to_int (ifd) != -1
+ && clone_file (ofd, emacs_fd_to_int (ifd)))
newsize = st.st_size;
else
{
@@ -2292,30 +2386,38 @@ permissions. */)
ssize_t copied;
#ifndef MSDOS
- for (newsize = 0; newsize < insize; newsize += copied)
+ newsize = 0;
+
+ if (emacs_fd_to_int (ifd) != -1)
{
- /* Copy at most COPY_MAX bytes at a time; this is min
- (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
- surely aligned well. */
- ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
- ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
- off_t intail = insize - newsize;
- ptrdiff_t len = min (intail, copy_max);
- copied = copy_file_range (ifd, NULL, ofd, NULL, len, 0);
- if (copied <= 0)
- break;
- maybe_quit ();
+ for (; newsize < insize; newsize += copied)
+ {
+ /* Copy at most COPY_MAX bytes at a time; this is min
+ (PTRDIFF_MAX, SIZE_MAX) truncated to a value that is
+ surely aligned well. */
+ ssize_t ssize_max = TYPE_MAXIMUM (ssize_t);
+ ptrdiff_t copy_max = min (ssize_max, SIZE_MAX) >> 30 << 30;
+ off_t intail = insize - newsize;
+ ptrdiff_t len = min (intail, copy_max);
+ copied = copy_file_range (emacs_fd_to_int (ifd), NULL,
+ ofd, NULL, len, 0);
+ if (copied <= 0)
+ break;
+ maybe_quit ();
+ }
}
#endif /* MSDOS */
/* Fall back on read+write if copy_file_range failed, or if the
- input is empty and so could be a /proc file. read+write will
- either succeed, or report an error more precisely than
- copy_file_range would. */
+ input is empty and so could be a /proc file, or if ifd is an
+ invention of android.c. read+write will either succeed, or
+ report an error more precisely than copy_file_range
+ would. */
if (newsize != insize || insize == 0)
{
char buf[MAX_ALLOCA];
- for (; (copied = emacs_read_quit (ifd, buf, sizeof buf));
+
+ for (; (copied = emacs_fd_read (ifd, buf, sizeof buf));
newsize += copied)
{
if (copied < 0)
@@ -2363,8 +2465,10 @@ permissions. */)
}
}
- switch (!NILP (preserve_permissions)
- ? qcopy_acl (SSDATA (encoded_file), ifd,
+ switch ((!NILP (preserve_permissions)
+ && emacs_fd_to_int (ifd) != -1)
+ ? qcopy_acl (SSDATA (encoded_file),
+ emacs_fd_to_int (ifd),
SSDATA (encoded_newname), ofd,
preserved_permissions)
: (already_exists
@@ -2396,7 +2500,17 @@ permissions. */)
struct timespec ts[2];
ts[0] = get_stat_atime (&st);
ts[1] = get_stat_mtime (&st);
- if (futimens (ofd, ts) != 0)
+ if (futimens (ofd, ts) != 0
+ /* Various versions of the Android C library are missing
+ futimens, which leads a gnulib fallback to be installed
+ that uses fdutimens instead. However, fdutimens is not
+ supported on many Android kernels, so just silently fail
+ if errno is ENOTSUP or ENOSYS. */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ && errno != ENOTSUP
+ && errno != ENOSYS
+#endif
+ )
xsignal2 (Qfile_date_error,
build_string ("Cannot set file date"), newname);
}
@@ -2404,7 +2518,9 @@ permissions. */)
if (emacs_close (ofd) < 0)
report_file_error ("Write error", newname);
- emacs_close (ifd);
+ /* Note that ifd is not closed twice because unwind_protects are
+ discarded at the end of this function. */
+ emacs_fd_close (ifd);
#ifdef MSDOS
/* In DJGPP v2.0 and later, fstat usually returns true file mode bits,
@@ -2456,6 +2572,8 @@ DEFUN ("delete-directory-internal", Fdelete_directory_internal,
encoded_dir = ENCODE_FILE (directory);
dir = SSDATA (encoded_dir);
+ check_mutable_filename (encoded_dir, false);
+
if (rmdir (dir) != 0)
report_file_error ("Removing directory", directory);
@@ -2495,6 +2613,7 @@ With a prefix argument, TRASH is nil. */)
return call1 (Qmove_file_to_trash, filename);
encoded_file = ENCODE_FILE (filename);
+ check_mutable_filename (encoded_file, false);
if (unlink (SSDATA (encoded_file)) != 0 && errno != ENOENT)
report_file_error ("Removing old name", filename);
@@ -2652,6 +2771,8 @@ This is what happens in interactive use with M-x. */)
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
+ check_mutable_filename (encoded_file, false);
+ check_mutable_filename (encoded_newname, false);
bool plain_rename = (case_only_rename
|| (!NILP (ok_if_already_exists)
@@ -2763,6 +2884,8 @@ This is what happens in interactive use with M-x. */)
encoded_file = ENCODE_FILE (file);
encoded_newname = ENCODE_FILE (newname);
+ check_mutable_filename (encoded_file, false);
+ check_mutable_filename (encoded_newname, false);
if (link (SSDATA (encoded_file), SSDATA (encoded_newname)) == 0)
return Qnil;
@@ -2816,6 +2939,8 @@ This happens for interactive use with M-x. */)
encoded_target = ENCODE_FILE (target);
encoded_linkname = ENCODE_FILE (linkname);
+ check_mutable_filename (encoded_target, false);
+ check_mutable_filename (encoded_linkname, false);
if (symlink (SSDATA (encoded_target), SSDATA (encoded_linkname)) == 0)
return Qnil;
@@ -2971,7 +3096,8 @@ If there is no error, returns nil. */)
encoded_filename = ENCODE_FILE (absname);
- if (faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK, AT_EACCESS) != 0)
+ if (sys_faccessat (AT_FDCWD, SSDATA (encoded_filename), R_OK,
+ AT_EACCESS) != 0)
report_file_error (SSDATA (string), filename);
return Qnil;
@@ -3073,12 +3199,13 @@ file_directory_p (Lisp_Object file)
{
#ifdef DOS_NT
/* This is cheaper than 'stat'. */
- bool retval = faccessat (AT_FDCWD, SSDATA (file), D_OK, AT_EACCESS) == 0;
+ bool retval = sys_faccessat (AT_FDCWD, SSDATA (file),
+ D_OK, AT_EACCESS) == 0;
if (!retval && errno == EACCES)
errno = ENOTDIR; /* like the non-DOS_NT branch below does */
return retval;
#else
-# ifdef O_PATH
+# if defined O_PATH && !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
/* Use O_PATH if available, as it avoids races and EOVERFLOW issues. */
int fd = emacs_openat (AT_FDCWD, SSDATA (file),
O_PATH | O_CLOEXEC | O_DIRECTORY, 0);
@@ -3187,7 +3314,11 @@ file_accessible_directory_p (Lisp_Object file)
There are three exceptions: "", "/", and "//". Leave "" alone,
as it's invalid. Append only "." to the other two exceptions as
"/" and "//" are distinct on some platforms, whereas "/", "///",
- "////", etc. are all equivalent. */
+ "////", etc. are all equivalent.
+
+ Android has a special directory named "/assets". There is no "."
+ directory there, but appending a "/" is sufficient to check
+ whether or not it is a directory. */
if (! len)
dir = data;
else
@@ -3197,11 +3328,27 @@ file_accessible_directory_p (Lisp_Object file)
special cases "/" and "//", and it's a safe optimization
here. After appending '.', append another '/' to work around
a macOS bug (Bug#30350). */
- static char const appended[] = "/./";
- char *buf = SAFE_ALLOCA (len + sizeof appended);
- memcpy (buf, data, len);
- strcpy (buf + len, &appended[data[len - 1] == '/']);
- dir = buf;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ if (!strncmp ("/assets/", data,
+ sizeof "/assets" - 1))
+ {
+ static char const appended[] = "/";
+ char *buf = SAFE_ALLOCA (len + sizeof appended);
+ memcpy (buf, data, len);
+ strcpy (buf + len, &appended[data[len - 1] == '/']);
+ dir = buf;
+ }
+ else
+ {
+#endif
+ static char const appended[] = "/./";
+ char *buf = SAFE_ALLOCA (len + sizeof appended);
+ memcpy (buf, data, len);
+ strcpy (buf + len, &appended[data[len - 1] == '/']);
+ dir = buf;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ }
+#endif
}
ok = file_access_p (dir, F_OK);
@@ -3521,6 +3668,8 @@ Interactively, prompt for FILENAME, and read MODE with
command from GNU Coreutils. */)
(Lisp_Object filename, Lisp_Object mode, Lisp_Object flag)
{
+ Lisp_Object encoded;
+
CHECK_FIXNUM (mode);
int nofollow = symlink_nofollow_flag (flag);
Lisp_Object absname = Fexpand_file_name (filename,
@@ -3532,7 +3681,9 @@ command from GNU Coreutils. */)
if (!NILP (handler))
return call4 (handler, Qset_file_modes, absname, mode, flag);
- char *fname = SSDATA (ENCODE_FILE (absname));
+ encoded = ENCODE_FILE (absname);
+ check_mutable_filename (encoded, false);
+ char *fname = SSDATA (encoded);
mode_t imode = XFIXNUM (mode) & 07777;
if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0)
report_file_error ("Doing chmod", absname);
@@ -3604,6 +3755,7 @@ TIMESTAMP is in the format of `current-time'. */)
return call4 (handler, Qset_file_times, absname, timestamp, flag);
Lisp_Object encoded_absname = ENCODE_FILE (absname);
+ check_mutable_filename (encoded_absname, false);
if (utimensat (AT_FDCWD, SSDATA (encoded_absname), ts, nofollow) != 0)
{
@@ -3636,6 +3788,7 @@ otherwise, if FILE2 does not exist, the answer is t. */)
(Lisp_Object file1, Lisp_Object file2)
{
struct stat st1, st2;
+ Lisp_Object encoded;
CHECK_STRING (file1);
CHECK_STRING (file2);
@@ -3652,8 +3805,10 @@ otherwise, if FILE2 does not exist, the answer is t. */)
if (!NILP (handler))
return call3 (handler, Qfile_newer_than_file_p, absname1, absname2);
+ encoded = ENCODE_FILE (absname1);
+
int err1;
- if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname1)), &st1, 0) == 0)
+ if (emacs_fstatat (AT_FDCWD, SSDATA (encoded), &st1, 0) == 0)
err1 = 0;
else
{
@@ -3742,7 +3897,7 @@ union read_non_regular
{
struct
{
- int fd;
+ emacs_fd fd;
ptrdiff_t inserted, trytry;
} s;
GCALIGNED_UNION_MEMBER
@@ -3753,10 +3908,10 @@ static Lisp_Object
read_non_regular (Lisp_Object state)
{
union read_non_regular *data = XFIXNUMPTR (state);
- int nbytes = emacs_read_quit (data->s.fd,
- ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + data->s.inserted),
- data->s.trytry);
+ int nbytes = emacs_fd_read (data->s.fd,
+ ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+ + data->s.inserted),
+ data->s.trytry);
return make_fixnum (nbytes);
}
@@ -3905,7 +4060,7 @@ by calling `format-decode', which see. */)
{
struct stat st;
struct timespec mtime;
- int fd;
+ emacs_fd fd;
ptrdiff_t inserted = 0;
int unprocessed;
specpdl_ref count = SPECPDL_INDEX ();
@@ -3983,8 +4138,8 @@ by calling `format-decode', which see. */)
orig_filename = filename;
filename = ENCODE_FILE (filename);
- fd = emacs_open (SSDATA (filename), O_RDONLY, 0);
- if (fd < 0)
+ fd = emacs_fd_open (SSDATA (filename), O_RDONLY, 0);
+ if (!emacs_fd_valid_p (fd))
{
save_errno = errno;
if (NILP (visit))
@@ -4002,7 +4157,7 @@ by calling `format-decode', which see. */)
}
specpdl_ref fd_index = SPECPDL_INDEX ();
- record_unwind_protect_int (close_file_unwind, fd);
+ record_unwind_protect_ptr (close_file_unwind_emacs_fd, &fd);
/* Replacement should preserve point as it preserves markers. */
if (!NILP (replace))
@@ -4012,7 +4167,7 @@ by calling `format-decode', which see. */)
XCAR (XCAR (window_markers)));
}
- if (fstat (fd, &st) != 0)
+ if (emacs_fd_fstat (fd, &st) != 0)
report_file_error ("Input file status", orig_filename);
mtime = get_stat_mtime (&st);
@@ -4033,7 +4188,7 @@ by calling `format-decode', which see. */)
xsignal2 (Qfile_error,
build_string ("not a regular file"), orig_filename);
- seekable = lseek (fd, 0, SEEK_CUR) < 0;
+ seekable = emacs_fd_lseek (fd, 0, SEEK_CUR) < 0;
if (!NILP (beg) && !seekable)
xsignal2 (Qfile_error,
build_string ("cannot use a start position in a non-seekable file/device"),
@@ -4112,17 +4267,17 @@ by calling `format-decode', which see. */)
int nread;
if (st.st_size <= (1024 * 4))
- nread = emacs_read_quit (fd, read_buf, 1024 * 4);
+ nread = emacs_fd_read (fd, read_buf, 1024 * 4);
else
{
- nread = emacs_read_quit (fd, read_buf, 1024);
+ nread = emacs_fd_read (fd, read_buf, 1024);
if (nread == 1024)
{
int ntail;
- if (lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0)
+ if (emacs_fd_lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0)
report_file_error ("Setting file position",
orig_filename);
- ntail = emacs_read_quit (fd, read_buf + nread, 1024 * 3);
+ ntail = emacs_fd_read (fd, read_buf + nread, 1024 * 3);
nread = ntail < 0 ? ntail : nread + ntail;
}
}
@@ -4163,7 +4318,7 @@ by calling `format-decode', which see. */)
specpdl_ptr--;
/* Rewind the file for the actual read done later. */
- if (lseek (fd, 0, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, 0, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
}
@@ -4222,7 +4377,7 @@ by calling `format-decode', which see. */)
if (beg_offset != 0)
{
- if (lseek (fd, beg_offset, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
@@ -4230,7 +4385,7 @@ by calling `format-decode', which see. */)
match the text at the beginning of the buffer. */
while (true)
{
- int nread = emacs_read_quit (fd, read_buf, sizeof read_buf);
+ int nread = emacs_fd_read (fd, read_buf, sizeof read_buf);
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread == 0)
@@ -4265,7 +4420,7 @@ by calling `format-decode', which see. */)
there's no need to replace anything. */
if (same_at_start - BEGV_BYTE == end_offset - beg_offset)
{
- emacs_close (fd);
+ emacs_fd_close (fd);
clear_unwind_protect (fd_index);
/* Truncate the buffer to the size of the file. */
@@ -4288,14 +4443,14 @@ by calling `format-decode', which see. */)
break;
/* How much can we scan in the next step? */
trial = min (curpos, sizeof read_buf);
- if (lseek (fd, curpos - trial, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, curpos - trial, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
total_read = nread = 0;
while (total_read < trial)
{
- nread = emacs_read_quit (fd, read_buf + total_read,
- trial - total_read);
+ nread = emacs_fd_read (fd, read_buf + total_read,
+ trial - total_read);
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread == 0)
@@ -4415,7 +4570,7 @@ by calling `format-decode', which see. */)
/* First read the whole file, performing code conversion into
CONVERSION_BUFFER. */
- if (lseek (fd, beg_offset, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
inserted = 0; /* Bytes put into CONVERSION_BUFFER so far. */
@@ -4426,8 +4581,8 @@ by calling `format-decode', which see. */)
/* Read at most READ_BUF_SIZE bytes at a time, to allow
quitting while reading a huge file. */
- this = emacs_read_quit (fd, read_buf + unprocessed,
- READ_BUF_SIZE - unprocessed);
+ this = emacs_fd_read (fd, read_buf + unprocessed,
+ READ_BUF_SIZE - unprocessed);
if (this <= 0)
break;
@@ -4442,7 +4597,7 @@ by calling `format-decode', which see. */)
if (this < 0)
report_file_error ("Read error", orig_filename);
- emacs_close (fd);
+ emacs_fd_close (fd);
clear_unwind_protect (fd_index);
if (unprocessed > 0)
@@ -4589,7 +4744,7 @@ by calling `format-decode', which see. */)
if (beg_offset != 0 || !NILP (replace))
{
- if (lseek (fd, beg_offset, SEEK_SET) < 0)
+ if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
@@ -4642,10 +4797,10 @@ by calling `format-decode', which see. */)
/* Allow quitting out of the actual I/O. We don't make text
part of the buffer until all the reading is done, so a C-g
here doesn't do any harm. */
- this = emacs_read_quit (fd,
- ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
- + inserted),
- trytry);
+ this = emacs_fd_read (fd,
+ ((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+ + inserted),
+ trytry);
}
if (this <= 0)
@@ -4672,7 +4827,7 @@ by calling `format-decode', which see. */)
else
Fset (Qdeactivate_mark, Qt);
- emacs_close (fd);
+ emacs_fd_close (fd);
clear_unwind_protect (fd_index);
if (read_quit < 0)
@@ -4851,8 +5006,10 @@ by calling `format-decode', which see. */)
}
}
- /* Decode file format. */
- if (inserted > 0)
+ /* Decode file format. Don't do this if Qformat_decode is not
+ bound, which can happen when called early during loadup. */
+
+ if (inserted > 0 && !NILP (Ffboundp (Qformat_decode)))
{
/* Don't run point motion or modification hooks when decoding. */
specpdl_ref count1 = SPECPDL_INDEX ();
@@ -5297,6 +5454,8 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
}
encoded_filename = ENCODE_FILE (filename);
+ check_mutable_filename (encoded_filename, false);
+
fn = SSDATA (encoded_filename);
open_flags = O_WRONLY | O_CREAT;
open_flags |= EQ (mustbenew, Qexcl) ? O_EXCL : !NILP (append) ? 0 : O_TRUNC;
@@ -5383,7 +5542,7 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
modtime = invalid_timespec ();
if (visiting)
{
- if (fstat (desc, &st) == 0)
+ if (sys_fstat (desc, &st) == 0)
modtime = get_stat_mtime (&st);
else
ok = 0, save_errno = errno;
@@ -5421,7 +5580,7 @@ write_region (Lisp_Object start, Lisp_Object end, Lisp_Object filename,
if (desc1 >= 0)
{
struct stat st1;
- if (fstat (desc1, &st1) == 0
+ if (sys_fstat (desc1, &st1) == 0
&& st.st_dev == st1.st_dev && st.st_ino == st1.st_ino)
{
/* Use the heuristic if it appears to be valid. With neither
@@ -5798,7 +5957,6 @@ See Info node `(elisp)Modification Time' for more details. */)
return call2 (handler, Qverify_visited_file_modtime, buf);
filename = ENCODE_FILE (BVAR (b, filename));
-
mtime = (emacs_fstatat (AT_FDCWD, SSDATA (filename), &st, 0) == 0
? get_stat_mtime (&st)
: time_error_value (errno));
@@ -5858,7 +6016,7 @@ in `current-time' or an integer flag as returned by `visited-file-modtime'. */)
error ("An indirect buffer does not have a visited file");
else
{
- register Lisp_Object filename;
+ register Lisp_Object filename, encoded;
struct stat st;
Lisp_Object handler;
@@ -5871,7 +6029,9 @@ in `current-time' or an integer flag as returned by `visited-file-modtime'. */)
/* The handler can find the file name the same way we did. */
return call2 (handler, Qset_visited_file_modtime, Qnil);
- if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (filename)), &st, 0)
+ encoded = ENCODE_FILE (filename);
+
+ if (emacs_fstatat (AT_FDCWD, SSDATA (encoded), &st, 0)
== 0)
{
current_buffer->modtime = get_stat_mtime (&st);
@@ -5944,7 +6104,7 @@ do_auto_save_unwind (void *arg)
if (stream != NULL)
{
block_input ();
- fclose (stream);
+ emacs_fclose (stream);
unblock_input ();
}
}
@@ -6270,6 +6430,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
@@ -6284,6 +6449,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
@@ -6305,6 +6472,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);
@@ -6312,6 +6484,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 be9f8f488d9..be551fc876f 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
@@ -653,8 +659,31 @@ lock_if_free (lock_info_type *clasher, Lisp_Object lfname)
static Lisp_Object
make_lock_file_name (Lisp_Object fn)
{
- Lisp_Object lock_file_name = call1 (Qmake_lock_file_name,
- Fexpand_file_name (fn, Qnil));
+ Lisp_Object lock_file_name;
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ char *name;
+#endif
+
+ fn = Fexpand_file_name (fn, Qnil);
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Files in /assets and /contents can't have lock files on Android
+ as these directories are fabrications of android.c, and backed by
+ read only data. */
+
+ name = SSDATA (fn);
+
+ if (strcmp (name, "/assets")
+ || strcmp (name, "/assets/")
+ || strcmp (name, "/content")
+ || strcmp (name, "/content/")
+ || strncmp (name, "/assets/", sizeof "/assets")
+ || strncmp (name, "/content/", sizeof "/content"))
+ return Qnil;
+#endif
+
+ lock_file_name = call1 (Qmake_lock_file_name, fn);
+
return !NILP (lock_file_name) ? ENCODE_FILE (lock_file_name) : Qnil;
}
diff --git a/src/fns.c b/src/fns.c
index 561f526f8d0..7cb73f8375f 100644
--- a/src/fns.c
+++ b/src/fns.c
@@ -3556,6 +3556,10 @@ The data read from the system are decoded using `locale-coding-system'. */)
(Lisp_Object item)
{
char *str = NULL;
+
+ /* STR is apparently unused on Android. */
+ ((void) str);
+
#ifdef HAVE_LANGINFO_CODESET
if (EQ (item, Qcodeset))
{
diff --git a/src/font.c b/src/font.c
index de8748dd857..bf561095ef7 100644
--- a/src/font.c
+++ b/src/font.c
@@ -177,9 +177,35 @@ font_make_entity (void)
allocate_pseudovector (VECSIZE (struct font_entity),
FONT_ENTITY_MAX, FONT_ENTITY_MAX, PVEC_FONT));
XSETFONT (font_entity, entity);
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ entity->is_android = false;
+#endif
+
+ return font_entity;
+}
+
+#ifdef HAVE_ANDROID
+
+Lisp_Object
+font_make_entity_android (int size)
+{
+ Lisp_Object font_entity;
+ struct font_entity *entity
+ = ((struct font_entity *)
+ allocate_pseudovector (size, FONT_ENTITY_MAX, FONT_ENTITY_MAX,
+ PVEC_FONT));
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ entity->is_android = true;
+#endif
+
+ XSETFONT (font_entity, entity);
return font_entity;
}
+#endif
+
/* Create a font-object whose structure size is SIZE. If ENTITY is
not nil, copy properties from ENTITY to the font-object. If
PIXELSIZE is positive, set the `size' property to PIXELSIZE. */
diff --git a/src/font.h b/src/font.h
index 492c2e58b09..ed3b17db994 100644
--- a/src/font.h
+++ b/src/font.h
@@ -260,6 +260,11 @@ struct font_entity
{
union vectorlike_header header;
Lisp_Object props[FONT_ENTITY_MAX];
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* Whether or not this is an Android font entity. */
+ bool is_android;
+#endif
};
/* A value which may appear in the member `encoding' of struct font
@@ -547,8 +552,14 @@ CHECK_FONT_GET_OBJECT (Lisp_Object x)
return XFONT_OBJECT (x);
}
+#ifndef HAVE_ANDROID
/* Number of pt per inch (from the TeXbook). */
#define PT_PER_INCH 72.27
+#else
+/* Android uses this value instead to compensate for different device
+ dimensions. */
+#define PT_PER_INCH 160.00
+#endif
/* Return a pixel size (integer) corresponding to POINT size (double)
on resolution DPI. */
@@ -823,6 +834,9 @@ extern Lisp_Object copy_font_spec (Lisp_Object);
extern Lisp_Object merge_font_spec (Lisp_Object, Lisp_Object);
extern Lisp_Object font_make_entity (void);
+#ifdef HAVE_ANDROID
+extern Lisp_Object font_make_entity_android (int);
+#endif
extern Lisp_Object font_make_object (int, Lisp_Object, int);
#if defined (HAVE_XFT) || defined (HAVE_FREETYPE) || defined (HAVE_NS)
extern Lisp_Object font_build_object (int, Lisp_Object, Lisp_Object, double);
diff --git a/src/fontset.c b/src/fontset.c
index c0e00cfa346..139dcb6eb89 100644
--- a/src/fontset.c
+++ b/src/fontset.c
@@ -667,8 +667,35 @@ fontset_find_font (Lisp_Object fontset, int c, struct face *face,
}
font_object = font_open_for_lface (f, font_entity, face->lface,
FONT_DEF_SPEC (font_def));
+
+ /* If the font registry is not the same as explicitly
+ specified in the font spec, do not cache the font.
+ TrueType fonts have contrived character map selection
+ semantics which makes determining the repertory at font
+ spec matching time unduly expensive. */
+
+ {
+ Lisp_Object spec;
+
+ spec = FONT_DEF_SPEC (font_def);
+
+ if (!NILP (font_object)
+ && !NILP (AREF (spec, FONT_REGISTRY_INDEX))
+ && !NILP (AREF (font_object, FONT_REGISTRY_INDEX))
+ && !EQ (AREF (spec, FONT_REGISTRY_INDEX),
+ AREF (font_object, FONT_REGISTRY_INDEX))
+ /* See sfntfont_registries_compatible_p in
+ sfntfont.c. */
+ && !(EQ (AREF (spec, FONT_REGISTRY_INDEX),
+ Qiso8859_1)
+ && EQ (AREF (font_object, FONT_REGISTRY_INDEX),
+ Qiso10646_1)))
+ goto strangeness;
+ }
+
if (NILP (font_object))
{
+ strangeness:
/* Something strange happened, perhaps because of a
Font-backend problem. To avoid crashing, record
that this spec is unusable. It may be better to find
diff --git a/src/frame.c b/src/frame.c
index 2cea96d4a32..874e8c4cac1 100644
--- a/src/frame.c
+++ b/src/frame.c
@@ -228,6 +228,7 @@ Value is:
`pc' for a direct-write MS-DOS frame,
`pgtk' for an Emacs frame running on pure GTK.
`haiku' for an Emacs frame running in Haiku.
+ `android' for an Emacs frame running in Android.
See also `frame-live-p'. */)
(Lisp_Object object)
{
@@ -250,6 +251,8 @@ See also `frame-live-p'. */)
return Qpgtk;
case output_haiku:
return Qhaiku;
+ case output_android:
+ return Qandroid;
default:
emacs_abort ();
}
@@ -279,6 +282,7 @@ The value is a symbol:
`pc' for a direct-write MS-DOS frame.
`pgtk' for an Emacs frame using pure GTK facilities.
`haiku' for an Emacs frame running in Haiku.
+ `android' for an Emacs frame running in Android/
FRAME defaults to the currently selected frame.
@@ -993,6 +997,16 @@ make_frame (bool mini_p)
f->select_mini_window_flag = false;
/* This one should never be zero. */
f->change_stamp = 1;
+
+#ifdef HAVE_TEXT_CONVERSION
+ f->conversion.compose_region_start = Qnil;
+ f->conversion.compose_region_end = Qnil;
+ f->conversion.compose_region_overlay = Qnil;
+ f->conversion.batch_edit_count = 0;
+ f->conversion.batch_edit_flags = 0;
+ f->conversion.actions = NULL;
+#endif
+
root_window = make_window ();
rw = XWINDOW (root_window);
if (mini_p)
@@ -1228,6 +1242,7 @@ make_initial_frame (void)
return f;
}
+#ifndef HAVE_ANDROID
static struct frame *
make_terminal_frame (struct terminal *terminal)
@@ -1317,6 +1332,8 @@ get_future_frame_param (Lisp_Object parameter,
return result;
}
+#endif
+
DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame,
1, 1, 0,
doc: /* Create an additional terminal frame, possibly on another terminal.
@@ -1336,6 +1353,10 @@ Note that changing the size of one terminal frame automatically
affects all frames on the same terminal device. */)
(Lisp_Object parms)
{
+#ifdef HAVE_ANDROID
+ error ("Text terminals are not supported on this platform");
+ return Qnil;
+#else
struct frame *f;
struct terminal *t = NULL;
Lisp_Object frame;
@@ -1436,6 +1457,7 @@ affects all frames on the same terminal device. */)
f->after_make_frame = true;
return frame;
+#endif
}
@@ -2252,6 +2274,13 @@ delete_frame (Lisp_Object frame, Lisp_Object force)
f->terminal = 0; /* Now the frame is dead. */
unblock_input ();
+ /* Clear markers and overlays set by F on behalf of an input
+ method. */
+#ifdef HAVE_TEXT_CONVERSION
+ if (FRAME_WINDOW_P (f))
+ reset_frame_state (f);
+#endif
+
/* If needed, delete the terminal that this frame was on.
(This must be done after the frame is killed.) */
terminal->reference_count--;
@@ -5303,16 +5332,23 @@ gui_display_get_resource (Display_Info *dpyinfo, Lisp_Object attribute,
*nz++ = '.';
lispstpcpy (nz, attribute);
- const char *value =
- dpyinfo->terminal->get_string_resource_hook (&dpyinfo->rdb,
- name_key,
- class_key);
- SAFE_FREE();
+#ifndef HAVE_ANDROID
+ const char *value
+ = dpyinfo->terminal->get_string_resource_hook (&dpyinfo->rdb,
+ name_key,
+ class_key);
+
+ SAFE_FREE ();
if (value && *value)
return build_string (value);
else
return Qnil;
+#else
+
+ SAFE_FREE ();
+ return Qnil;
+#endif
}
@@ -5647,6 +5683,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
@@ -6119,8 +6157,11 @@ make_monitor_attribute_list (struct MonitorInfo *monitors,
mi->work.width, mi->work.height);
geometry = list4i (mi->geom.x, mi->geom.y,
mi->geom.width, mi->geom.height);
- attributes = Fcons (Fcons (Qsource, build_string (source)),
- attributes);
+
+ if (source)
+ attributes = Fcons (Fcons (Qsource, build_string (source)),
+ attributes);
+
attributes = Fcons (Fcons (Qframes, AREF (monitor_frames, i)),
attributes);
#ifdef HAVE_PGTK
@@ -6218,6 +6259,7 @@ syms_of_frame (void)
DEFSYM (Qns, "ns");
DEFSYM (Qpgtk, "pgtk");
DEFSYM (Qhaiku, "haiku");
+ DEFSYM (Qandroid, "android");
DEFSYM (Qvisible, "visible");
DEFSYM (Qbuffer_predicate, "buffer-predicate");
DEFSYM (Qbuffer_list, "buffer-list");
@@ -6624,7 +6666,7 @@ implicitly when there's no window system support.
Note that when a frame is not large enough to accommodate a change of
any of the parameters listed above, Emacs may try to enlarge the frame
even if this option is non-nil. */);
-#if defined (HAVE_WINDOW_SYSTEM)
+#if defined (HAVE_WINDOW_SYSTEM) && !defined (HAVE_ANDROID)
#if defined (USE_GTK) || defined (HAVE_NS)
frame_inhibit_implied_resize = list1 (Qtab_bar_lines);
#else
diff --git a/src/frame.h b/src/frame.h
index b95b94c7685..e2900d1c15b 100644
--- a/src/frame.h
+++ b/src/frame.h
@@ -76,6 +76,64 @@ enum ns_appearance_type
#endif
#endif /* HAVE_WINDOW_SYSTEM */
+#ifdef HAVE_TEXT_CONVERSION
+
+enum text_conversion_operation
+ {
+ TEXTCONV_START_BATCH_EDIT,
+ TEXTCONV_END_BATCH_EDIT,
+ TEXTCONV_COMMIT_TEXT,
+ TEXTCONV_FINISH_COMPOSING_TEXT,
+ TEXTCONV_SET_COMPOSING_TEXT,
+ TEXTCONV_SET_COMPOSING_REGION,
+ TEXTCONV_SET_POINT_AND_MARK,
+ TEXTCONV_DELETE_SURROUNDING_TEXT,
+ TEXTCONV_REQUEST_POINT_UPDATE,
+ };
+
+/* Structure describing a single edit being performed by the input
+ method that should be executed in the context of
+ kbd_buffer_get_event. */
+
+struct text_conversion_action
+{
+ /* The next text conversion action. */
+ struct text_conversion_action *next;
+
+ /* Any associated data. */
+ Lisp_Object data;
+
+ /* The operation being performed. */
+ enum text_conversion_operation operation;
+
+ /* Counter value. */
+ unsigned long counter;
+};
+
+/* Structure describing the text conversion state associated with a
+ frame. */
+
+struct text_conversion_state
+{
+ /* List of text conversion actions associated with this frame. */
+ struct text_conversion_action *actions;
+
+ /* Markers representing the composing region. */
+ Lisp_Object compose_region_start, compose_region_end;
+
+ /* Overlay representing the composing region. */
+ Lisp_Object compose_region_overlay;
+
+ /* The number of ongoing ``batch edits'' that are causing point
+ reporting to be delayed. */
+ int batch_edit_count;
+
+ /* Mask containing what must be updated after batch edits end. */
+ int batch_edit_flags;
+};
+
+#endif
+
/* The structure representing a frame. */
struct frame
@@ -181,7 +239,7 @@ struct frame
most recently buried buffer is first. For last-buffer. */
Lisp_Object buried_buffer_list;
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* A dummy window used to display menu bars under X when no X
toolkit support is available. */
Lisp_Object menu_bar_window;
@@ -377,7 +435,7 @@ struct frame
/* The output method says how the contents of this frame are
displayed. It could be using termcap, or using an X window.
This must be the same as the terminal->type. */
- ENUM_BF (output_method) output_method : 3;
+ ENUM_BF (output_method) output_method : 4;
#ifdef HAVE_WINDOW_SYSTEM
/* True if this frame is a tooltip frame. */
@@ -586,20 +644,22 @@ struct frame
well. */
union output_data
{
- struct tty_output *tty; /* From termchar.h. */
- struct x_output *x; /* From xterm.h. */
- struct w32_output *w32; /* From w32term.h. */
- struct ns_output *ns; /* From nsterm.h. */
- struct pgtk_output *pgtk; /* From pgtkterm.h. */
- struct haiku_output *haiku; /* From haikuterm.h. */
+ struct tty_output *tty; /* From termchar.h. */
+ struct x_output *x; /* From xterm.h. */
+ struct w32_output *w32; /* From w32term.h. */
+ struct ns_output *ns; /* From nsterm.h. */
+ struct pgtk_output *pgtk; /* From pgtkterm.h. */
+ struct haiku_output *haiku; /* From haikuterm.h. */
+ struct android_output *android; /* From androidterm.h. */
}
output_data;
/* List of font-drivers available on the frame. */
struct font_driver_list *font_driver_list;
-#if defined (HAVE_X_WINDOWS)
- /* Used by x_wait_for_event when watching for an X event on this frame. */
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+ /* Used by x_wait_for_event when watching for an X event on this
+ frame. */
int wait_event_type;
#endif
@@ -662,6 +722,11 @@ struct frame
enum ns_appearance_type ns_appearance;
bool_bf ns_transparent_titlebar;
#endif
+
+#ifdef HAVE_TEXT_CONVERSION
+ /* Text conversion state used by certain input methods. */
+ struct text_conversion_state conversion;
+#endif
} GCALIGNED_STRUCT;
/* Most code should use these functions to set Lisp fields in struct frame. */
@@ -713,7 +778,7 @@ fset_menu_bar_vector (struct frame *f, Lisp_Object val)
{
f->menu_bar_vector = val;
}
-#if defined (HAVE_X_WINDOWS) && ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
INLINE void
fset_menu_bar_window (struct frame *f, Lisp_Object val)
{
@@ -872,6 +937,11 @@ default_pixels_per_inch_y (void)
#else
#define FRAME_HAIKU_P(f) ((f)->output_method == output_haiku)
#endif
+#ifndef HAVE_ANDROID
+#define FRAME_ANDROID_P(f) false
+#else
+#define FRAME_ANDROID_P(f) ((f)->output_method == output_android)
+#endif
/* FRAME_WINDOW_P tests whether the frame is a graphical window system
frame. */
@@ -890,6 +960,9 @@ default_pixels_per_inch_y (void)
#ifdef HAVE_HAIKU
#define FRAME_WINDOW_P(f) FRAME_HAIKU_P (f)
#endif
+#ifdef HAVE_ANDROID
+#define FRAME_WINDOW_P(f) FRAME_ANDROID_P (f)
+#endif
#ifndef FRAME_WINDOW_P
#define FRAME_WINDOW_P(f) ((void) (f), false)
#endif
@@ -917,11 +990,17 @@ default_pixels_per_inch_y (void)
frame F. We need to define two versions because a TTY-only build
does not have FRAME_DISPLAY_INFO. */
#ifdef HAVE_WINDOW_SYSTEM
+#ifndef HAVE_ANDROID
# define MOUSE_HL_INFO(F) \
- (FRAME_WINDOW_P(F) \
+ (FRAME_WINDOW_P (F) \
? &FRAME_DISPLAY_INFO(F)->mouse_highlight \
: &(F)->output_data.tty->display_info->mouse_highlight)
#else
+/* There is no "struct tty_output" on Android at all. */
+# define MOUSE_HL_INFO(F) \
+ (&FRAME_DISPLAY_INFO(F)->mouse_highlight)
+#endif
+#else
# define MOUSE_HL_INFO(F) \
(&(F)->output_data.tty->display_info->mouse_highlight)
#endif
diff --git a/src/fringe.c b/src/fringe.c
index ed257c073b9..3452c8503f0 100644
--- a/src/fringe.c
+++ b/src/fringe.c
@@ -1422,25 +1422,30 @@ If BITMAP overrides a standard fringe bitmap, the original bitmap is restored.
On X, we bit-swap the built-in bitmaps and reduce bitmap
from short to char array if width is <= 8 bits.
+ The Android port tries to follow X as closely as possible, so do
+ that there too.
+
On MAC with big-endian CPU, we need to byte-swap each short.
On W32 and MAC (little endian), there's no need to do this.
*/
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK)
-static const unsigned char swap_nibble[16] = {
- 0x0, 0x8, 0x4, 0xc, /* 0000 1000 0100 1100 */
- 0x2, 0xa, 0x6, 0xe, /* 0010 1010 0110 1110 */
- 0x1, 0x9, 0x5, 0xd, /* 0001 1001 0101 1101 */
- 0x3, 0xb, 0x7, 0xf}; /* 0011 1011 0111 1111 */
-#endif /* HAVE_X_WINDOWS */
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_PGTK) || defined (HAVE_ANDROID)
+static const unsigned char swap_nibble[16] =
+ {
+ 0x0, 0x8, 0x4, 0xc, /* 0000 1000 0100 1100 */
+ 0x2, 0xa, 0x6, 0xe, /* 0010 1010 0110 1110 */
+ 0x1, 0x9, 0x5, 0xd, /* 0001 1001 0101 1101 */
+ 0x3, 0xb, 0x7, 0xf, /* 0011 1011 0111 1111 */
+ };
+#endif
static void
init_fringe_bitmap (int which, struct fringe_bitmap *fb, int once_p)
{
if (once_p || fb->dynamic)
{
-#if defined (HAVE_X_WINDOWS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_ANDROID)
unsigned short *bits = fb->bits;
int j;
@@ -1488,7 +1493,7 @@ init_fringe_bitmap (int which, struct fringe_bitmap *fb, int once_p)
}
}
#endif /* not USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
#if !defined(HAVE_X_WINDOWS) && defined (HAVE_PGTK)
unsigned short *bits = fb->bits;
diff --git a/src/image.c b/src/image.c
index 8ee802a9d62..821869a42cd 100644
--- a/src/image.c
+++ b/src/image.c
@@ -175,6 +175,31 @@ typedef struct haiku_bitmap_record Bitmap_Record;
#endif
+#ifdef HAVE_ANDROID
+#include "androidterm.h"
+
+typedef struct android_bitmap_record Bitmap_Record;
+
+typedef struct android_image XImage;
+typedef android_pixmap Pixmap;
+
+#define GET_PIXEL(ximg, x, y) android_get_pixel (ximg, x, y)
+#define PUT_PIXEL(ximg, x, y, pixel) android_put_pixel (ximg, x, y, pixel)
+#define NO_PIXMAP 0
+
+#define PIX_MASK_RETAIN 0
+#define PIX_MASK_DRAW 1
+
+#define RGB_TO_ULONG(r, g, b) (((r) << 16) | ((g) << 8) | (b))
+#define RED_FROM_ULONG(color) (((color) >> 16) & 0xff)
+#define GREEN_FROM_ULONG(color) (((color) >> 8) & 0xff)
+#define BLUE_FROM_ULONG(color) ((color) & 0xff)
+#define RED16_FROM_ULONG(color) (RED_FROM_ULONG (color) * 0x101)
+#define GREEN16_FROM_ULONG(color) (GREEN_FROM_ULONG (color) * 0x101)
+#define BLUE16_FROM_ULONG(color) (BLUE_FROM_ULONG (color) * 0x101)
+
+#endif
+
static void image_disable_image (struct frame *, struct image *);
static void image_edge_detection (struct frame *, struct image *, Lisp_Object,
Lisp_Object);
@@ -486,6 +511,18 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
return -1;
#endif /* HAVE_X_WINDOWS */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ android_pixmap bitmap;
+
+ bitmap = android_create_bitmap_from_data (bits, width, height);
+
+ if (!bitmap)
+ return -1;
+#elif defined HAVE_ANDROID
+ ((void) dpyinfo);
+ emacs_abort ();
+#endif /* HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
#ifdef HAVE_NTGUI
Lisp_Object frame UNINIT; /* The value is not used. */
Emacs_Pixmap bitmap;
@@ -598,14 +635,16 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
dpyinfo->bitmaps[id - 1].width = width;
dpyinfo->bitmaps[id - 1].refcount = 1;
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+#endif /* ANDROID_STUBIFY */
dpyinfo->bitmaps[id - 1].have_mask = false;
dpyinfo->bitmaps[id - 1].depth = 1;
#ifdef USE_CAIRO
dpyinfo->bitmaps[id - 1].stipple = NULL;
#endif /* USE_CAIRO */
-#endif /* HAVE_X_WINDOWS */
+#endif /* HAVE_X_WINDOWS || HAVE_ANDROID */
#ifdef HAVE_NTGUI
dpyinfo->bitmaps[id - 1].pixmap = bitmap;
@@ -616,9 +655,19 @@ image_create_bitmap_from_data (struct frame *f, char *bits,
return id;
}
-#if defined HAVE_HAIKU || defined HAVE_NS
-static char *slurp_file (int, ptrdiff_t *);
-static Lisp_Object image_find_image_fd (Lisp_Object, int *);
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "android.h"
+
+/* This abstraction allows directly loading images from assets without
+ copying them to a file descriptor first. */
+typedef struct android_fd_or_asset image_fd;
+#else /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
+typedef int image_fd;
+#endif /* defined HAVE_ANDROID && !defined ANDROID_STUBIFY */
+
+#if defined HAVE_HAIKU || defined HAVE_NS || defined HAVE_ANDROID
+static char *slurp_file (image_fd, ptrdiff_t *);
+static Lisp_Object image_find_image_fd (Lisp_Object, image_fd *);
static bool xbm_read_bitmap_data (struct frame *, char *, char *,
int *, int *, char **, bool);
#endif
@@ -724,7 +773,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
/* Search bitmap-file-path for the file, if appropriate. */
if (openp (Vx_bitmap_file_path, file, Qnil, &found,
- make_fixnum (R_OK), false, false)
+ make_fixnum (R_OK), false, false, NULL)
< 0)
return -1;
@@ -773,7 +822,7 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
/* Search bitmap-file-path for the file, if appropriate. */
if (openp (Vx_bitmap_file_path, file, Qnil, &found,
- make_fixnum (R_OK), false, false)
+ make_fixnum (R_OK), false, false, NULL)
< 0)
return -1;
@@ -834,6 +883,73 @@ image_create_bitmap_from_file (struct frame *f, Lisp_Object file)
xfree (contents);
return id;
#endif
+
+#ifdef HAVE_ANDROID
+#ifdef ANDROID_STUBIFY
+ ((void) dpyinfo);
+
+ /* This function should never be called when building stubs. */
+ emacs_abort ();
+#else
+ ptrdiff_t id, size;
+ int width, height, rc;
+ image_fd fd;
+ char *contents, *data;
+ Lisp_Object found;
+ android_pixmap bitmap;
+
+ /* Look for an existing bitmap with the same name. */
+ for (id = 0; id < dpyinfo->bitmaps_last; ++id)
+ {
+ if (dpyinfo->bitmaps[id].refcount
+ && dpyinfo->bitmaps[id].file
+ && !strcmp (dpyinfo->bitmaps[id].file, SSDATA (file)))
+ {
+ ++dpyinfo->bitmaps[id].refcount;
+ return id + 1;
+ }
+ }
+
+ /* Search bitmap-file-path for the file, if appropriate. */
+ if (openp (Vx_bitmap_file_path, file, Qnil, &found,
+ make_fixnum (R_OK), false, false, NULL)
+ < 0)
+ return -1;
+
+ if (!STRINGP (image_find_image_fd (file, &fd))
+ && !STRINGP (image_find_image_fd (found, &fd)))
+ return -1;
+
+ contents = slurp_file (fd, &size);
+
+ if (!contents)
+ return -1;
+
+ rc = xbm_read_bitmap_data (f, contents, contents + size,
+ &width, &height, &data, 0);
+
+ if (!rc)
+ {
+ xfree (contents);
+ return -1;
+ }
+
+ xfree (contents);
+ bitmap = android_create_bitmap_from_data (data, width, height);
+ xfree (data);
+
+ id = image_allocate_bitmap_record (f);
+ dpyinfo->bitmaps[id - 1].pixmap = bitmap;
+ dpyinfo->bitmaps[id - 1].have_mask = false;
+ dpyinfo->bitmaps[id - 1].refcount = 1;
+ dpyinfo->bitmaps[id - 1].file = xlispstrdup (file);
+ dpyinfo->bitmaps[id - 1].depth = 1;
+ dpyinfo->bitmaps[id - 1].height = height;
+ dpyinfo->bitmaps[id - 1].width = width;
+
+ return id;
+#endif
+#endif
}
/* Free bitmap B. */
@@ -851,6 +967,13 @@ free_bitmap_record (Display_Info *dpyinfo, Bitmap_Record *bm)
#endif /* USE_CAIRO */
#endif /* HAVE_X_WINDOWS */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ android_free_pixmap (bm->pixmap);
+
+ if (bm->have_mask)
+ android_free_pixmap (bm->pixmap);
+#endif
+
#ifdef HAVE_NTGUI
DeleteObject (bm->pixmap);
#endif /* HAVE_NTGUI */
@@ -938,7 +1061,7 @@ static void image_unget_x_image (struct image *, bool, Emacs_Pix_Container);
image_unget_x_image (img, mask_p, ximg)
#endif
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
#ifndef USE_CAIRO
static void image_sync_to_pixmaps (struct frame *, struct image *);
@@ -952,6 +1075,8 @@ static bool x_create_x_image_and_pixmap (struct frame *, int, int, int,
XImage **, Pixmap *);
static void x_destroy_x_image (XImage *);
+#if defined HAVE_X_WINDOWS
+
/* Create a mask of a bitmap. Note is this not a perfect mask.
It's nicer with some borders in this context */
@@ -1048,7 +1173,9 @@ x_create_bitmap_mask (struct frame *f, ptrdiff_t id)
x_destroy_x_image (mask_img);
}
-#endif /* HAVE_X_WINDOWS */
+#endif
+
+#endif /* HAVE_X_WINDOWS || defined HAVE_ANDROID*/
/***********************************************************************
Image types
@@ -1082,7 +1209,7 @@ struct image_type
#if defined HAVE_RSVG || defined HAVE_PNG || defined HAVE_GIF || \
defined HAVE_TIFF || defined HAVE_JPEG || defined HAVE_XPM || \
defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK || \
- defined HAVE_WEBP
+ defined HAVE_WEBP || defined HAVE_ANDROID
# ifdef WINDOWSNT
# define IMAGE_TYPE_INIT(f) f
# else
@@ -1584,7 +1711,7 @@ prepare_image_for_display (struct frame *f, struct image *img)
}
unblock_input ();
}
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
if (!img->load_failed_p)
{
block_input ();
@@ -1791,7 +1918,7 @@ image_clear_image_1 (struct frame *f, struct image *img, int flags)
/* NOTE (HAVE_NS): background color is NOT an indexed color! */
img->background_valid = 0;
}
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
if (img->ximg)
{
image_destroy_x_image (img->ximg);
@@ -1809,7 +1936,7 @@ image_clear_image_1 (struct frame *f, struct image *img, int flags)
img->mask = NO_PIXMAP;
img->background_transparent_valid = 0;
}
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
if (img->mask_img)
{
image_destroy_x_image (img->mask_img);
@@ -2181,11 +2308,11 @@ image_size_in_bytes (struct image *img)
if (msk)
size += msk->height * msk->bytes_per_line;
-#elif defined HAVE_X_WINDOWS
- /* Use a nominal depth of 24 bpp for pixmap and 1 bpp for mask,
- to avoid having to query the server. */
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+ /* Use a nominal depth of 24 and a bpp of 32 for pixmap and 1 bpp
+ for mask, to avoid having to query the server. */
if (img->pixmap != NO_PIXMAP)
- size += img->width * img->height * 3;
+ size += img->width * img->height * 4;
if (img->mask != NO_PIXMAP)
size += img->width * img->height / 8;
@@ -2520,11 +2647,11 @@ compute_image_size (double width, double height,
finally move the origin back to the top left of the image, which
may now be a different corner.
- Note that different GUI backends (X, Cairo, w32, NS, Haiku) want
- the transform matrix defined as transform from the original image
- to the transformed image, while others want the matrix to describe
- the transform of the space, which boils down to inverting the
- matrix.
+ Note that different GUI backends (X, Cairo, w32, NS, Haiku,
+ Android) want the transform matrix defined as transform from the
+ original image to the transformed image, while others want the
+ matrix to describe the transform of the space, which boils down to
+ inverting the matrix.
It's possible to pre-calculate the matrix multiplications and just
generate one transform matrix that will do everything we need in a
@@ -2566,6 +2693,96 @@ compute_image_rotation (struct image *img, double *rotation)
*rotation = XFIXNUM (reduced_angle);
}
+#ifdef HAVE_ANDROID
+
+static void
+matrix_identity (matrix3x3 matrix)
+{
+ memset (matrix, 0, sizeof (matrix3x3));
+
+ matrix[0][0] = 1.0;
+ matrix[1][1] = 1.0;
+ matrix[2][2] = 1.0;
+}
+
+/* Translate the matrix TRANSFORM to X, Y, and then perform clockwise
+ rotation by the given angle THETA in radians and translate back.
+ As the transform is being performed in a coordinate system where Y
+ grows downwards, the given angle describes a clockwise
+ rotation. */
+
+static void
+matrix_rotate (matrix3x3 transform, double theta, double x, double y)
+{
+ matrix3x3 temp, copy;
+
+ /* 1. Translate the matrix so X and Y are in the center. */
+
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ temp[0][2] = x;
+ temp[1][2] = y;
+
+ matrix3x3_mult (copy, temp, transform);
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ /* 2. Rotate the matrix counter-clockwise, assuming a coordinate
+ system where Y grows downwards. */
+
+ temp[0][0] = cos (theta);
+ temp[0][1] = -sin (theta);
+ temp[1][0] = sinf (theta);
+ temp[1][1] = cosf (theta);
+
+ matrix3x3_mult (copy, temp, transform);
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ /* 3. Translate back. */
+
+ temp[0][2] = -x;
+ temp[1][2] = -y;
+
+ matrix3x3_mult (copy, temp, transform);
+}
+
+/* Scale the matrix TRANSFORM by -1, and then apply a TX of width, in
+ effect flipping the image horizontally. */
+
+static void
+matrix_mirror_horizontal (matrix3x3 transform, double width)
+{
+ matrix3x3 temp, copy;
+
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ temp[0][0] = -1.0f;
+ temp[0][2] = width;
+
+ matrix3x3_mult (copy, temp, transform);
+}
+
+static void
+matrix_translate (matrix3x3 transform, float tx, float ty)
+{
+ matrix3x3 temp, copy;
+
+ matrix_identity (temp);
+ memcpy (copy, transform, sizeof copy);
+
+ /* Set the tx and ty. */
+ temp[0][2] = tx;
+ temp[1][2] = ty;
+
+ /* Multiply it with the transform. */
+ matrix3x3_mult (copy, temp, transform);
+}
+
+#endif
+
static void
image_set_transform (struct frame *f, struct image *img)
{
@@ -2585,6 +2802,14 @@ image_set_transform (struct frame *f, struct image *img)
memcpy (&img->transform, identity, sizeof identity);
#endif
+#if defined HAVE_ANDROID
+ matrix3x3 identity = {
+ { 1, 0, 0 },
+ { 0, 1, 0 },
+ { 0, 0, 1 },
+ };
+#endif
+
# if (defined HAVE_IMAGEMAGICK \
&& !defined DONT_CREATE_TRANSFORMED_IMAGEMAGICK_IMAGE)
/* ImageMagick images already have the correct transform. */
@@ -2622,7 +2847,8 @@ image_set_transform (struct frame *f, struct image *img)
/* Determine flipping. */
flip = !NILP (image_spec_value (img->spec, QCflip, NULL));
-# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU
+# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_ANDROID
/* We want scale up operations to use a nearest neighbor filter to
show real pixels instead of munging them, but scale down
operations to use a blended filter, to avoid aliasing and the like.
@@ -2644,7 +2870,7 @@ image_set_transform (struct frame *f, struct image *img)
matrix3x3 matrix
= {
-# if defined USE_CAIRO || defined HAVE_XRENDER
+# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID
[0][0] = (!IEEE_FLOATING_POINT && width == 0 ? DBL_MAX
: img->width / (double) width),
[1][1] = (!IEEE_FLOATING_POINT && height == 0 ? DBL_MAX
@@ -2667,7 +2893,7 @@ image_set_transform (struct frame *f, struct image *img)
/* Haiku needs this, since the transformation is done on the basis
of the view, and not the image. */
-#ifdef HAVE_HAIKU
+#if defined HAVE_HAIKU
int extra_tx, extra_ty;
extra_tx = 0;
@@ -2678,8 +2904,9 @@ image_set_transform (struct frame *f, struct image *img)
rotate_flag = 0;
else
{
-# if (defined USE_CAIRO || defined HAVE_XRENDER \
- || defined HAVE_NTGUI || defined HAVE_NS \
+#ifndef HAVE_ANDROID
+# if (defined USE_CAIRO || defined HAVE_XRENDER \
+ || defined HAVE_NTGUI || defined HAVE_NS \
|| defined HAVE_HAIKU)
int cos_r, sin_r;
if (rotation == 0)
@@ -2706,7 +2933,7 @@ image_set_transform (struct frame *f, struct image *img)
sin_r = 1;
rotate_flag = 1;
-#ifdef HAVE_HAIKU
+#if defined HAVE_HAIKU
if (!flip)
extra_ty = height;
extra_tx = 0;
@@ -2742,7 +2969,7 @@ image_set_transform (struct frame *f, struct image *img)
if (0 < rotate_flag)
{
-# if defined USE_CAIRO || defined HAVE_XRENDER
+# if defined USE_CAIRO || defined HAVE_XRENDER || defined HAVE_ANDROID
/* 1. Translate so (0, 0) is in the center of the image. */
matrix3x3 t
= { [0][0] = 1,
@@ -2793,6 +3020,93 @@ image_set_transform (struct frame *f, struct image *img)
img->height = height;
}
# endif
+#else
+ /* Calculate the inverse transform from the destination to the
+ source. The matrix is currently identity with scale
+ applied.
+
+ This code makes more sense to me than what lies above. But
+ I'm not touching what works. */
+
+ if (rotation != 0 && rotation != 90
+ && rotation != 180 && rotation != 270)
+ {
+ rotate_flag = 0;
+ goto bail;
+ }
+
+ rotate_flag = 1;
+
+ switch ((int) rotation + (flip ? 1 : 0))
+ {
+ case 0:
+ break;
+
+ case 90:
+ /* Rotate the image 90 degrees clockwise. IOW, rotate the
+ destination by 90 degrees counterclockwise, which is 270
+ degrees clockwise. */
+ matrix_rotate (matrix, M_PI * 1.5, 0, 0);
+ matrix_translate (matrix, -height, 0);
+ break;
+
+ case 180:
+ /* Apply clockwise 180 degree rotation around the
+ center. */
+ matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0);
+ break;
+
+ case 270:
+ /* Apply 270 degree counterclockwise rotation to the
+ destination, which is 90 degrees clockwise. */
+ matrix_rotate (matrix, M_PI * 0.5, 0, 0);
+ matrix_translate (matrix, 0, -width);
+ break;
+
+ case 1:
+ /* Flipped. Apply horizontal flip. */
+ matrix_mirror_horizontal (matrix, width);
+ break;
+
+ case 91:
+ /* Apply a flip but otherwise treat this the same as 90. */
+ matrix_rotate (matrix, M_PI * 1.5, 0, 0);
+ matrix_translate (matrix, -height, 0);
+ matrix_mirror_horizontal (matrix, height);
+ break;
+
+ case 181:
+ /* Flipped 180 degrees. Apply a flip and treat this the
+ same as 180. */
+ matrix_rotate (matrix, M_PI, width / 2.0, height / 2.0);
+ matrix_mirror_horizontal (matrix, width);
+ break;
+
+ case 271:
+ /* Flipped 270 degrees. Apply a flip and treat this the
+ same as 270. */
+ matrix_rotate (matrix, M_PI * 0.5, 0, 0);
+ matrix_translate (matrix, 0, -width);
+ matrix_mirror_horizontal (matrix, height);
+ break;
+ }
+
+ /* Now set img->width and img->height. Flip them if the
+ rotation being applied requires so. */
+
+ if (rotation != 270 && rotation != 90)
+ {
+ img->width = width;
+ img->height = height;
+ }
+ else
+ {
+ img->height = width;
+ img->width = height;
+ }
+ bail:
+ ;
+#endif
}
if (rotate_flag < 0)
@@ -2857,6 +3171,103 @@ image_set_transform (struct frame *f, struct image *img)
img->transform[0][2] = extra_tx;
img->transform[1][2] = extra_ty;
}
+# elif defined HAVE_ANDROID
+ /* Create a new image of the right size, then turn it into a pixmap
+ and set that as img->pixmap. Destroy img->mask for now (this is
+ not right.) */
+
+ struct android_image *transformed_image, *image;
+ struct android_transform transform;
+
+ /* If there is no transform, simply return. */
+ if (!memcmp (&matrix, &identity, sizeof matrix))
+ return;
+
+ /* First, get the source image. */
+ image = image_get_x_image (f, img, false);
+
+ /* Make the transformed image. */
+ transformed_image = android_create_image (image->depth,
+ ANDROID_Z_PIXMAP,
+ NULL, img->width,
+ img->height);
+
+ /* Allocate memory for that image. */
+ transformed_image->data
+ = xmalloc (transformed_image->bytes_per_line
+ * transformed_image->height);
+
+ /* Do the transform. */
+ transform.m1 = matrix[0][0];
+ transform.m2 = matrix[0][1];
+ transform.m3 = matrix[0][2];
+ transform.m4 = matrix[1][0];
+ transform.m5 = matrix[1][1];
+ transform.m6 = matrix[1][2];
+
+ if (image->depth == 24 && smoothing)
+ android_project_image_bilinear (image, transformed_image,
+ &transform);
+ else
+ android_project_image_nearest (image, transformed_image,
+ &transform);
+
+ image_unget_x_image (img, false, image);
+
+ /* Now replace the image. */
+
+ if (img->ximg)
+ image_destroy_x_image (img->ximg);
+
+ img->ximg = transformed_image;
+
+#ifndef ANDROID_STUBIFY
+ /* Then replace the pixmap. */
+ android_free_pixmap (img->pixmap);
+
+ /* In case android_create_pixmap signals. */
+ img->pixmap = ANDROID_NONE;
+ img->pixmap = android_create_pixmap (img->width, img->height,
+ transformed_image->depth);
+ android_put_image (img->pixmap, transformed_image);
+#else
+ emacs_abort ();
+#endif
+
+ /* Now, transform the mask. The mask should be depth 1, and is
+ always transformed using a nearest neighbor filter. */
+
+ if (img->mask_img || img->mask)
+ {
+ image = image_get_x_image (f, img, true);
+ transformed_image = android_create_image (1, ANDROID_Z_PIXMAP,
+ NULL, img->width,
+ img->height);
+ transformed_image->data
+ = xmalloc (transformed_image->bytes_per_line
+ * transformed_image->height);
+ android_project_image_nearest (image, transformed_image,
+ &transform);
+ image_unget_x_image (img, true, image);
+
+ /* Now replace the image. */
+
+ if (img->mask_img)
+ image_destroy_x_image (img->mask_img);
+
+ img->mask_img = transformed_image;
+
+#ifndef ANDROID_STUBIFY
+ if (img->mask)
+ android_free_pixmap (img->mask);
+
+ img->mask = ANDROID_NONE;
+ img->mask = android_create_pixmap (img->width, img->height, 1);
+ android_put_image (img->mask, transformed_image);
+#endif
+ }
+
+ /* Done! */
#endif
}
@@ -3164,9 +3575,12 @@ mark_image_cache (struct image_cache *c)
/***********************************************************************
X / NS / W32 support code
+ Most of this code is shared with Android to make
+ it easier to maintain.
***********************************************************************/
-#ifdef HAVE_X_WINDOWS
+#if defined HAVE_X_WINDOWS || defined HAVE_ANDROID
+
static bool
x_check_image_size (XImage *ximg, int width, int height)
{
@@ -3182,7 +3596,11 @@ x_check_image_size (XImage *ximg, int width, int height)
int bitmap_pad, depth, bytes_per_line;
if (ximg)
{
+#ifndef HAVE_ANDROID
bitmap_pad = ximg->bitmap_pad;
+#else
+ bitmap_pad = (ximg->depth == 1 ? 8 : 32);
+#endif
depth = ximg->depth;
bytes_per_line = ximg->bytes_per_line;
}
@@ -3200,16 +3618,23 @@ static bool
x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
XImage **ximg, Pixmap *pixmap)
{
+#ifndef HAVE_ANDROID
Display *display = FRAME_X_DISPLAY (f);
Drawable drawable = FRAME_X_DRAWABLE (f);
+#endif
eassert (input_blocked_p ());
if (depth <= 0)
depth = FRAME_DISPLAY_INFO (f)->n_planes;
+#ifndef HAVE_ANDROID
*ximg = XCreateImage (display, FRAME_X_VISUAL (f),
depth, ZPixmap, 0, NULL, width, height,
depth > 16 ? 32 : depth > 8 ? 16 : 8, 0);
+#else
+ *ximg = android_create_image (depth, ANDROID_Z_PIXMAP, NULL, width,
+ height);
+#endif
if (*ximg == NULL)
{
image_error ("Unable to allocate X image");
@@ -3229,7 +3654,15 @@ x_create_x_image_and_pixmap (struct frame *f, int width, int height, int depth,
(*ximg)->data = xmalloc ((*ximg)->bytes_per_line * height);
/* Allocate a pixmap of the same size. */
+#ifndef HAVE_ANDROID
*pixmap = XCreatePixmap (display, drawable, width, height, depth);
+#else
+#ifndef ANDROID_STUBIFY
+ *pixmap = android_create_pixmap (width, height, depth);
+#else
+ emacs_abort ();
+#endif
+#endif
if (*pixmap == NO_PIXMAP)
{
x_destroy_x_image (*ximg);
@@ -3250,7 +3683,11 @@ x_destroy_x_image (XImage *ximg)
ximg->data = NULL;
}
+#ifndef HAVE_ANDROID
XDestroyImage (ximg);
+#else
+ android_destroy_image (ximg);
+#endif
}
# if !defined USE_CAIRO && defined HAVE_XRENDER
@@ -3317,7 +3754,7 @@ x_create_xrender_picture (struct frame *f, Emacs_Pixmap pixmap, int depth)
static bool
image_check_image_size (Emacs_Pix_Container ximg, int width, int height)
{
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
return x_check_image_size (ximg, width, height);
#else
/* FIXME: Implement this check for the HAVE_NS and HAVE_NTGUI cases.
@@ -3355,7 +3792,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
*pimg = *pixmap;
return 1;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
if (!x_create_x_image_and_pixmap (f, width, height, depth, pimg, pixmap))
return 0;
# ifdef HAVE_XRENDER
@@ -3501,7 +3938,7 @@ image_create_x_image_and_pixmap_1 (struct frame *f, int width, int height, int d
static void
image_destroy_x_image (Emacs_Pix_Container pimg)
{
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
x_destroy_x_image (pimg);
#else
eassert (input_blocked_p ());
@@ -3540,7 +3977,9 @@ gui_put_x_image (struct frame *f, Emacs_Pix_Container pimg,
XPutImage (FRAME_X_DISPLAY (f), pixmap, gc, pimg, 0, 0, 0, 0,
pimg->width, pimg->height);
XFreeGC (FRAME_X_DISPLAY (f), gc);
-#endif /* HAVE_X_WINDOWS */
+#elif defined HAVE_ANDROID
+ android_put_image (pixmap, pimg);
+#endif
#ifdef HAVE_NS
eassert (pimg == pixmap);
@@ -3577,7 +4016,7 @@ static void
image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container ximg,
bool mask_p)
{
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
if (!mask_p)
{
eassert (img->ximg == NULL);
@@ -3595,7 +4034,7 @@ image_put_x_image (struct frame *f, struct image *img, Emacs_Pix_Container ximg,
#endif
}
-#if defined HAVE_X_WINDOWS && !defined USE_CAIRO
+#if (defined HAVE_X_WINDOWS || defined HAVE_ANDROID) && !defined USE_CAIRO
/* Put the X images recorded in IMG on frame F into pixmaps, then free
the X images and their buffers. */
@@ -3651,7 +4090,7 @@ image_get_x_image (struct frame *f, struct image *img, bool mask_p)
{
#if defined USE_CAIRO || defined (HAVE_HAIKU)
return !mask_p ? img->pixmap : img->mask;
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
if (ximg_in_img)
@@ -3661,9 +4100,15 @@ image_get_x_image (struct frame *f, struct image *img, bool mask_p)
return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
0, 0, img->original_width, img->original_height, ~0, ZPixmap);
#endif
+#ifndef HAVE_ANDROID
else
return XGetImage (FRAME_X_DISPLAY (f), !mask_p ? img->pixmap : img->mask,
0, 0, img->width, img->height, ~0, ZPixmap);
+#else
+ else
+ return android_get_image (!mask_p ? img->pixmap : img->mask,
+ ANDROID_Z_PIXMAP);
+#endif
#elif defined (HAVE_NS)
Emacs_Pix_Container pixmap = !mask_p ? img->pixmap : img->mask;
@@ -3676,13 +4121,18 @@ static void
image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg)
{
#ifdef USE_CAIRO
-#elif defined HAVE_X_WINDOWS
+#elif defined HAVE_X_WINDOWS || defined HAVE_ANDROID
XImage *ximg_in_img = !mask_p ? img->ximg : img->mask_img;
if (ximg_in_img)
eassert (ximg == ximg_in_img);
+#ifdef HAVE_ANDROID
+ else
+ android_destroy_image (ximg);
+#else
else
XDestroyImage (ximg);
+#endif
#elif defined (HAVE_NS)
ns_release_object (ximg);
#endif
@@ -3701,10 +4151,11 @@ image_unget_x_image (struct image *img, bool mask_p, Emacs_Pix_Container ximg)
PFD is null, do not open the file. */
static Lisp_Object
-image_find_image_fd (Lisp_Object file, int *pfd)
+image_find_image_fd (Lisp_Object file, image_fd *pfd)
{
Lisp_Object file_found, search_path;
int fd;
+ void *platform;
/* TODO I think this should use something like image-load-path
instead. Unfortunately, that can contain non-string elements. */
@@ -3713,8 +4164,10 @@ image_find_image_fd (Lisp_Object file, int *pfd)
Vx_bitmap_file_path);
/* Try to find FILE in data-directory/images, then x-bitmap-file-path. */
+ platform = NULL;
fd = openp (search_path, file, Qnil, &file_found,
- pfd ? Qt : make_fixnum (R_OK), false, false);
+ pfd ? Qt : make_fixnum (R_OK), false, false,
+ pfd ? &platform : NULL);
if (fd == -2)
{
/* The file exists locally, but has a file name handler.
@@ -3724,10 +4177,23 @@ image_find_image_fd (Lisp_Object file, int *pfd)
Lisp_Object encoded_name = ENCODE_FILE (file_found);
fd = emacs_open (SSDATA (encoded_name), O_RDONLY, 0);
}
- else if (fd < 0)
+ /* FD is -3 if PLATFORM is set to a valid asset file descriptor on
+ Android. */
+ else if (fd < 0 && fd != -3)
return Qnil;
+
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
if (pfd)
*pfd = fd;
+#else
+ /* Construct an asset file descriptor. */
+
+ if (pfd)
+ {
+ pfd->fd = fd;
+ pfd->asset = platform;
+ }
+#endif
return file_found;
}
@@ -3741,14 +4207,25 @@ image_find_image_file (Lisp_Object file)
return image_find_image_fd (file, 0);
}
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+
+static void
+close_android_fd (void *ptr)
+{
+ android_close_asset (*(struct android_fd_or_asset *) ptr);
+}
+
+#endif
+
/* Read FILE into memory. Value is a pointer to a buffer allocated
with xmalloc holding FILE's contents. Value is null if an error
occurred. FD is a file descriptor open for reading FILE. Set
*SIZE to the size of the file. */
static char *
-slurp_file (int fd, ptrdiff_t *size)
+slurp_file (image_fd fd, ptrdiff_t *size)
{
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
FILE *fp = fdopen (fd, "rb");
char *buf = NULL;
@@ -3759,7 +4236,7 @@ slurp_file (int fd, ptrdiff_t *size)
specpdl_ref count = SPECPDL_INDEX ();
record_unwind_protect_ptr (fclose_unwind, fp);
- if (fstat (fileno (fp), &st) == 0
+ if (sys_fstat (fileno (fp), &st) == 0
&& 0 <= st.st_size && st.st_size < min (PTRDIFF_MAX, SIZE_MAX))
{
/* Report an error if we read past the purported EOF.
@@ -3777,6 +4254,39 @@ slurp_file (int fd, ptrdiff_t *size)
unbind_to (count, Qnil);
}
+#else
+ char *buf;
+ struct stat st;
+ specpdl_ref count;
+
+ if (!android_asset_fstat (fd, &st)
+ && (0 <= st.st_size
+ && st.st_size < min (PTRDIFF_MAX, SIZE_MAX)))
+ {
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (close_android_fd, &fd);
+ buf = xmalloc (st.st_size + 1);
+
+ /* Read one byte past the end of the file. That allows
+ detecting if the file grows as it is being read. */
+
+ if (android_asset_read (fd, buf,
+ st.st_size + 1) == st.st_size)
+ *size = st.st_size;
+ else
+ {
+ xfree (buf);
+ buf = NULL;
+ }
+
+ unbind_to (count, Qnil);
+ }
+ else
+ {
+ buf = NULL;
+ android_close_asset (fd);
+ }
+#endif
return buf;
}
@@ -4205,6 +4715,15 @@ Create_Pixmap_From_Bitmap_Data (struct frame *f, struct image *img, char *data,
img->picture = x_create_xrender_picture (f, img->pixmap, 0);
# endif
+#elif defined HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
+ img->pixmap
+ = android_create_pixmap_from_bitmap_data (data, img->width, img->height,
+ fg, bg,
+ FRAME_DISPLAY_INFO (f)->n_planes);
+#else
+ emacs_abort ();
+#endif
#elif defined HAVE_NTGUI
img->pixmap
= w32_create_pixmap_from_bitmap_data (img->width, img->height, data);
@@ -4490,7 +5009,7 @@ xbm_load (struct frame *f, struct image *img)
file_name = image_spec_value (img->spec, QCfile, NULL);
if (STRINGP (file_name))
{
- int fd;
+ image_fd fd;
Lisp_Object file = image_find_image_fd (file_name, &fd);
if (!STRINGP (file))
{
@@ -4635,7 +5154,8 @@ xbm_load (struct frame *f, struct image *img)
XPM images
***********************************************************************/
-#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK)
+#if defined (HAVE_XPM) || defined (HAVE_NS) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
static bool xpm_image_p (Lisp_Object object);
static bool xpm_load (struct frame *f, struct image *img);
@@ -4665,7 +5185,8 @@ static bool xpm_load (struct frame *f, struct image *img);
#endif /* not HAVE_NTGUI */
#endif /* HAVE_XPM */
-#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
+#if defined HAVE_XPM || defined USE_CAIRO || defined HAVE_NS \
+ || defined HAVE_HAIKU || defined HAVE_ANDROID
/* Indices of image specification fields in xpm_format, below. */
@@ -4685,7 +5206,8 @@ enum xpm_keyword_index
XPM_LAST
};
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_PGTK || defined HAVE_ANDROID
/* Vector of image_keyword structures describing the format
of valid XPM image specifications. */
@@ -4927,7 +5449,8 @@ init_xpm_functions (void)
#endif /* WINDOWSNT */
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_PGTK || defined HAVE_ANDROID
/* Value is true if COLOR_SYMBOLS is a valid color symbols list
for XPM images. Such a list must consist of conses whose car and
cdr are strings. */
@@ -4963,9 +5486,9 @@ xpm_image_p (Lisp_Object object)
&& (! fmt[XPM_COLOR_SYMBOLS].count
|| xpm_valid_color_symbols_p (fmt[XPM_COLOR_SYMBOLS].value)));
}
-#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK */
+#endif /* HAVE_XPM || HAVE_NS || HAVE_HAIKU || HAVE_PGTK || HAVE_ANDROID */
-#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU */
+#endif /* HAVE_XPM || USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */
#if defined HAVE_XPM && defined HAVE_X_WINDOWS && !defined USE_GTK
ptrdiff_t
@@ -5338,10 +5861,12 @@ xpm_load (struct frame *f, struct image *img)
#if (defined USE_CAIRO && defined HAVE_XPM) \
|| (defined HAVE_NS && !defined HAVE_XPM) \
|| (defined HAVE_HAIKU && !defined HAVE_XPM) \
- || (defined HAVE_PGTK && !defined HAVE_XPM)
+ || (defined HAVE_PGTK && !defined HAVE_XPM) \
+ || (defined HAVE_ANDROID && !defined HAVE_XPM)
-/* XPM support functions for NS and Haiku where libxpm is not available, and for
- Cairo. Only XPM version 3 (without any extensions) is supported. */
+/* XPM support functions for NS, Haiku and Android where libxpm is not
+ available, and for Cairo. Only XPM version 3 (without any
+ extensions) is supported. */
static void xpm_put_color_table_v (Lisp_Object, const char *,
int, Lisp_Object);
@@ -5780,7 +6305,7 @@ xpm_load (struct frame *f,
file_name = image_spec_value (img->spec, QCfile, NULL);
if (STRINGP (file_name))
{
- int fd;
+ image_fd fd;
Lisp_Object file = image_find_image_fd (file_name, &fd);
if (!STRINGP (file))
{
@@ -6077,7 +6602,8 @@ lookup_rgb_color (struct frame *f, int r, int g, int b)
{
#ifdef HAVE_NTGUI
return PALETTERGB (r >> 8, g >> 8, b >> 8);
-#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU
+#elif defined USE_CAIRO || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_ANDROID
return RGB_TO_ULONG (r >> 8, g >> 8, b >> 8);
#else
xsignal1 (Qfile_error,
@@ -6150,7 +6676,8 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
p = colors;
for (y = 0; y < img->height; ++y)
{
-#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU
+#if !defined USE_CAIRO && !defined HAVE_NS && !defined HAVE_HAIKU \
+ && !defined HAVE_ANDROID
Emacs_Color *row = p;
for (x = 0; x < img->width; ++x, ++p)
p->pixel = GET_PIXEL (ximg, x, y);
@@ -6158,7 +6685,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
{
FRAME_TERMINAL (f)->query_colors (f, row, img->width);
}
-#else /* USE_CAIRO || HAVE_NS || HAVE_HAIKU */
+#else /* USE_CAIRO || HAVE_NS || HAVE_HAIKU || HAVE_ANDROID */
for (x = 0; x < img->width; ++x, ++p)
{
p->pixel = GET_PIXEL (ximg, x, y);
@@ -6169,7 +6696,7 @@ image_to_emacs_colors (struct frame *f, struct image *img, bool rgb_p)
p->blue = BLUE16_FROM_ULONG (p->pixel);
}
}
-#endif /* USE_CAIRO || HAVE_NS */
+#endif /* USE_CAIRO || HAVE_NS || HAVE_ANDROID */
}
image_unget_x_image_or_dc (img, 0, ximg, prev);
@@ -6234,7 +6761,11 @@ image_from_emacs_colors (struct frame *f, struct image *img, Emacs_Color *colors
Emacs_Pix_Container ximage;
Emacs_Color *p;
+#ifndef HAVE_ANDROID
ximage = NULL;
+#else
+ ximage = 0;
+#endif
init_color_table ();
@@ -6396,7 +6927,9 @@ image_edge_detection (struct frame *f, struct image *img,
}
-#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_HAIKU
+#if defined HAVE_X_WINDOWS || defined USE_CAIRO || defined HAVE_HAIKU \
+ || defined HAVE_ANDROID
+
static void
image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap,
int x, int y, unsigned int width, unsigned int height,
@@ -6432,8 +6965,21 @@ image_pixmap_draw_cross (struct frame *f, Emacs_Pixmap pixmap,
XFreeGC (dpy, gc);
#elif HAVE_HAIKU
be_draw_cross_on_pixmap (pixmap, x, y, width, height, color);
+#elif HAVE_ANDROID
+#ifndef ANDROID_STUBIFY
+ struct android_gc *gc;
+
+ gc = android_create_gc (0, NULL);
+ android_set_foreground (gc, color);
+ android_draw_line (pixmap, gc, x, y, x + width - 1, y + height - 1);
+ android_draw_line (pixmap, gc, x, y + height - 1, x + width - 1, y);
+ android_free_gc (gc);
+#else
+ emacs_abort ();
+#endif
#endif
}
+
#endif /* HAVE_X_WINDOWS || USE_CAIRO || HAVE_HAIKU */
/* Transform image IMG on frame F so that it looks disabled. */
@@ -6477,7 +7023,7 @@ image_disable_image (struct frame *f, struct image *img)
#ifndef HAVE_NTGUI
#ifndef HAVE_NS /* TODO: NS support, however this not needed for toolbars */
-#if !defined USE_CAIRO && !defined HAVE_HAIKU
+#if !defined USE_CAIRO && !defined HAVE_HAIKU && !defined HAVE_ANDROID
#define CrossForeground(f) BLACK_PIX_DEFAULT (f)
#define MaskForeground(f) WHITE_PIX_DEFAULT (f)
#else /* USE_CAIRO || HAVE_HAIKU */
@@ -6788,7 +7334,7 @@ pbm_load (struct frame *f, struct image *img)
if (STRINGP (specified_file))
{
- int fd;
+ image_fd fd;
Lisp_Object file = image_find_image_fd (specified_file, &fd);
if (!STRINGP (file))
{
@@ -7456,8 +8002,11 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
if (NILP (specified_data))
{
int fd;
- Lisp_Object file = image_find_image_fd (specified_file, &fd);
- if (!STRINGP (file))
+ Lisp_Object file = image_find_image_file (specified_file);
+
+ if (!STRINGP (file)
+ || (fd = emacs_open (SSDATA (ENCODE_FILE (file)),
+ O_RDONLY, 0)) < 0)
{
image_error ("Cannot find image file `%s'", specified_file);
return 0;
@@ -7475,7 +8024,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
if (fread (sig, 1, sizeof sig, fp) != sizeof sig
|| png_sig_cmp (sig, 0, sizeof sig))
{
- fclose (fp);
+ emacs_fclose (fp);
image_error ("Not a PNG file: `%s'", file);
return 0;
}
@@ -7529,7 +8078,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
}
if (! png_ptr)
{
- if (fp) fclose (fp);
+ if (fp) emacs_fclose (fp);
return 0;
}
@@ -7543,7 +8092,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
xfree (c->pixels);
xfree (c->rows);
if (c->fp)
- fclose (c->fp);
+ emacs_fclose (c->fp);
return 0;
}
@@ -7668,7 +8217,7 @@ png_load_body (struct frame *f, struct image *img, struct png_load_context *c)
png_read_end (png_ptr, info_ptr);
if (fp)
{
- fclose (fp);
+ emacs_fclose (fp);
c->fp = NULL;
}
@@ -8184,8 +8733,10 @@ jpeg_load_body (struct frame *f, struct image *img,
if (NILP (specified_data))
{
int fd;
- Lisp_Object file = image_find_image_fd (specified_file, &fd);
- if (!STRINGP (file))
+ Lisp_Object file = image_find_image_file (specified_file);
+ if (!STRINGP (file)
+ || (fd = emacs_open (SSDATA (ENCODE_FILE (file)),
+ O_RDONLY, 0)) < 0)
{
image_error ("Cannot find image file `%s'", specified_file);
return 0;
@@ -8231,7 +8782,7 @@ jpeg_load_body (struct frame *f, struct image *img,
/* Close the input file and destroy the JPEG object. */
if (fp)
- fclose (fp);
+ emacs_fclose (fp);
jpeg_destroy_decompress (&mgr->cinfo);
/* If we already have an XImage, free that. */
@@ -8326,7 +8877,7 @@ jpeg_load_body (struct frame *f, struct image *img,
jpeg_finish_decompress (&mgr->cinfo);
jpeg_destroy_decompress (&mgr->cinfo);
if (fp)
- fclose (fp);
+ emacs_fclose (fp);
/* Maybe fill in the background field while we have ximg handy. */
if (NILP (image_spec_value (img->spec, QCbackground, NULL)))
@@ -9100,11 +9651,16 @@ gif_load (struct frame *f, struct image *img)
/* Get the file size so that we can report it in
`image-cache-size'. */
- struct stat st;
- FILE *fp = fopen (SSDATA (encoded_file), "rb");
- if (fstat (fileno (fp), &st) == 0)
- byte_size = st.st_size;
- fclose (fp);
+ {
+ struct stat st;
+ int fd;
+
+ fd = emacs_open (SSDATA (encoded_file), O_RDONLY,
+ 0);
+ if (!sys_fstat (fd, &st))
+ byte_size = st.st_size;
+ emacs_close (fd);
+ }
}
else
{
@@ -9682,7 +10238,7 @@ webp_load (struct frame *f, struct image *img)
if (NILP (specified_data))
{
- int fd;
+ image_fd fd;
file = image_find_image_fd (specified_file, &fd);
if (!STRINGP (file))
{
@@ -10365,7 +10921,8 @@ imagemagick_load_image (struct frame *f, struct image *img,
return 0;
}
-#ifdef HAVE_MAGICKAUTOORIENTIMAGE
+#if defined HAVE_MAGICKAUTOORIENTIMAGE \
+ || HAVE_DECL_MAGICKAUTOORIENTIMAGE
/* If no :rotation is explicitly specified, apply the automatic
rotation from EXIF. */
if (NILP (image_spec_value (img->spec, QCrotation, NULL)))
@@ -10522,7 +11079,8 @@ imagemagick_load_image (struct frame *f, struct image *img,
{
MagickWand *new_wand;
MagickSetImageBackgroundColor (image_wand, bg_wand);
-#ifdef HAVE_MAGICKMERGEIMAGELAYERS
+#if defined HAVE_MAGICKMERGEIMAGELAYERS \
+ || HAVE_DECL_MAGICKMERGEIMAGELAYERS
new_wand = MagickMergeImageLayers (image_wand, MergeLayer);
#else
new_wand = MagickFlattenImages (image_wand);
@@ -10551,8 +11109,9 @@ imagemagick_load_image (struct frame *f, struct image *img,
init_color_table ();
-#if defined (HAVE_MAGICKEXPORTIMAGEPIXELS) && \
- ! defined (HAVE_NS) && ! defined (HAVE_HAIKU)
+#if (defined (HAVE_MAGICKEXPORTIMAGEPIXELS) \
+ || HAVE_DECL_MAGICKEXPORTIMAGEPIXELS) \
+ && ! defined (HAVE_NS) && ! defined (HAVE_HAIKU)
if (imagemagick_render_type != 0)
{
/* Magicexportimage is normally faster than pixelpushing. This
@@ -11078,7 +11637,7 @@ svg_load (struct frame *f, struct image *img)
base_uri = image_spec_value (img->spec, QCbase_uri, NULL);
if (STRINGP (file_name))
{
- int fd;
+ image_fd fd;
Lisp_Object file = image_find_image_fd (file_name, &fd);
if (!STRINGP (file))
{
@@ -11901,7 +12460,7 @@ The list of capabilities can include one or more of the following:
{
#ifdef HAVE_NATIVE_TRANSFORMS
# if defined HAVE_IMAGEMAGICK || defined (USE_CAIRO) || defined (HAVE_NS) \
- || defined (HAVE_HAIKU)
+ || defined (HAVE_HAIKU) | defined HAVE_ANDROID
return list2 (Qscale, Qrotate90);
# elif defined (HAVE_X_WINDOWS) && defined (HAVE_XRENDER)
if (FRAME_DISPLAY_INFO (f)->xrender_supported_p)
@@ -12012,7 +12571,8 @@ static struct image_type const image_types[] =
{ SYMBOL_INDEX (Qjpeg), jpeg_image_p, jpeg_load, image_clear_image,
IMAGE_TYPE_INIT (init_jpeg_functions) },
#endif
-#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU || defined HAVE_PGTK
+#if defined HAVE_XPM || defined HAVE_NS || defined HAVE_HAIKU \
+ || defined HAVE_PGTK || defined HAVE_ANDROID
{ SYMBOL_INDEX (Qxpm), xpm_image_p, xpm_load, image_clear_image,
IMAGE_TYPE_INIT (init_xpm_functions) },
#endif
@@ -12174,7 +12734,8 @@ non-numeric, there is no explicit limit on the size of images. */);
add_image_type (Qxbm);
#if defined (HAVE_XPM) || defined (HAVE_NS) \
- || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
+ || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
DEFSYM (Qxpm, "xpm");
add_image_type (Qxpm);
#endif
diff --git a/src/inotify.c b/src/inotify.c
index 7562ffb1701..844bf54105c 100644
--- a/src/inotify.c
+++ b/src/inotify.c
@@ -419,6 +419,7 @@ IN_ONESHOT */)
int wd = -1;
uint32_t imask = aspect_to_inotifymask (aspect);
uint32_t mask = imask | IN_MASK_ADD | IN_EXCL_UNLINK;
+ char *name;
CHECK_STRING (filename);
@@ -432,7 +433,23 @@ IN_ONESHOT */)
}
encoded_file_name = ENCODE_FILE (filename);
- wd = inotify_add_watch (inotifyfd, SSDATA (encoded_file_name), mask);
+ name = SSDATA (encoded_file_name);
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ /* If FILENAME actually lies in a special directory, return now
+ instead of letting inotify fail. These directories cannot
+ receive file notifications as they are read only. */
+
+ if (strcmp (name, "/assets")
+ || strcmp (name, "/assets/")
+ || strcmp (name, "/content")
+ || strcmp (name, "/content/")
+ || strncmp (name, "/assets/", sizeof "/assets")
+ || strncmp (name, "/content/", sizeof "/content"))
+ return Qnil;
+#endif
+
+ wd = inotify_add_watch (inotifyfd, name, mask);
if (wd < 0)
report_file_notify_error ("Could not add watch for file", filename);
diff --git a/src/keyboard.c b/src/keyboard.c
index b1ccf4acde4..6e63cd71f30 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -44,6 +44,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "atimer.h"
#include "process.h"
#include "menu.h"
+
+#ifdef HAVE_TEXT_CONVERSION
+#include "textconv.h"
+#endif
+
#include <errno.h>
#ifdef HAVE_PTHREAD
@@ -62,6 +67,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>
@@ -348,6 +357,10 @@ static struct timespec timer_last_idleness_start_time;
static Lisp_Object virtual_core_pointer_name;
static Lisp_Object virtual_core_keyboard_name;
+/* If not nil, ID of the last TOUCHSCREEN_END_EVENT to land on the
+ menu bar. */
+static Lisp_Object menu_bar_touch_id;
+
/* Global variable declarations. */
@@ -3022,6 +3035,10 @@ read_char (int commandflag, Lisp_Object map,
{
struct buffer *prev_buffer = current_buffer;
last_input_event = c;
+
+ /* All a `text-conversion' event does is prevent Emacs from
+ staying idle. It is not useful. */
+
call4 (Qcommand_execute, tem, Qnil, Fvector (1, &last_input_event), Qt);
if (CONSP (c) && !NILP (Fmemq (XCAR (c), Vwhile_no_input_ignore_events))
@@ -3584,6 +3601,11 @@ readable_events (int flags)
return 1;
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ if (detect_conversion_events ())
+ return 1;
+#endif
+
if (!(flags & READABLE_EVENTS_IGNORE_SQUEEZABLES) && some_mouse_moved ())
return 1;
if (single_kboard)
@@ -3916,6 +3938,11 @@ kbd_buffer_get_event (KBOARD **kbp,
had_pending_selection_requests = false;
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ bool had_pending_conversion_events;
+
+ had_pending_conversion_events = false;
+#endif
#ifdef subprocesses
if (kbd_on_hold_p () && kbd_buffer_nr_stored () < KBD_BUFFER_SIZE / 4)
@@ -3980,6 +4007,13 @@ kbd_buffer_get_event (KBOARD **kbp,
break;
}
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ if (detect_conversion_events ())
+ {
+ had_pending_conversion_events = true;
+ break;
+ }
+#endif
if (end_time)
{
struct timespec now = current_timespec ();
@@ -4035,6 +4069,24 @@ kbd_buffer_get_event (KBOARD **kbp,
return first;
}
+#ifdef HAVE_TEXT_CONVERSION
+ /* There are pending text conversion operations. Text conversion
+ events should be generated before processing any other keyboard
+ input. */
+ if (had_pending_conversion_events)
+ {
+ handle_pending_conversion_events ();
+ obj = Qtext_conversion;
+
+ /* See the comment in handle_pending_conversion_events_1.
+ Note that in addition, text conversion events are not
+ generated if no edits were actually made. */
+ if (conversion_disabled_p ()
+ || NILP (Vtext_conversion_edits))
+ obj = Qnil;
+ }
+ else
+#endif
/* At this point, we know that there is a readable event available
somewhere. If the event queue is empty, then there must be a
mouse movement enabled and available. */
@@ -4919,7 +4971,74 @@ static const char *const lispy_accent_keys[] =
"dead-horn",
};
-#ifdef HAVE_NTGUI
+#ifdef HAVE_ANDROID
+#define FUNCTION_KEY_OFFSET 0
+
+const char *const lispy_function_keys[] =
+ {
+ /* All elements in this array default to 0, except for the few
+ function keys that Emacs recognizes. */
+ [111] = "escape",
+ [112] = "delete",
+ [120] = "sysrq",
+ [121] = "break",
+ [122] = "home",
+ [123] = "end",
+ [124] = "insert",
+ [126] = "media-play",
+ [127] = "media-pause",
+ [130] = "media-record",
+ [131] = "f1",
+ [132] = "f2",
+ [133] = "f3",
+ [134] = "f4",
+ [135] = "f5",
+ [136] = "f6",
+ [137] = "f7",
+ [138] = "f8",
+ [139] = "f9",
+ [140] = "f10",
+ [141] = "f11",
+ [142] = "f12",
+ [160] = "kp-ret",
+ [164] = "volume-mute",
+ [19] = "up",
+ [20] = "down",
+ [213] = "muhenkan",
+ [214] = "henkan",
+ [215] = "hiragana-katakana",
+ [218] = "kana",
+ [21] = "left",
+ [22] = "right",
+ [24] = "volume-up",
+ [259] = "help",
+ [25] = "volume-down",
+ [268] = "kp-up-left",
+ [269] = "kp-down-left",
+ [270] = "kp-up-right",
+ [271] = "kp-down-right",
+ [272] = "media-skip-forward",
+ [273] = "media-skip-backward",
+ [277] = "cut",
+ [278] = "copy",
+ [279] = "paste",
+ [28] = "clear",
+ [4] = "XF86Back",
+ [61] = "tab",
+ [66] = "return",
+ [67] = "backspace",
+ [82] = "menu",
+ [84] = "find",
+ [85] = "media-play-pause",
+ [86] = "media-stop",
+ [87] = "media-next",
+ [88] = "media-previous",
+ [89] = "media-rewind",
+ [92] = "prior",
+ [93] = "next",
+ };
+
+#elif defined HAVE_NTGUI
#define FUNCTION_KEY_OFFSET 0x0
const char *const lispy_function_keys[] =
@@ -6404,11 +6523,74 @@ make_lispy_event (struct input_event *event)
{
Lisp_Object x, y, id, position;
struct frame *f = XFRAME (event->frame_or_window);
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+ int column, row, dummy;
+#endif
id = event->arg;
x = event->x;
y = event->y;
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
+ if (event->kind == TOUCHSCREEN_BEGIN_EVENT
+ && coords_in_menu_bar_window (f, XFIXNUM (x), XFIXNUM (y)))
+ {
+ /* If the tap began in the menu bar window, then save the
+ id. */
+ menu_bar_touch_id = id;
+ return Qnil;
+ }
+ else if (event->kind == TOUCHSCREEN_END_EVENT
+ && EQ (menu_bar_touch_id, id))
+ {
+ /* This touch should activate the menu bar. Generate the
+ menu bar event. */
+ menu_bar_touch_id = Qnil;
+
+ if (f->menu_bar_window)
+ {
+ x_y_to_hpos_vpos (XWINDOW (f->menu_bar_window), XFIXNUM (x),
+ XFIXNUM (y), &column, &row, NULL, NULL,
+ &dummy);
+
+ if (row >= 0 && row < FRAME_MENU_BAR_LINES (f))
+ {
+ Lisp_Object items, item;
+
+ /* Find the menu bar item under `column'. */
+ item = Qnil;
+ items = FRAME_MENU_BAR_ITEMS (f);
+ for (i = 0; i < ASIZE (items); i += 4)
+ {
+ Lisp_Object pos, string;
+ string = AREF (items, i + 1);
+ pos = AREF (items, i + 3);
+ if (NILP (string))
+ break;
+ if (column >= XFIXNUM (pos)
+ && column < XFIXNUM (pos) + SCHARS (string))
+ {
+ item = AREF (items, i);
+ break;
+ }
+ }
+
+ /* ELisp manual 2.4b says (x y) are window
+ relative but code says they are
+ frame-relative. */
+ position = list4 (event->frame_or_window,
+ Qmenu_bar,
+ Fcons (event->x, event->y),
+ INT_TO_INTEGER (event->timestamp));
+
+ return list2 (item, position);
+ }
+ }
+
+ return Qnil;
+ }
+#endif
+
position = make_lispy_position (f, x, y, event->timestamp);
return list2 (((event->kind
@@ -9896,6 +10078,13 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
/* Gets around Microsoft compiler limitations. */
bool dummyflag = false;
+#ifdef HAVE_TEXT_CONVERSION
+ bool disabled_conversion;
+
+ /* Whether or not text conversion has already been disabled. */
+ disabled_conversion = false;
+#endif
+
struct buffer *starting_buffer;
/* List of events for which a fake prefix key has been generated. */
@@ -10045,6 +10234,43 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
echo_local_start = echo_length ();
keys_local_start = this_command_key_count;
+#ifdef HAVE_TEXT_CONVERSION
+ /* When reading a key sequence while text conversion is in
+ effect, turn it off after the first actual character read.
+ This makes input methods send actual key events instead.
+
+ Make sure only to do this once. Also, disabling text
+ conversion seems to interact badly with menus, so don't
+ disable text conversion if a menu was displayed. */
+
+ if (!disabled_conversion && t && !used_mouse_menu
+ && !disable_inhibit_text_conversion)
+ {
+ int i;
+
+ /* used_mouse_menu isn't set if a menu bar prefix key has
+ just been stored. It appears necessary to look for a
+ prefix key itself. Don't look through too many keys for
+ efficiency reasons. */
+
+ for (i = 0; i < min (t, 10); ++i)
+ {
+ if (NUMBERP (keybuf[i])
+ || (SYMBOLP (keybuf[i])
+ && EQ (Fget (keybuf[i], Qevent_kind),
+ Qfunction_key)))
+ goto disable_text_conversion;
+ }
+
+ goto replay_key;
+
+ disable_text_conversion:
+ disable_text_conversion ();
+ record_unwind_protect_void (resume_text_conversion);
+ disabled_conversion = true;
+ }
+#endif
+
replay_key:
/* These are no-ops, unless we throw away a keystroke below and
jumped back up to replay_key; in that case, these restore the
@@ -10272,7 +10498,7 @@ read_key_sequence (Lisp_Object *keybuf, Lisp_Object prompt,
if (EVENT_HAS_PARAMETERS (key))
{
Lisp_Object kind = EVENT_HEAD_KIND (EVENT_HEAD (key));
- if (EQ (kind, Qmouse_click))
+ if (EQ (kind, Qmouse_click) || EQ (kind, Qtouchscreen))
{
Lisp_Object window = POSN_WINDOW (EVENT_START (key));
Lisp_Object posn = POSN_POSN (EVENT_START (key));
@@ -11184,7 +11410,7 @@ This may include sensitive information such as passwords. */)
if (dribble)
{
block_input ();
- fclose (dribble);
+ emacs_fclose (dribble);
unblock_input ();
dribble = 0;
}
@@ -12083,7 +12309,9 @@ static const struct event_head head_table[] = {
{SYMBOL_INDEX (Qmake_frame_visible), SYMBOL_INDEX (Qmake_frame_visible)},
/* `select-window' should be handled just like `switch-frame'
in read_key_sequence. */
- {SYMBOL_INDEX (Qselect_window), SYMBOL_INDEX (Qswitch_frame)}
+ {SYMBOL_INDEX (Qselect_window), SYMBOL_INDEX (Qswitch_frame)},
+ /* Touchscreen events should be prefixed by the posn. */
+ {SYMBOL_INDEX (Qtouchscreen_begin), SYMBOL_INDEX (Qtouchscreen)},
};
static Lisp_Object
@@ -12429,6 +12657,9 @@ syms_of_keyboard (void)
virtual_core_keyboard_name = Qnil;
staticpro (&virtual_core_keyboard_name);
+ menu_bar_touch_id = Qnil;
+ staticpro (&menu_bar_touch_id);
+
defsubr (&Scurrent_idle_time);
defsubr (&Sevent_symbol_parse_modifiers);
defsubr (&Sevent_convert_list);
@@ -12788,6 +13019,10 @@ See also `pre-command-hook'. */);
"display-monitors-changed-functions");
DEFSYM (Qcoding, "coding");
+ DEFSYM (Qtouchscreen, "touchscreen");
+#ifdef HAVE_TEXT_CONVERSION
+ DEFSYM (Qtext_conversion, "text-conversion");
+#endif
Fset (Qecho_area_clear_hook, Qnil);
@@ -13162,9 +13397,16 @@ which see. */);
DEFVAR_LISP ("post-select-region-hook", Vpost_select_region_hook,
doc: /* Abnormal hook run after the region is selected.
This usually happens as a result of `select-active-regions'. The hook
-is called with one argument, the string that was selected. */);;
+is called with one argument, the string that was selected. */);
Vpost_select_region_hook = Qnil;
+ DEFVAR_LISP ("disable-inhibit-text-conversion",
+ disable_inhibit_text_conversion,
+ doc: /* Don't disable text conversion inside `read-key-sequence'.
+If non-nil, text conversion will continue to happen after a prefix
+key has been read inside `read-key-sequence'. */);
+ disable_inhibit_text_conversion = false;
+
pdumper_do_now_and_after_load (syms_of_keyboard_for_pdumper);
}
diff --git a/src/keyboard.h b/src/keyboard.h
index 3f86a8e03ad..26eecd48b00 100644
--- a/src/keyboard.h
+++ b/src/keyboard.h
@@ -395,8 +395,17 @@ extern void unuse_menu_items (void);
#define EVENT_HEAD(event) \
(EVENT_HAS_PARAMETERS (event) ? XCAR (event) : (event))
-/* Extract the starting and ending positions from a composite event. */
-#define EVENT_START(event) (CAR_SAFE (CDR_SAFE (event)))
+/* Extract the starting and ending positions from a composite event. */
+
+/* Unlike Lisp `event-start', this also handles touch screen events,
+ which are not actually mouse events in the general sense. */
+#define EVENT_START(event) \
+ ((EQ (EVENT_HEAD (event), Qtouchscreen_begin) \
+ || EQ (EVENT_HEAD (event), Qtouchscreen_end)) \
+ ? CDR_SAFE (CAR_SAFE (CDR_SAFE (event))) \
+ : CAR_SAFE (CDR_SAFE (event)))
+
+/* This does not handle touchscreen events. */
#define EVENT_END(event) (CAR_SAFE (CDR_SAFE (CDR_SAFE (event))))
/* Extract the click count from a multi-click event. */
diff --git a/src/lisp.h b/src/lisp.h
index c9a64f07427..79c96e11da8 100644
--- a/src/lisp.h
+++ b/src/lisp.h
@@ -28,6 +28,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <float.h>
#include <inttypes.h>
#include <limits.h>
+#include <stdio.h>
+
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
#include <attribute.h>
#include <count-leading-zeros.h>
@@ -4517,7 +4522,8 @@ extern bool suffix_p (Lisp_Object, const char *);
extern Lisp_Object save_match_data_load (Lisp_Object, Lisp_Object, Lisp_Object,
Lisp_Object, Lisp_Object);
extern int openp (Lisp_Object, Lisp_Object, Lisp_Object,
- Lisp_Object *, Lisp_Object, bool, bool);
+ Lisp_Object *, Lisp_Object, bool, bool,
+ void **);
enum { S2N_IGNORE_TRAILING = 1 };
extern Lisp_Object string_to_number (char const *, int, ptrdiff_t *);
extern void map_obarray (Lisp_Object, void (*) (Lisp_Object, Lisp_Object),
@@ -4736,6 +4742,7 @@ extern void syms_of_marker (void);
/* Defined in fileio.c. */
+extern Lisp_Object file_name_directory (Lisp_Object);
extern char *splice_dir_file (char *, char const *, char const *)
ATTRIBUTE_RETURNS_NONNULL;
extern bool file_name_absolute_p (const char *);
@@ -5075,11 +5082,18 @@ extern void init_random (void);
extern void emacs_backtrace (int);
extern AVOID emacs_abort (void) NO_INLINE;
extern int emacs_fstatat (int, char const *, void *, int);
+#ifdef HAVE_SYS_STAT_H
+extern int sys_fstat (int, struct stat *);
+#endif
+extern int sys_faccessat (int, const char *, int, int);
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
extern int emacs_openat (int, char const *, int, int);
+#endif
extern int emacs_open (const char *, int, int);
extern int emacs_open_noquit (const char *, int, int);
extern int emacs_pipe (int[2]);
extern int emacs_close (int);
+extern int emacs_fclose (FILE *);
extern ptrdiff_t emacs_read (int, void *, ptrdiff_t);
extern ptrdiff_t emacs_read_quit (int, void *, ptrdiff_t);
extern ptrdiff_t emacs_write (int, void const *, ptrdiff_t);
@@ -5113,7 +5127,9 @@ extern Lisp_Object directory_files_internal (Lisp_Object, Lisp_Object,
bool, Lisp_Object, Lisp_Object);
/* Defined in term.c. */
+#ifndef HAVE_ANDROID
extern int *char_ins_del_vector;
+#endif
extern void syms_of_term (void);
extern AVOID fatal (const char *msgid, ...) ATTRIBUTE_FORMAT_PRINTF (1, 2);
@@ -5223,7 +5239,13 @@ extern char *emacs_root_dir (void);
#ifdef HAVE_TEXT_CONVERSION
/* Defined in textconv.c. */
+extern void reset_frame_state (struct frame *);
extern void report_selected_window_change (struct frame *);
+extern void report_point_change (struct frame *, struct window *,
+ struct buffer *);
+extern void disable_text_conversion (void);
+extern void resume_text_conversion (void);
+extern void syms_of_textconv (void);
#endif
#ifdef HAVE_NATIVE_COMP
diff --git a/src/lread.c b/src/lread.c
index 4f1d29d80b2..8634fd7fc37 100644
--- a/src/lread.c
+++ b/src/lread.c
@@ -62,6 +62,24 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <fcntl.h>
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY \
+ || (__ANDROID_API__ < 9)
+
+#define lread_fd int
+#define lread_fd_cmp(n) (fd == (n))
+#define lread_fd_p (fd >= 0)
+#define lread_close emacs_close
+#define lread_fstat fstat
+#define lread_read_quit emacs_read_quit
+#define lread_lseek lseek
+
+#define file_stream FILE *
+#define file_seek fseek
+#define file_stream_valid_p(p) (p)
+#define file_stream_close emacs_fclose
+#define file_stream_invalid NULL
+#define file_get_char getc
+
#ifdef HAVE_FSEEKO
#define file_offset off_t
#define file_tell ftello
@@ -70,6 +88,79 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#define file_tell ftell
#endif
+#else
+
+#include "android.h"
+
+/* Use an Android file descriptor under Android instead, as this
+ allows loading directly from asset files without loading each asset
+ into memory and creating a separate file descriptor every time.
+
+ Note that `struct android_fd_or_asset' as used here is different
+ from that returned from `android_open_asset'; if fd.asset is NULL,
+ then fd.fd is either a valid file descriptor or -1, meaning that
+ the file descriptor is invalid.
+
+ However, lread requires the ability to seek inside asset files,
+ which is not provided under Android 2.2. So when building for that
+ particular system, fall back to the usual file descriptor-based
+ code. */
+
+#define lread_fd struct android_fd_or_asset
+#define lread_fd_cmp(n) (!fd.asset && fd.fd == (n))
+#define lread_fd_p (fd.asset || fd.fd >= 0)
+#define lread_close android_close_asset
+#define lread_fstat android_asset_fstat
+#define lread_read_quit android_asset_read_quit
+#define lread_lseek android_asset_lseek
+
+/* The invalid file stream. */
+
+static struct android_fd_or_asset invalid_file_stream =
+ {
+ -1,
+ NULL,
+ };
+
+#define file_stream struct android_fd_or_asset
+#define file_offset off_t
+#define file_tell(n) (android_asset_lseek ((n), 0, SEEK_CUR))
+#define file_seek android_asset_lseek
+#define file_stream_valid_p(p) ((p).asset || (p).fd >= 0)
+#define file_stream_close android_close_asset
+#define file_stream_invalid invalid_file_stream
+
+/* Return a single character from the file input stream STREAM.
+ Value and errors are the same as getc. */
+
+static int
+file_get_char (file_stream stream)
+{
+ int c;
+ char byte;
+ ssize_t rc;
+
+ retry:
+ rc = android_asset_read (stream, &byte, 1);
+
+ if (rc == 0)
+ c = EOF;
+ else if (rc == -1)
+ {
+ if (errno == EINTR)
+ goto retry;
+ else
+ c = EOF;
+ }
+ else
+ c = (unsigned char) byte;
+
+ return c;
+}
+
+#define USE_ANDROID_ASSETS
+#endif
+
#if IEEE_FLOATING_POINT
# include <ieee754.h>
# ifndef INFINITY
@@ -113,7 +204,7 @@ static Lisp_Object read_objects_completed;
static struct infile
{
/* The input stream. */
- FILE *stream;
+ file_stream stream;
/* Lookahead byte count. */
signed char lookahead;
@@ -375,7 +466,7 @@ skip_dyn_bytes (Lisp_Object readcharfun, ptrdiff_t n)
if (FROM_FILE_P (readcharfun))
{
block_input (); /* FIXME: Not sure if it's needed. */
- fseek (infile->stream, n - infile->lookahead, SEEK_CUR);
+ file_seek (infile->stream, n - infile->lookahead, SEEK_CUR);
unblock_input ();
infile->lookahead = 0;
}
@@ -399,7 +490,7 @@ skip_dyn_eof (Lisp_Object readcharfun)
if (FROM_FILE_P (readcharfun))
{
block_input (); /* FIXME: Not sure if it's needed. */
- fseek (infile->stream, 0, SEEK_END);
+ file_seek (infile->stream, 0, SEEK_END);
unblock_input ();
infile->lookahead = 0;
}
@@ -480,10 +571,12 @@ readbyte_from_stdio (void)
return infile->buf[--infile->lookahead];
int c;
- FILE *instream = infile->stream;
+ file_stream instream = infile->stream;
block_input ();
+#if !defined USE_ANDROID_ASSETS
+
/* Interrupted reads have been observed while reading over the network. */
while ((c = getc (instream)) == EOF && errno == EINTR && ferror (instream))
{
@@ -493,6 +586,35 @@ readbyte_from_stdio (void)
clearerr (instream);
}
+#else
+
+ {
+ char byte;
+ ssize_t rc;
+
+ retry:
+ rc = android_asset_read (instream, &byte, 1);
+
+ if (rc == 0)
+ c = EOF;
+ else if (rc == -1)
+ {
+ if (errno == EINTR)
+ {
+ unblock_input ();
+ maybe_quit ();
+ block_input ();
+ goto retry;
+ }
+ else
+ c = EOF;
+ }
+ else
+ c = (unsigned char) byte;
+ }
+
+#endif
+
unblock_input ();
return (c == EOF ? -1 : c);
@@ -672,7 +794,11 @@ static void substitute_in_interval (INTERVAL, void *);
if the character warrants that.
If SECONDS is a number, wait that many seconds for input, and
- return Qnil if no input arrives within that time. */
+ return Qnil if no input arrives within that time.
+
+ If text conversion is enabled and ASCII_REQUIRED && ERROR_NONASCII,
+ temporarily disable any input method which wants to perform
+ edits, unless `disable-inhibit-text-conversion'. */
static Lisp_Object
read_filtered_event (bool no_switch_frame, bool ascii_required,
@@ -680,12 +806,29 @@ read_filtered_event (bool no_switch_frame, bool ascii_required,
{
Lisp_Object val, delayed_switch_frame;
struct timespec end_time;
+#ifdef HAVE_TEXT_CONVERSION
+ specpdl_ref count;
+#endif
#ifdef HAVE_WINDOW_SYSTEM
if (display_hourglass_p)
cancel_hourglass ();
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ count = SPECPDL_INDEX ();
+
+ /* Don't use text conversion when trying to just read a
+ character. */
+
+ if (ascii_required && error_nonascii
+ && !disable_inhibit_text_conversion)
+ {
+ disable_text_conversion ();
+ record_unwind_protect_void (resume_text_conversion);
+ }
+#endif
+
delayed_switch_frame = Qnil;
/* Compute timeout. */
@@ -761,7 +904,11 @@ read_filtered_event (bool no_switch_frame, bool ascii_required,
#endif
+#ifdef HAVE_TEXT_CONVERSION
+ return unbind_to (count, val);
+#else
return val;
+#endif
}
DEFUN ("read-char", Fread_char, Sread_char, 0, 3, 0,
@@ -1038,7 +1185,7 @@ lisp_file_lexically_bound_p (Lisp_Object readcharfun)
safe to load. Only files compiled with Emacs can be loaded. */
static int
-safe_to_load_version (Lisp_Object file, int fd)
+safe_to_load_version (Lisp_Object file, lread_fd fd)
{
struct stat st;
char buf[512];
@@ -1047,12 +1194,12 @@ safe_to_load_version (Lisp_Object file, int fd)
/* If the file is not regular, then we cannot safely seek it.
Assume that it is not safe to load as a compiled file. */
- if (fstat (fd, &st) == 0 && !S_ISREG (st.st_mode))
+ if (lread_fstat (fd, &st) == 0 && !S_ISREG (st.st_mode))
return 0;
/* Read the first few bytes from the file, and look for a line
specifying the byte compiler version used. */
- nbytes = emacs_read_quit (fd, buf, sizeof buf);
+ nbytes = lread_read_quit (fd, buf, sizeof buf);
if (nbytes > 0)
{
/* Skip to the next newline, skipping over the initial `ELC'
@@ -1067,7 +1214,7 @@ safe_to_load_version (Lisp_Object file, int fd)
version = 0;
}
- if (lseek (fd, 0, SEEK_SET) < 0)
+ if (lread_lseek (fd, 0, SEEK_SET) < 0)
report_file_error ("Seeking to start of file", file);
return version;
@@ -1141,7 +1288,7 @@ close_infile_unwind (void *arg)
{
struct infile *prev_infile = arg;
eassert (infile && infile != prev_infile);
- fclose (infile->stream);
+ file_stream_close (infile->stream);
infile = prev_infile;
}
@@ -1170,6 +1317,22 @@ loadhist_initialize (Lisp_Object filename)
specbind (Qcurrent_load_list, Fcons (filename, Qnil));
}
+#ifdef USE_ANDROID_ASSETS
+
+/* Like `close_file_unwind'. However, PTR is a pointer to an Android
+ file descriptor instead of a system file descriptor. */
+
+static void
+close_file_unwind_android_fd (void *ptr)
+{
+ struct android_fd_or_asset *fd;
+
+ fd = ptr;
+ android_close_asset (*fd);
+}
+
+#endif
+
DEFUN ("load", Fload, Sload, 1, 5, 0,
doc: /* Execute a file of Lisp code named FILE.
First try FILE with `.elc' appended, then try with `.el', then try
@@ -1218,8 +1381,12 @@ Return t if the file exists and loads successfully. */)
(Lisp_Object file, Lisp_Object noerror, Lisp_Object nomessage,
Lisp_Object nosuffix, Lisp_Object must_suffix)
{
- FILE *stream UNINIT;
- int fd;
+ file_stream stream UNINIT;
+ lread_fd fd;
+#ifdef USE_ANDROID_ASSETS
+ int rc;
+ void *asset;
+#endif
specpdl_ref fd_index UNINIT;
specpdl_ref count = SPECPDL_INDEX ();
Lisp_Object found, efound, hist_file_name;
@@ -1260,7 +1427,12 @@ Return t if the file exists and loads successfully. */)
since it would try to load a directory as a Lisp file. */
if (SCHARS (file) == 0)
{
+#if !defined USE_ANDROID_ASSETS
fd = -1;
+#else
+ fd.asset = NULL;
+ fd.fd = -1;
+#endif
errno = ENOENT;
}
else
@@ -1299,12 +1471,22 @@ Return t if the file exists and loads successfully. */)
suffixes = CALLN (Fappend, suffixes, Vload_file_rep_suffixes);
}
- fd =
- openp (Vload_path, file, suffixes, &found, Qnil, load_prefer_newer,
- no_native);
+#if !defined USE_ANDROID_ASSETS
+ fd = openp (Vload_path, file, suffixes, &found, Qnil,
+ load_prefer_newer, no_native, NULL);
+#else
+ asset = NULL;
+ rc = openp (Vload_path, file, suffixes, &found, Qnil,
+ load_prefer_newer, no_native, &asset);
+ fd.fd = rc;
+ fd.asset = asset;
+
+ /* fd.asset will be non-NULL if this is actually an asset
+ file. */
+#endif
}
- if (fd == -1)
+ if (lread_fd_cmp (-1))
{
if (NILP (noerror))
report_file_error ("Cannot open load file", file);
@@ -1316,7 +1498,7 @@ Return t if the file exists and loads successfully. */)
Vuser_init_file = found;
/* If FD is -2, that means openp found a magic file. */
- if (fd == -2)
+ if (lread_fd_cmp (-2))
{
if (NILP (Fequal (found, file)))
/* If FOUND is a different file name from FILE,
@@ -1345,11 +1527,21 @@ Return t if the file exists and loads successfully. */)
#endif
}
+#if !defined USE_ANDROID_ASSETS
if (0 <= fd)
{
fd_index = SPECPDL_INDEX ();
record_unwind_protect_int (close_file_unwind, fd);
}
+#else
+ if (fd.asset || fd.fd >= 0)
+ {
+ /* Use a different kind of unwind_protect here. */
+ fd_index = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (close_file_unwind_android_fd,
+ &fd);
+ }
+#endif
#ifdef HAVE_MODULES
bool is_module =
@@ -1415,11 +1607,12 @@ Return t if the file exists and loads successfully. */)
if (is_elc
/* version = 1 means the file is empty, in which case we can
treat it as not byte-compiled. */
- || (fd >= 0 && (version = safe_to_load_version (file, fd)) > 1))
+ || (lread_fd_p
+ && (version = safe_to_load_version (file, fd)) > 1))
/* Load .elc files directly, but not when they are
remote and have no handler! */
{
- if (fd != -2)
+ if (!lread_fd_cmp (-2))
{
struct stat s1, s2;
int result;
@@ -1476,9 +1669,9 @@ Return t if the file exists and loads successfully. */)
{
Lisp_Object val;
- if (fd >= 0)
+ if (lread_fd_p)
{
- emacs_close (fd);
+ lread_close (fd);
clear_unwind_protect (fd_index);
}
val = call4 (Vload_source_file_function, found, hist_file_name,
@@ -1488,12 +1681,12 @@ Return t if the file exists and loads successfully. */)
}
}
- if (fd < 0)
+ if (!lread_fd_p)
{
/* We somehow got here with fd == -2, meaning the file is deemed
to be remote. Don't even try to reopen the file locally;
just force a failure. */
- stream = NULL;
+ stream = file_stream_invalid;
errno = EINVAL;
}
else if (!is_module && !is_native_elisp)
@@ -1504,7 +1697,15 @@ Return t if the file exists and loads successfully. */)
efound = ENCODE_FILE (found);
stream = emacs_fopen (SSDATA (efound), fmode);
#else
+#if !defined USE_ANDROID_ASSETS
stream = fdopen (fd, fmode);
+#else
+ /* Android systems use special file descriptors which can point
+ into compressed data and double as file streams. FMODE is
+ unused. */
+ ((void) fmode);
+ stream = fd;
+#endif
#endif
}
@@ -1516,15 +1717,15 @@ Return t if the file exists and loads successfully. */)
{
/* `module-load' uses the file name, so we can close the stream
now. */
- if (fd >= 0)
+ if (lread_fd_p)
{
- emacs_close (fd);
+ lread_close (fd);
clear_unwind_protect (fd_index);
}
}
else
{
- if (! stream)
+ if (!file_stream_valid_p (stream))
report_file_error ("Opening stdio stream", file);
set_unwind_protect_ptr (fd_index, close_infile_unwind, infile);
input.stream = stream;
@@ -1660,7 +1861,8 @@ directories, make sure the PREDICATE function returns `dir-ok' for them. */)
(Lisp_Object filename, Lisp_Object path, Lisp_Object suffixes, Lisp_Object predicate)
{
Lisp_Object file;
- int fd = openp (path, filename, suffixes, &file, predicate, false, true);
+ int fd = openp (path, filename, suffixes, &file, predicate, false, true,
+ NULL);
if (NILP (predicate) && fd >= 0)
emacs_close (fd);
return file;
@@ -1676,7 +1878,7 @@ maybe_swap_for_eln1 (Lisp_Object src_name, Lisp_Object eln_name,
if (eln_fd > 0)
{
- if (fstat (eln_fd, &eln_st) || S_ISDIR (eln_st.st_mode))
+ if (sys_fstat (eln_fd, &eln_st) || S_ISDIR (eln_st.st_mode))
emacs_close (eln_fd);
else
{
@@ -1801,14 +2003,20 @@ maybe_swap_for_eln (bool no_native, Lisp_Object *filename, int *fd,
If NEWER is true, try all SUFFIXes and return the result for the
newest file that exists. Does not apply to remote files,
- or if a non-nil and non-t PREDICATE is specified.
+ platform-specific files, or if a non-nil and non-t PREDICATE is
+ specified.
+
+ If NO_NATIVE is true do not try to load native code.
- if NO_NATIVE is true do not try to load native code. */
+ If PLATFORM is non-NULL and the file being loaded lies in a special
+ directory, such as the Android `/assets' directory, return a handle
+ to that directory in *PLATFORM instead of a file descriptor; in
+ that case, value is -3. */
int
openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
Lisp_Object *storeptr, Lisp_Object predicate, bool newer,
- bool no_native)
+ bool no_native, void **platform)
{
ptrdiff_t fn_size = 100;
char buf[100];
@@ -1820,6 +2028,9 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
ptrdiff_t max_suffix_len = 0;
int last_errno = ENOENT;
int save_fd = -1;
+#ifdef USE_ANDROID_ASSETS
+ struct android_fd_or_asset platform_fd;
+#endif
USE_SAFE_ALLOCA;
/* The last-modified time of the newest matching file found.
@@ -1964,8 +2175,8 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
fd = -1;
if (INT_MAX < XFIXNAT (predicate))
last_errno = EINVAL;
- else if (faccessat (AT_FDCWD, pfn, XFIXNAT (predicate),
- AT_EACCESS)
+ else if (sys_faccessat (AT_FDCWD, pfn, XFIXNAT (predicate),
+ AT_EACCESS)
== 0)
{
if (file_directory_p (encoded_fn))
@@ -1985,11 +2196,34 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
it. Only open the file when we are sure that it
exists. */
#ifdef WINDOWSNT
- if (faccessat (AT_FDCWD, pfn, R_OK, AT_EACCESS))
+ if (sys_faccessat (AT_FDCWD, pfn, R_OK, AT_EACCESS))
fd = -1;
else
#endif
- fd = emacs_open (pfn, O_RDONLY, 0);
+ {
+#if !defined USE_ANDROID_ASSETS
+ fd = emacs_open (pfn, O_RDONLY, 0);
+#else
+ if (platform)
+ {
+ platform_fd = android_open_asset (pfn, O_RDONLY, 0);
+
+ if (platform_fd.asset
+ && platform_fd.asset != (void *) -1)
+ {
+ *storeptr = string;
+ goto handle_platform_fd;
+ }
+
+ if (platform_fd.asset == (void *) -1)
+ fd = -1;
+ else
+ fd = platform_fd.fd;
+ }
+ else
+ fd = emacs_open (pfn, O_RDONLY, 0);
+#endif
+ }
if (fd < 0)
{
@@ -1998,7 +2232,7 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
}
else
{
- int err = (fstat (fd, &st) != 0 ? errno
+ int err = (sys_fstat (fd, &st) != 0 ? errno
: S_ISDIR (st.st_mode) ? EISDIR : 0);
if (err)
{
@@ -2057,6 +2291,16 @@ openp (Lisp_Object path, Lisp_Object str, Lisp_Object suffixes,
SAFE_FREE ();
errno = last_errno;
return -1;
+
+#ifdef USE_ANDROID_ASSETS
+ handle_platform_fd:
+
+ /* Here, openp found a platform specific file descriptor. It can't
+ be a directory under Android, so return it in *PLATFORM and then
+ -3 as the file descriptor. */
+ *platform = platform_fd.asset;
+ return -3;
+#endif
}
@@ -2088,7 +2332,7 @@ build_load_history (Lisp_Object filename, bool entire)
{
foundit = 1;
- /* If we're loading the entire file, remove old data. */
+ /* If we're loading the entire file, remove old data. */
if (entire)
{
if (NILP (prev))
@@ -2096,8 +2340,8 @@ build_load_history (Lisp_Object filename, bool entire)
else
Fsetcdr (prev, XCDR (tail));
}
-
- /* Otherwise, cons on new symbols that are not already members. */
+ /* Otherwise, cons on new symbols that are not already
+ members. */
else
{
tem2 = Vcurrent_load_list;
@@ -3460,7 +3704,7 @@ skip_lazy_string (Lisp_Object readcharfun)
ss->string = xrealloc (ss->string, ss->size);
}
- FILE *instream = infile->stream;
+ file_stream instream = infile->stream;
ss->position = (file_tell (instream) - infile->lookahead);
/* Copy that many bytes into the saved string. */
@@ -3470,7 +3714,7 @@ skip_lazy_string (Lisp_Object readcharfun)
ss->string[i++] = c = infile->buf[--infile->lookahead];
block_input ();
for (; i < nskip && c >= 0; i++)
- ss->string[i] = c = getc (instream);
+ ss->string[i] = c = file_get_char (instream);
unblock_input ();
ss->length = i;
diff --git a/src/marker.c b/src/marker.c
index e42c49a5434..7b15cd62f1e 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -23,6 +23,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "lisp.h"
#include "character.h"
#include "buffer.h"
+#include "window.h"
/* Record one cached position found recently by
buf_charpos_to_bytepos or buf_bytepos_to_charpos. */
@@ -566,6 +567,31 @@ set_marker_internal (Lisp_Object marker, Lisp_Object position,
attach_marker (m, b, charpos, bytepos);
}
+
+#ifdef HAVE_TEXT_CONVERSION
+
+ /* If B is the buffer's mark and there is a window displaying B, and
+ text conversion is enabled while the mark is active, redisplay
+ the buffer.
+
+ propagate_window_redisplay will propagate this redisplay to the
+ window, which will eventually reach
+ mark_window_display_accurate_1. At that point,
+ report_point_change will be told to update the mark as seen by
+ the input method.
+
+ This is done all the way in (the seemingly irrelevant) redisplay
+ because the selection reported to the input method is actually what
+ is visible on screen, namely w->last_point. */
+
+ if (m->buffer
+ && EQ (marker, BVAR (m->buffer, mark))
+ && !NILP (BVAR (m->buffer, mark_active))
+ && buffer_window_count (m->buffer))
+ bset_redisplay (m->buffer);
+
+#endif
+
return marker;
}
diff --git a/src/menu.c b/src/menu.c
index 73d4215b94b..6ab34a16996 100644
--- a/src/menu.c
+++ b/src/menu.c
@@ -48,7 +48,7 @@ static bool
have_boxes (void)
{
#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NTGUI) || defined (HAVE_NS) \
- || defined (HAVE_HAIKU)
+ || defined (HAVE_HAIKU) || defined (HAVE_ANDROID)
if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame)))
return 1;
#endif
@@ -167,7 +167,7 @@ ensure_menu_items (int items)
}
}
-#ifdef HAVE_EXT_MENU_BAR
+#if defined HAVE_EXT_MENU_BAR || defined HAVE_ANDROID
/* Begin a submenu. */
@@ -191,7 +191,7 @@ push_submenu_end (void)
menu_items_submenu_depth--;
}
-#endif /* HAVE_EXT_MENU_BAR */
+#endif /* HAVE_EXT_MENU_BAR || HAVE_ANDROID */
/* Indicate boundary between left and right. */
@@ -420,8 +420,9 @@ single_menu_item (Lisp_Object key, Lisp_Object item, Lisp_Object dummy, void *sk
AREF (item_properties, ITEM_PROPERTY_SELECTED),
AREF (item_properties, ITEM_PROPERTY_HELP));
-#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
- || defined (HAVE_NTGUI) || defined (HAVE_HAIKU) || defined (HAVE_PGTK)
+#if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) \
+ || defined (HAVE_NTGUI) || defined (HAVE_HAIKU) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
/* Display a submenu using the toolkit. */
if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame))
&& ! (NILP (map) || NILP (enabled)))
@@ -1151,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
@@ -1465,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/pdumper.c b/src/pdumper.c
index 34998549cc8..04e3b66b0c2 100644
--- a/src/pdumper.c
+++ b/src/pdumper.c
@@ -4071,10 +4071,12 @@ types. */)
{
eassert (initialized);
+#ifndef HAVE_ANDROID
if (! noninteractive)
error ("Dumping Emacs currently works only in batch mode. "
"If you'd like it to work interactively, please consider "
"contributing a patch to Emacs.");
+#endif
if (will_dump_with_unexec_p ())
error ("This Emacs instance was started under the assumption "
@@ -4744,7 +4746,9 @@ dump_discard_mem (void *mem, size_t size)
# ifdef HAVE_POSIX_MADVISE
/* Discard COWed pages. */
(void) posix_madvise (mem, size, POSIX_MADV_DONTNEED);
-# endif
+# elif defined HAVE_MADVISE
+ (void) madvise (mem, size, MADV_DONTNEED);
+#endif
/* Release the commit charge for the mapping. */
(void) mprotect (mem, size, PROT_NONE);
#endif
@@ -5614,7 +5618,7 @@ pdumper_load (const char *dump_filename, char *argv0)
}
err = PDUMPER_LOAD_FILE_NOT_FOUND;
- if (fstat (dump_fd, &stat) < 0)
+ if (sys_fstat (dump_fd, &stat) < 0)
goto out;
err = PDUMPER_LOAD_BAD_FILE_TYPE;
@@ -5836,6 +5840,10 @@ void
syms_of_pdumper (void)
{
#ifdef HAVE_PDUMPER
+ unsigned char desired[sizeof fingerprint];
+ int i;
+ char hexbuf[2 * sizeof fingerprint];
+
defsubr (&Sdump_emacs_portable);
defsubr (&Sdump_emacs_portable__sort_predicate);
defsubr (&Sdump_emacs_portable__sort_predicate_copied);
@@ -5848,5 +5856,17 @@ syms_of_pdumper (void)
DEFSYM (Qdump_file_name, "dump-file-name");
DEFSYM (Qafter_pdump_load_hook, "after-pdump-load-hook");
defsubr (&Spdumper_stats);
+
+ for (i = 0; i < sizeof fingerprint; i++)
+ desired[i] = fingerprint[i];
+
+ hexbuf_digest (hexbuf, desired, sizeof desired);
+
+ DEFVAR_LISP ("pdumper-fingerprint", Vpdumper_fingerprint,
+ doc: /* The fingerprint of this Emacs binary.
+It is a string that is supposed to be unique to each build of
+Emacs. */);
+ Vpdumper_fingerprint = make_unibyte_string ((char *) hexbuf,
+ sizeof hexbuf);
#endif /* HAVE_PDUMPER */
}
diff --git a/src/print.c b/src/print.c
index 5c95aeb9a20..940ec7312bf 100644
--- a/src/print.c
+++ b/src/print.c
@@ -1913,12 +1913,17 @@ print_vectorlike (Lisp_Object obj, Lisp_Object printcharfun, bool escapeflag,
print_c_string ("#<font-entity", printcharfun);
for (int i = 0; i < FONT_SPEC_MAX; i++)
{
- printchar (' ', printcharfun);
- if (i < FONT_WEIGHT_INDEX || i > FONT_WIDTH_INDEX)
- print_object (AREF (obj, i), printcharfun, escapeflag);
- else
- print_object (font_style_symbolic (obj, i, 0),
- printcharfun, escapeflag);
+ /* FONT_EXTRA_INDEX can contain private information in
+ font entities which isn't safe to print. */
+ if (i != FONT_EXTRA_INDEX || !FONT_ENTITY_P (obj))
+ {
+ printchar (' ', printcharfun);
+ if (i < FONT_WEIGHT_INDEX || i > FONT_WIDTH_INDEX)
+ print_object (AREF (obj, i), printcharfun, escapeflag);
+ else
+ print_object (font_style_symbolic (obj, i, 0),
+ printcharfun, escapeflag);
+ }
}
}
else
diff --git a/src/process.c b/src/process.c
index 67d1d3e425f..0eff789e599 100644
--- a/src/process.c
+++ b/src/process.c
@@ -119,6 +119,11 @@ static struct rlimit nofile_limit;
#include "gnutls.h"
#endif
+#ifdef HAVE_ANDROID
+#include "android.h"
+#include "androidterm.h"
+#endif
+
#ifdef HAVE_WINDOW_SYSTEM
#include TERM_HEADER
#endif /* HAVE_WINDOW_SYSTEM */
@@ -876,7 +881,8 @@ allocate_pty (char pty_name[PTY_NAME_SIZE])
/* Check to make certain that both sides are available.
This avoids a nasty yet stupid bug in rlogins. */
- if (faccessat (AT_FDCWD, pty_name, R_OK | W_OK, AT_EACCESS) != 0)
+ if (sys_faccessat (AT_FDCWD, pty_name,
+ R_OK | W_OK, AT_EACCESS) != 0)
{
emacs_close (fd);
continue;
@@ -2002,7 +2008,7 @@ usage: (make-process &rest ARGS) */)
{
tem = Qnil;
openp (Vexec_path, program, Vexec_suffixes, &tem,
- make_fixnum (X_OK), false, false);
+ make_fixnum (X_OK), false, false, NULL);
if (NILP (tem))
report_file_error ("Searching for program", program);
tem = Fexpand_file_name (tem, Qnil);
@@ -5679,7 +5685,17 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
timeout = short_timeout;
#endif
- /* Non-macOS HAVE_GLIB builds call thread_select in xgselect.c. */
+ /* Android doesn't support threads and requires using a
+ replacement for pselect in android.c to poll for
+ events. */
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ nfds = android_select (max_desc + 1,
+ &Available, (check_write ? &Writeok : 0),
+ NULL, &timeout);
+#else
+
+ /* Non-macOS HAVE_GLIB builds call thread_select in
+ xgselect.c. */
#if defined HAVE_GLIB && !defined HAVE_NS
nfds = xg_select (max_desc + 1,
&Available, (check_write ? &Writeok : 0),
@@ -5695,6 +5711,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd,
(check_write ? &Writeok : 0),
NULL, &timeout, NULL);
#endif /* !HAVE_GLIB */
+#endif /* HAVE_ANDROID && !ANDROID_STUBIFY */
#ifdef HAVE_GNUTLS
/* Merge tls_available into Available. */
@@ -7231,7 +7248,7 @@ process has been transmitted to the serial port. */)
send_process (proc, "\004", 1, Qnil);
else if (EQ (XPROCESS (proc)->type, Qserial))
{
-#ifndef WINDOWSNT
+#if !defined WINDOWSNT && defined HAVE_TCDRAIN
if (tcdrain (XPROCESS (proc)->outfd) != 0)
report_file_error ("Failed tcdrain", Qnil);
#endif /* not WINDOWSNT */
diff --git a/src/scroll.c b/src/scroll.c
index eee1ad80950..1f530dc5c95 100644
--- a/src/scroll.c
+++ b/src/scroll.c
@@ -21,6 +21,11 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
+/* The entire file is defined out under Android, where there is no
+ text terminal support of any kind. */
+
+#ifndef HAVE_ANDROID
+
#include "lisp.h"
#include "termchar.h"
#include "dispextern.h"
@@ -984,3 +989,5 @@ do_line_insertion_deletion_costs (struct frame *frame,
FRAME_DELETE_COST (frame), FRAME_DELETEN_COST (frame),
coefficient);
}
+
+#endif
diff --git a/src/sfnt.c b/src/sfnt.c
new file mode 100644
index 00000000000..800adc19214
--- /dev/null
+++ b/src/sfnt.c
@@ -0,0 +1,19684 @@
+/* TrueType format font support for GNU Emacs.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+
+#include "sfnt.h"
+
+#include <assert.h>
+#include <attribute.h>
+#include <byteswap.h>
+#include <fcntl.h>
+#include <intprops.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <setjmp.h>
+#include <errno.h>
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#if defined __GNUC__ && !defined __clang__
+#pragma GCC diagnostic ignored "-Wstringop-overflow"
+#endif
+
+#ifdef TEST
+
+#include <time.h>
+#include <timespec.h>
+#include <sys/wait.h>
+#include <errno.h>
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrender.h>
+
+static void *
+xmalloc (size_t size)
+{
+ void *ptr;
+
+ ptr = malloc (size);
+
+ if (!ptr)
+ abort ();
+
+ return ptr;
+}
+
+static void *
+xrealloc (void *ptr, size_t size)
+{
+ void *new_ptr;
+
+ new_ptr = realloc (ptr, size);
+
+ if (!new_ptr)
+ abort ();
+
+ return new_ptr;
+}
+
+static void
+xfree (void *ptr)
+{
+ return free (ptr);
+}
+
+/* Use this for functions that are static while building in test mode,
+ but are used outside as well. */
+#define TEST_STATIC static
+
+/* Needed for tests. */
+#define ARRAYELTS(arr) (sizeof (arr) / sizeof (arr)[0])
+
+#else
+#define TEST_STATIC
+#include "lisp.h"
+#endif
+
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
+
+/* This file provides generic support for reading most TrueType fonts,
+ and some OpenType fonts with TrueType outlines, along with glyph
+ lookup, outline decomposition, and alpha mask generation from those
+ glyphs. It is intended to be used on any platform where proper
+ libraries such as FreeType are not easily available, and the native
+ font library is too limited for Emacs to support properly.
+
+ Unlike most popular libraries for handling fonts, no ``font'' or
+ ``face'' type is provided. Instead, routines and structure
+ definitions for accessing and making use of individual tables in a
+ font file are exported, which allows for flexibility in the rest of
+ Emacs.
+
+ Try not to keep this file too dependent on Emacs. Everything Lisp
+ related goes in sfntfont.c. The author wants to keep using it for
+ some other (free) software.
+
+ The source of reference is the TrueType Reference Manual, published
+ by Apple Computer, which is currently found at:
+
+ https://developer.apple.com/fonts/TrueType-Reference-Manual/
+
+ Apple's TrueType implementation is notably missing features
+ provided by Microsoft's extended OpenType scaler, such as the two
+ additional phantom points on the Y axis, and also behaves
+ differently, especially when it comes to considering phantom points
+ as anchors in compound glyphs.
+
+ As a result, do not expect this scaler to work well with Microsoft
+ fonts such as Arial. */
+
+
+
+/* Mapping between sfnt table names and their identifiers. */
+
+static uint32_t sfnt_table_names[] =
+ {
+ [SFNT_TABLE_CMAP] = 0x636d6170,
+ [SFNT_TABLE_GLYF] = 0x676c7966,
+ [SFNT_TABLE_HEAD] = 0x68656164,
+ [SFNT_TABLE_HHEA] = 0x68686561,
+ [SFNT_TABLE_HMTX] = 0x686d7478,
+ [SFNT_TABLE_LOCA] = 0x6c6f6361,
+ [SFNT_TABLE_MAXP] = 0x6d617870,
+ [SFNT_TABLE_NAME] = 0x6e616d65,
+ [SFNT_TABLE_META] = 0x6d657461,
+ [SFNT_TABLE_CVT ] = 0x63767420,
+ [SFNT_TABLE_FPGM] = 0x6670676d,
+ [SFNT_TABLE_PREP] = 0x70726570,
+ [SFNT_TABLE_FVAR] = 0x66766172,
+ [SFNT_TABLE_GVAR] = 0x67766172,
+ [SFNT_TABLE_CVAR] = 0x63766172,
+ [SFNT_TABLE_AVAR] = 0x61766172,
+ };
+
+/* Swap values from TrueType to system byte order. */
+
+static void
+sfnt_swap16_1 (uint16_t *value)
+{
+#ifndef WORDS_BIGENDIAN
+ *value = bswap_16 (*value);
+#endif
+}
+
+static void
+sfnt_swap32_1 (uint32_t *value)
+{
+#ifndef WORDS_BIGENDIAN
+ *value = bswap_32 (*value);
+#endif
+}
+
+#define sfnt_swap16(what) (sfnt_swap16_1 ((uint16_t *) (what)))
+#define sfnt_swap32(what) (sfnt_swap32_1 ((uint32_t *) (what)))
+
+/* Read the table directory from the file FD. FD must currently be at
+ the start of the file (or an offset defined in the TTC header, if
+ applicable), and must be seekable. Return the table directory upon
+ success, else NULL.
+
+ Value is NULL upon failure, and the offset subtable upon success.
+ If FD is actually a TrueType collection file, value is -1. */
+
+TEST_STATIC struct sfnt_offset_subtable *
+sfnt_read_table_directory (int fd)
+{
+ struct sfnt_offset_subtable *subtable;
+ ssize_t rc;
+ size_t offset, subtable_size;
+ int i;
+
+ subtable = xmalloc (sizeof *subtable);
+ offset = SFNT_ENDOF (struct sfnt_offset_subtable,
+ range_shift, uint16_t);
+ rc = read (fd, subtable, offset);
+
+ if (rc < offset)
+ {
+ if (rc >= sizeof (uint32_t))
+ {
+ /* Detect a TTC file. In that case, the first long will be
+ ``ttcf''. */
+ sfnt_swap32 (&subtable->scaler_type);
+
+ if (subtable->scaler_type == SFNT_TTC_TTCF)
+ {
+ xfree (subtable);
+ return (struct sfnt_offset_subtable *) -1;
+ }
+ }
+
+ xfree (subtable);
+ return NULL;
+ }
+
+ sfnt_swap32 (&subtable->scaler_type);
+
+ /* Bail out early if this font is actually a TrueType collection
+ file. */
+
+ if (subtable->scaler_type == SFNT_TTC_TTCF)
+ {
+ xfree (subtable);
+ return (struct sfnt_offset_subtable *) -1;
+ }
+
+ sfnt_swap16 (&subtable->num_tables);
+ sfnt_swap16 (&subtable->search_range);
+ sfnt_swap16 (&subtable->entry_selector);
+ sfnt_swap16 (&subtable->range_shift);
+
+ /* Figure out how many more tables have to be read, and read each
+ one of them. */
+ subtable_size = (subtable->num_tables
+ * sizeof (struct sfnt_table_directory));
+ subtable = xrealloc (subtable, sizeof *subtable + subtable_size);
+ subtable->subtables
+ = (struct sfnt_table_directory *) (subtable + 1);
+
+ rc = read (fd, subtable->subtables, subtable_size);
+
+ if (rc < offset)
+ {
+ xfree (subtable);
+ return NULL;
+ }
+
+ /* Swap each of the subtables. */
+
+ for (i = 0; i < subtable->num_tables; ++i)
+ {
+ sfnt_swap32 (&subtable->subtables[i].tag);
+ sfnt_swap32 (&subtable->subtables[i].checksum);
+ sfnt_swap32 (&subtable->subtables[i].offset);
+ sfnt_swap32 (&subtable->subtables[i].length);
+ }
+
+ return subtable;
+}
+
+/* Return a pointer to the table directory entry for TABLE in
+ SUBTABLE, or NULL if it was not found. */
+
+static struct sfnt_table_directory *
+sfnt_find_table (struct sfnt_offset_subtable *subtable,
+ enum sfnt_table table)
+{
+ int i;
+
+ for (i = 0; i < subtable->num_tables; ++i)
+ {
+ if (subtable->subtables[i].tag == sfnt_table_names[table])
+ return &subtable->subtables[i];
+ }
+
+ return NULL;
+}
+
+
+
+/* Character mapping routines. */
+
+/* Read a format 0 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_0 *
+sfnt_read_cmap_format_0 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_0 *format0;
+ ssize_t rc;
+ size_t wanted_size;
+
+ format0 = xmalloc (sizeof *format0);
+
+ /* Fill in fields that have already been read. */
+ format0->format = header->format;
+ format0->length = header->length;
+
+ /* Read the rest. */
+ wanted_size = (sizeof *format0
+ - offsetof (struct sfnt_cmap_format_0,
+ language));
+ rc = read (fd, &format0->language, wanted_size);
+
+ if (rc < wanted_size)
+ {
+ xfree (format0);
+ return (struct sfnt_cmap_format_0 *) -1;
+ }
+
+ /* Swap fields and return. */
+ sfnt_swap16 (&format0->language);
+ return format0;
+}
+
+/* Read a format 2 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_2 *
+sfnt_read_cmap_format_2 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_2 *format2;
+ ssize_t rc;
+ size_t min_bytes;
+ int i, nsub;
+
+ /* Reject contents that are too small. */
+ min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_2,
+ sub_header_keys, uint16_t[256]);
+ if (header->length < min_bytes)
+ return NULL;
+
+ /* Add enough bytes at the end to fit the two variable length
+ pointers. */
+ format2 = xmalloc (header->length + sizeof *format2);
+ format2->format = header->format;
+ format2->length = header->length;
+
+ /* Read the part before the variable length data. */
+ min_bytes -= offsetof (struct sfnt_cmap_format_2, language);
+ rc = read (fd, &format2->language, min_bytes);
+ if (rc < min_bytes)
+ {
+ xfree (format2);
+ return (struct sfnt_cmap_format_2 *) -1;
+ }
+
+ /* Swap the fields now. */
+
+ sfnt_swap16 (&format2->language);
+
+ /* At the same time, look for the largest value in sub_header_keys.
+ That will be the number of subheaders and elements in the glyph
+ index array. */
+
+ nsub = 0;
+
+ for (i = 0; i < 256; ++i)
+ {
+ sfnt_swap16 (&format2->sub_header_keys[i]);
+
+ if (format2->sub_header_keys[i] > nsub)
+ nsub = format2->sub_header_keys[i];
+ }
+
+ if (!nsub)
+ /* If there are no subheaders, then things are finished. */
+ return format2;
+
+ /* Otherwise, read the rest of the variable length data to the end
+ of format2. */
+ min_bytes = (format2->length
+ - SFNT_ENDOF (struct sfnt_cmap_format_2,
+ sub_header_keys, uint16_t[256]));
+ rc = read (fd, format2 + 1, min_bytes);
+ if (rc < min_bytes)
+ {
+ xfree (format2);
+ return (struct sfnt_cmap_format_2 *) -1;
+ }
+
+ /* Check whether or not the data is of the correct size. */
+ if (min_bytes < nsub * sizeof *format2->subheaders)
+ {
+ xfree (format2);
+ return (struct sfnt_cmap_format_2 *) -1;
+ }
+
+ /* Point the data pointers to the right location, swap everything,
+ and return. */
+
+ format2->subheaders
+ = (struct sfnt_cmap_format_2_subheader *) (format2 + 1);
+ format2->glyph_index_array
+ = (uint16_t *) (format2->subheaders + nsub);
+
+ for (i = 0; i < nsub; ++i)
+ {
+ sfnt_swap16 (&format2->subheaders[i].first_code);
+ sfnt_swap16 (&format2->subheaders[i].entry_count);
+ sfnt_swap16 (&format2->subheaders[i].id_delta);
+ sfnt_swap16 (&format2->subheaders[i].id_range_offset);
+ }
+
+ /* Figure out how big the glyph index array is, and swap everything
+ there. */
+ format2->num_glyphs
+ = (min_bytes - nsub * sizeof *format2->subheaders) / 2;
+
+ for (i = 0; i < format2->num_glyphs; ++i)
+ sfnt_swap16 (&format2->glyph_index_array[i]);
+
+ return format2;
+}
+
+/* Read a format 4 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_4 *
+sfnt_read_cmap_format_4 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_4 *format4;
+ size_t min_bytes, variable_size;
+ ssize_t rc;
+ size_t bytes_minus_format4;
+ int seg_count, i;
+
+ min_bytes = SFNT_ENDOF (struct sfnt_cmap_format_4,
+ entry_selector, uint16_t);
+
+ /* Check that the length is at least min_bytes. */
+ if (header->length < min_bytes)
+ return NULL;
+
+ /* Allocate the format4 buffer, making it the size of the buffer
+ itself plus that of the data. */
+ format4 = xmalloc (header->length + sizeof *format4);
+
+ /* Copy over fields that have already been read. */
+ format4->format = header->format;
+ format4->length = header->length;
+
+ /* Read the initial data. */
+ min_bytes -= offsetof (struct sfnt_cmap_format_4, language);
+ rc = read (fd, &format4->language, min_bytes);
+ if (rc < min_bytes)
+ {
+ xfree (format4);
+ return (struct sfnt_cmap_format_4 *) -1;
+ }
+
+ /* Swap fields that have been read. */
+ sfnt_swap16 (&format4->language);
+ sfnt_swap16 (&format4->seg_count_x2);
+ sfnt_swap16 (&format4->search_range);
+ sfnt_swap16 (&format4->entry_selector);
+
+ /* Get the number of segments to read. */
+ seg_count = format4->seg_count_x2 / 2;
+
+ /* Now calculate whether or not the size is sufficiently large. */
+ bytes_minus_format4
+ = format4->length - SFNT_ENDOF (struct sfnt_cmap_format_4,
+ entry_selector, uint16_t);
+ variable_size = (seg_count * sizeof *format4->end_code
+ + sizeof *format4->reserved_pad
+ + seg_count * sizeof *format4->start_code
+ + seg_count * sizeof *format4->id_delta
+ + seg_count * sizeof *format4->id_range_offset);
+
+ if (bytes_minus_format4 < variable_size)
+ {
+ /* Not enough bytes to fit the entire implied table
+ contents. */
+ xfree (format4);
+ return NULL;
+ }
+
+ /* Read the rest of the bytes to the end of format4. */
+ rc = read (fd, format4 + 1, bytes_minus_format4);
+ if (rc < bytes_minus_format4)
+ {
+ xfree (format4);
+ return (struct sfnt_cmap_format_4 *) -1;
+ }
+
+ /* Set data pointers to the right locations. */
+ format4->end_code = (uint16_t *) (format4 + 1);
+ format4->reserved_pad = format4->end_code + seg_count;
+ format4->start_code = format4->reserved_pad + 1;
+ format4->id_delta = (int16_t *) (format4->start_code + seg_count);
+ format4->id_range_offset = format4->id_delta + seg_count;
+ format4->glyph_index_array = (uint16_t *) (format4->id_range_offset
+ + seg_count);
+
+ /* N.B. that the number of elements in glyph_index_array is
+ (bytes_minus_format4 - variable_size) / 2. Swap all the
+ data. */
+
+ sfnt_swap16 (format4->reserved_pad);
+
+ for (i = 0; i < seg_count; ++i)
+ {
+ sfnt_swap16 (&format4->end_code[i]);
+ sfnt_swap16 (&format4->start_code[i]);
+ sfnt_swap16 (&format4->id_delta[i]);
+ sfnt_swap16 (&format4->id_range_offset[i]);
+ }
+
+ format4->glyph_index_size
+ = (bytes_minus_format4 - variable_size) / 2;
+
+ for (i = 0; i < format4->glyph_index_size; ++i)
+ sfnt_swap16 (&format4->glyph_index_array[i]);
+
+ /* Done. Return the format 4 character map. */
+ return format4;
+}
+
+/* Read a format 6 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_6 *
+sfnt_read_cmap_format_6 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_6 *format6;
+ size_t min_size;
+ ssize_t rc;
+ uint16_t i;
+
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_6, entry_count,
+ uint16_t);
+
+ /* See if header->length is big enough. */
+ if (header->length < min_size)
+ return NULL;
+
+ /* Allocate the buffer to hold header->size and enough for at least
+ the glyph index array pointer. */
+ format6 = xmalloc (header->length + sizeof *format6);
+
+ /* Fill in data that has already been read. */
+ format6->format = header->format;
+ format6->length = header->length;
+
+ /* Read the fixed size data. */
+ min_size -= offsetof (struct sfnt_cmap_format_6, language);
+ rc = read (fd, &format6->language, min_size);
+ if (rc < min_size)
+ {
+ xfree (format6);
+ return (struct sfnt_cmap_format_6 *) -1;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap16 (&format6->language);
+ sfnt_swap16 (&format6->first_code);
+ sfnt_swap16 (&format6->entry_count);
+
+ /* Figure out whether or not header->length is sufficient to hold
+ the variable length data. */
+ if (header->length
+ < format6->entry_count * sizeof *format6->glyph_index_array)
+ {
+ xfree (format6);
+ return NULL;
+ }
+
+ /* Read the variable length data. */
+ rc = read (fd, format6 + 1,
+ (format6->entry_count
+ * sizeof *format6->glyph_index_array));
+ if (rc < format6->entry_count * sizeof *format6->glyph_index_array)
+ {
+ xfree (format6);
+ return (struct sfnt_cmap_format_6 *) -1;
+ }
+
+ /* Set the data pointer and swap everything. */
+ format6->glyph_index_array = (uint16_t *) (format6 + 1);
+ for (i = 0; i < format6->entry_count; ++i)
+ sfnt_swap16 (&format6->glyph_index_array[i]);
+
+ /* All done! */
+ return format6;
+}
+
+/* Read a format 8 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_8 *
+sfnt_read_cmap_format_8 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_8 *format8;
+ size_t min_size, temp;
+ ssize_t rc;
+ uint32_t length, i;
+
+ /* Read the 32-bit length field. */
+ if (read (fd, &length, sizeof (length)) < sizeof (length))
+ return (struct sfnt_cmap_format_8 *) -1;
+
+ /* Swap the 32-bit length field. */
+ sfnt_swap32 (&length);
+
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_8, num_groups,
+ uint32_t);
+
+ /* Make sure the header is at least as large as min_size. */
+ if (length < min_size)
+ return NULL;
+
+ /* Allocate a buffer of sufficient size. */
+ format8 = xmalloc (length + sizeof *format8);
+ format8->format = header->format;
+ format8->reserved = header->length;
+ format8->length = length;
+
+ /* Read the fixed length data. */
+ min_size -= offsetof (struct sfnt_cmap_format_8, language);
+ rc = read (fd, &format8->language, min_size);
+ if (rc < min_size)
+ {
+ xfree (format8);
+ return (struct sfnt_cmap_format_8 *) -1;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap32 (&format8->language);
+ sfnt_swap32 (&format8->num_groups);
+
+ /* See if the size is sufficient to read the variable length
+ data. */
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_8, num_groups,
+ uint32_t);
+
+ if (INT_MULTIPLY_WRAPV (format8->num_groups, sizeof *format8->groups,
+ &temp))
+ {
+ xfree (format8);
+ return NULL;
+ }
+
+ if (INT_ADD_WRAPV (min_size, temp, &min_size))
+ {
+ xfree (format8);
+ return NULL;
+ }
+
+ if (length < min_size)
+ {
+ xfree (format8);
+ return NULL;
+ }
+
+ /* Now read the variable length data. */
+ rc = read (fd, format8 + 1, temp);
+ if (rc < temp)
+ {
+ xfree (format8);
+ return (struct sfnt_cmap_format_8 *) -1;
+ }
+
+ /* Set the pointer to the variable length data. */
+ format8->groups
+ = (struct sfnt_cmap_format_8_or_12_group *) (format8 + 1);
+
+ for (i = 0; i < format8->num_groups; ++i)
+ {
+ sfnt_swap32 (&format8->groups[i].start_char_code);
+ sfnt_swap32 (&format8->groups[i].end_char_code);
+ sfnt_swap32 (&format8->groups[i].start_glyph_code);
+ }
+
+ /* All done. */
+ return format8;
+}
+
+/* Read a format 12 cmap subtable from FD. HEADER has already been
+ read. */
+
+static struct sfnt_cmap_format_12 *
+sfnt_read_cmap_format_12 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header)
+{
+ struct sfnt_cmap_format_12 *format12;
+ size_t min_size, temp;
+ ssize_t rc;
+ uint32_t length, i;
+
+ /* Read the 32-bit length field. */
+ if (read (fd, &length, sizeof (length)) < sizeof (length))
+ return (struct sfnt_cmap_format_12 *) -1;
+
+ /* Swap the 32-bit length field. */
+ sfnt_swap32 (&length);
+
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_12, num_groups,
+ uint32_t);
+
+ /* Make sure the header is at least as large as min_size. */
+ if (length < min_size)
+ return NULL;
+
+ /* Allocate a buffer of sufficient size. */
+ format12 = xmalloc (length + sizeof *format12);
+ format12->format = header->format;
+ format12->reserved = header->length;
+ format12->length = length;
+
+ /* Read the fixed length data. */
+ min_size -= offsetof (struct sfnt_cmap_format_12, language);
+ rc = read (fd, &format12->language, min_size);
+ if (rc < min_size)
+ {
+ xfree (format12);
+ return (struct sfnt_cmap_format_12 *) -1;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap32 (&format12->language);
+ sfnt_swap32 (&format12->num_groups);
+
+ /* See if the size is sufficient to read the variable length
+ data. */
+ min_size = SFNT_ENDOF (struct sfnt_cmap_format_12, num_groups,
+ uint32_t);
+
+ if (INT_MULTIPLY_WRAPV (format12->num_groups, sizeof *format12->groups,
+ &temp))
+ {
+ xfree (format12);
+ return NULL;
+ }
+
+ if (INT_ADD_WRAPV (min_size, temp, &min_size))
+ {
+ xfree (format12);
+ return NULL;
+ }
+
+ if (length < min_size)
+ {
+ xfree (format12);
+ return NULL;
+ }
+
+ /* Now read the variable length data. */
+ rc = read (fd, format12 + 1, temp);
+ if (rc < temp)
+ {
+ xfree (format12);
+ return (struct sfnt_cmap_format_12 *) -1;
+ }
+
+ /* Set the pointer to the variable length data. */
+ format12->groups
+ = (struct sfnt_cmap_format_8_or_12_group *) (format12 + 1);
+
+ for (i = 0; i < format12->num_groups; ++i)
+ {
+ sfnt_swap32 (&format12->groups[i].start_char_code);
+ sfnt_swap32 (&format12->groups[i].end_char_code);
+ sfnt_swap32 (&format12->groups[i].start_glyph_code);
+ }
+
+ /* All done. */
+ return format12;
+}
+
+/* Read a 3-byte big endian number from BYTES. */
+
+static unsigned int
+sfnt_read_24 (unsigned char *bytes)
+{
+ return (bytes[0] << 16u) | (bytes[1] << 8u) | bytes[2];
+}
+
+/* Read a format 14 cmap table from FD. HEADER->format will be 14 and
+ HEADER->length will be 0; the 16-bit length field is not read.
+ OFFSET is the offset of the table's header in the font file.
+
+ Only variation selector records will be read. UVS tables will
+ not. */
+
+static struct sfnt_cmap_format_14 *
+sfnt_read_cmap_format_14 (int fd,
+ struct sfnt_cmap_encoding_subtable_data *header,
+ off_t offset)
+{
+ struct sfnt_cmap_format_14 *format14;
+ uint32_t length;
+ uint32_t num_records;
+ uint32_t buffer1[2];
+ size_t size, temp;
+ char buffer[3 + 4 + 4];
+ int i;
+
+ /* Read the length field and number of variation selector
+ records. */
+
+ if (read (fd, buffer1, sizeof buffer1) < sizeof buffer1)
+ return NULL;
+
+ length = buffer1[0];
+ num_records = buffer1[1];
+
+ sfnt_swap32 (&length);
+ sfnt_swap32 (&num_records);
+
+ /* Now, the number of records present is known. Allocate the format
+ 14 cmap table. */
+
+ size = sizeof *format14;
+ if (INT_MULTIPLY_WRAPV (num_records, sizeof *format14->records,
+ &temp)
+ || INT_ADD_WRAPV (size, temp, &size))
+ return NULL;
+
+ format14 = xmalloc (size);
+
+ /* Fill in the data already read. */
+ format14->format = header->format;
+ format14->length = length;
+ format14->num_var_selector_records = num_records;
+ format14->offset = offset;
+
+ /* Set the pointer to the remaining record data. */
+ format14->records
+ = (struct sfnt_variation_selector_record *) (format14 + 1);
+
+ /* Read each variation selector record. */
+
+ for (i = 0; i < num_records; ++i)
+ {
+ if (read (fd, buffer, sizeof buffer) < sizeof buffer)
+ {
+ xfree (format14);
+ return NULL;
+ }
+
+ /* First, read the 24 bit variation selector. */
+ format14->records[i].var_selector
+ = sfnt_read_24 ((unsigned char *) buffer);
+
+ /* Next, read the two unaligned longs. */
+ memcpy (&format14->records[i].default_uvs_offset,
+ buffer + 3,
+ sizeof format14->records[i].default_uvs_offset);
+ memcpy (&format14->records[i].nondefault_uvs_offset,
+ buffer + 7,
+ sizeof format14->records[i].nondefault_uvs_offset);
+
+ /* And swap them. */
+ sfnt_swap32 (&format14->records[i].default_uvs_offset);
+ sfnt_swap32 (&format14->records[i].nondefault_uvs_offset);
+ }
+
+ /* Return the format 14 character mapping table. */
+ return format14;
+}
+
+/* Read the CMAP subtable data from a given file FD at TABLE_OFFSET
+ bytes from DIRECTORY_OFFSET. Return the subtable data if it is
+ supported. Else, value is NULL if the format is unsupported, or -1
+ upon an IO error. */
+
+static struct sfnt_cmap_encoding_subtable_data *
+sfnt_read_cmap_table_1 (int fd, uint32_t directory_offset,
+ uint32_t table_offset)
+{
+ off_t offset;
+ struct sfnt_cmap_encoding_subtable_data header;
+
+ if (INT_ADD_WRAPV (directory_offset, table_offset, &offset))
+ return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+ if (lseek (fd, offset, SEEK_SET) == (off_t) -1)
+ return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+ if (read (fd, &header.format, sizeof header.format)
+ < sizeof header.format)
+ return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+ sfnt_swap16 (&header.format);
+
+ /* Format 14 tables are rather special: they do not have a 16-bit
+ `length' field. When these tables are encountered, leave reading
+ the rest of the header to `sfnt_read_cmap_table_14'. */
+
+ if (header.format != 14)
+ {
+ if (read (fd, &header.length, sizeof header.length)
+ < sizeof header.length)
+ return (struct sfnt_cmap_encoding_subtable_data *) -1;
+
+ sfnt_swap16 (&header.length);
+ }
+ else
+ header.length = 0;
+
+ switch (header.format)
+ {
+ case 0:
+ /* If the length changes, then something has changed to the
+ format. */
+ if (header.length != 262)
+ return NULL;
+
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_0 (fd, &header));
+
+ case 2:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_2 (fd, &header));
+
+ case 4:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_4 (fd, &header));
+
+ case 6:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_6 (fd, &header));
+
+ case 8:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_8 (fd, &header));
+
+ case 12:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_12 (fd, &header));
+
+ case 14:
+ return ((struct sfnt_cmap_encoding_subtable_data *)
+ sfnt_read_cmap_format_14 (fd, &header, offset));
+
+ default:
+ return NULL;
+ }
+}
+
+/* Read the CMAP table of a given font from the file FD. Use the
+ table directory specified in SUBTABLE.
+
+ Return the CMAP table and a list of encoding subtables in
+ *SUBTABLES and *DATA upon success, else NULL. If DATA is NULL, do
+ not read the subtable data. */
+
+TEST_STATIC struct sfnt_cmap_table *
+sfnt_read_cmap_table (int fd, struct sfnt_offset_subtable *subtable,
+ struct sfnt_cmap_encoding_subtable **subtables,
+ struct sfnt_cmap_encoding_subtable_data ***data)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_cmap_table *cmap;
+ ssize_t rc;
+ int i, j;
+
+ /* Find the CMAP table in the table directory. */
+ directory = sfnt_find_table (subtable, SFNT_TABLE_CMAP);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the start of the CMAP table. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Read the table header. */
+ cmap = xmalloc (sizeof *cmap);
+ rc = read (fd, cmap, sizeof *cmap);
+
+ if (rc < sizeof *cmap)
+ {
+ xfree (cmap);
+ return NULL;
+ }
+
+ /* Swap the header data. */
+ sfnt_swap16 (&cmap->version);
+ sfnt_swap16 (&cmap->num_subtables);
+
+ if (cmap->version != 0)
+ {
+ xfree (cmap);
+ return NULL;
+ }
+
+ *subtables = xmalloc (cmap->num_subtables
+ * sizeof **subtables);
+
+
+ /* First, read the common parts of each encoding subtable. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ /* Read the common part of the new subtable. */
+ rc = read (fd, &(*subtables)[i], sizeof (*subtables)[i]);
+
+ if (rc < sizeof (*subtables))
+ {
+ xfree (cmap);
+ xfree (*subtables);
+ return NULL;
+ }
+
+ sfnt_swap16 (&(*subtables)[i].platform_id);
+ sfnt_swap16 (&(*subtables)[i].platform_specific_id);
+ sfnt_swap32 (&(*subtables)[i].offset);
+ }
+
+ /* If data is NULL, the caller only wants the table headers. */
+
+ if (!data)
+ return cmap;
+
+ /* Second, read each encoding subtable itself. */
+ *data = xmalloc (cmap->num_subtables * sizeof *data);
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ (*data)[i] = sfnt_read_cmap_table_1 (fd, directory->offset,
+ (*subtables)[i].offset);
+
+ if ((*data)[i] == (void *) -1)
+ {
+ /* An IO error occurred (as opposed to the subtable format
+ being unsupported.) Return now. */
+
+ for (j = 0; j < i; ++j)
+ xfree ((*data)[j]);
+
+ xfree (*data);
+ xfree (*subtables);
+ xfree (cmap);
+ return NULL;
+ }
+ }
+
+ return cmap;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 0 cmap
+ FORMAT0. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_0 (sfnt_char character,
+ struct sfnt_cmap_format_0 *format0)
+{
+ if (character >= 256)
+ return 0;
+
+ return format0->glyph_index_array[character];
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 2 cmap
+ FORMAT2. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_2 (sfnt_char character,
+ struct sfnt_cmap_format_2 *format2)
+{
+ unsigned char i, k, j;
+ struct sfnt_cmap_format_2_subheader *subheader;
+ unsigned char *slice;
+ uint16_t glyph;
+
+ if (character > 65335)
+ return 0;
+
+ i = character >> 16;
+ j = character & 0xff;
+ k = format2->sub_header_keys[i] / 8;
+
+ if (k)
+ {
+ subheader = &format2->subheaders[k];
+
+ if (subheader->first_code <= j
+ && j <= ((int) subheader->first_code
+ + (int) subheader->entry_count))
+ {
+ /* id_range_offset is actually the number of bytes past
+ itself containing the uint16_t ``slice''. It is possibly
+ unaligned. */
+ slice = (unsigned char *) &subheader->id_range_offset;
+ slice += subheader->id_range_offset;
+ slice += (j - subheader->first_code) * sizeof (uint16_t);
+
+ if (slice < (unsigned char *) format2->glyph_index_array
+ || (slice + 1
+ > (unsigned char *) (format2->glyph_index_array
+ + format2->num_glyphs)))
+ /* The character is out of bounds. */
+ return 0;
+
+ memcpy (&glyph, slice, sizeof glyph);
+ return (glyph + subheader->id_delta) % 65536;
+ }
+ else
+ return 0;
+ }
+
+ /* k is 0, so glyph_index_array[i] is the glyph. */
+ return (i < format2->num_glyphs
+ ? format2->glyph_index_array[i]
+ : 0);
+}
+
+/* Like `bsearch'. However, return the highest element above KEY if
+ it could not be found. */
+
+static void *
+sfnt_bsearch_above (const void *key, const void *base,
+ size_t nmemb, size_t size,
+ int (*compar) (const void *,
+ const void *))
+{
+ const unsigned char *bytes, *sample;
+ size_t low, high, mid;
+
+ bytes = base;
+ low = 0;
+ high = nmemb - 1;
+
+ if (!nmemb)
+ return NULL;
+
+ while (low != high)
+ {
+ mid = low + (high - low) / 2;
+ sample = bytes + mid * size;
+
+ if (compar (key, sample) > 0)
+ low = mid + 1;
+ else
+ high = mid;
+ }
+
+ return (unsigned char *) bytes + low * size;
+}
+
+/* Compare two uint16_t's. Used to bisect through a format 4
+ table. */
+
+static int
+sfnt_compare_uint16 (const void *a, const void *b)
+{
+ return ((int) *((uint16_t *) a)) - ((int) *((uint16_t *) b));
+}
+
+/* Look up the glyph corresponding to CODE in the format 4 cmap
+ FORMAT4, using the table segment SEGMENT. Value is 0 if no glyph
+ was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_4_1 (uint16_t code, uint16_t segment,
+ struct sfnt_cmap_format_4 *format4)
+{
+ uint16_t *index;
+
+ if (format4->id_range_offset[segment])
+ {
+ /* id_range_offset is not 0, so the glyph mapping depends on
+ it. */
+ index = (uint16_t *) (&format4->id_range_offset[segment]
+ + format4->id_range_offset[segment] / 2
+ + (code - format4->start_code[segment]));
+
+ /* Check that index is not out of bounds. */
+ if (index >= (format4->glyph_index_array
+ + format4->glyph_index_size)
+ || index < format4->glyph_index_array)
+ return 0;
+
+ /* Return what is in index. */
+ return (*index ? (format4->id_delta[segment]
+ + *index) % 65536 : 0);
+ }
+
+ /* Otherwise, just add id_delta. */
+ return (format4->id_delta[segment] + code) % 65536;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 4 cmap
+ FORMAT4. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_4 (sfnt_char character,
+ struct sfnt_cmap_format_4 *format4)
+{
+ uint16_t *segment_address;
+ uint16_t code, segment;
+ sfnt_glyph glyph;
+
+ if (character > 65535)
+ return 0;
+
+ code = character;
+
+ /* Find the segment ending above or at CHARACTER. */
+ segment_address = sfnt_bsearch_above (&code, format4->end_code,
+ format4->seg_count_x2 / 2,
+ sizeof code,
+ sfnt_compare_uint16);
+ segment = segment_address - format4->end_code;
+
+ /* If the segment starts too late, return 0. */
+ if (!segment_address || format4->start_code[segment] > character)
+ return 0;
+
+ glyph = sfnt_lookup_glyph_4_1 (character, segment, format4);
+
+ if (glyph)
+ return glyph;
+
+ /* Droid Sans Mono has overlapping segments in its format 4 cmap
+ subtable where the first segment's end code is 32, while the
+ second segment's start code is also 32. The TrueType Reference
+ Manual says that mapping should begin by searching for the first
+ segment whose end code is greater than or equal to the character
+ being indexed, but that results in the first subtable being
+ found, which doesn't work, while the second table does. Try to
+ detect this situation and use the second table if possible. */
+
+ if (!glyph
+ /* The character being looked up is the current segment's end
+ code. */
+ && code == format4->end_code[segment]
+ /* There is an additional segment. */
+ && segment + 1 < format4->seg_count_x2 / 2
+ /* That segment's start code is the same as this segment's end
+ code. */
+ && format4->start_code[segment + 1] == format4->end_code[segment])
+ /* Try the second segment. */
+ return sfnt_lookup_glyph_4_1 (character, segment + 1, format4);
+
+ /* Fail. */
+ return 0;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 6 cmap
+ FORMAT6. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_6 (sfnt_char character,
+ struct sfnt_cmap_format_6 *format6)
+{
+ if (character < format6->first_code
+ || character >= (format6->first_code
+ + (int) format6->entry_count))
+ return 0;
+
+ return format6->glyph_index_array[character - format6->first_code];
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 8 cmap
+ FORMAT8. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_8 (sfnt_char character,
+ struct sfnt_cmap_format_8 *format8)
+{
+ uint32_t i;
+
+ if (character > 0xffffffff)
+ return 0;
+
+ for (i = 0; i < format8->num_groups; ++i)
+ {
+ if (format8->groups[i].start_char_code <= character
+ && format8->groups[i].end_char_code >= character)
+ return (format8->groups[i].start_glyph_code
+ + (character
+ - format8->groups[i].start_char_code));
+ }
+
+ return 0;
+}
+
+/* Look up the glyph corresponding to CHARACTER in the format 12 cmap
+ FORMAT12. Return 0 if no glyph was found. */
+
+static sfnt_glyph
+sfnt_lookup_glyph_12 (sfnt_char character,
+ struct sfnt_cmap_format_12 *format12)
+{
+ uint32_t i;
+
+ if (character > 0xffffffff)
+ return 0;
+
+ for (i = 0; i < format12->num_groups; ++i)
+ {
+ if (format12->groups[i].start_char_code <= character
+ && format12->groups[i].end_char_code >= character)
+ return (format12->groups[i].start_glyph_code
+ + (character
+ - format12->groups[i].start_char_code));
+ }
+
+ return 0;
+}
+
+/* Look up the glyph index corresponding to the character CHARACTER,
+ which must be in the correct encoding for the cmap table pointed to
+ by DATA.
+
+ DATA must be either a format 0, 2, 4, 6, 8 or 12 cmap table, else
+ behavior is undefined. */
+
+TEST_STATIC sfnt_glyph
+sfnt_lookup_glyph (sfnt_char character,
+ struct sfnt_cmap_encoding_subtable_data *data)
+{
+ switch (data->format)
+ {
+ case 0:
+ return sfnt_lookup_glyph_0 (character,
+ (struct sfnt_cmap_format_0 *) data);
+
+ case 2:
+ return sfnt_lookup_glyph_2 (character,
+ (struct sfnt_cmap_format_2 *) data);
+
+ case 4:
+ return sfnt_lookup_glyph_4 (character,
+ (struct sfnt_cmap_format_4 *) data);
+
+ case 6:
+ return sfnt_lookup_glyph_6 (character,
+ (struct sfnt_cmap_format_6 *) data);
+
+ case 8:
+ return sfnt_lookup_glyph_8 (character,
+ (struct sfnt_cmap_format_8 *) data);
+
+ case 12:
+ return sfnt_lookup_glyph_12 (character,
+ (struct sfnt_cmap_format_12 *) data);
+ }
+
+ return 0;
+}
+
+
+
+/* Header reading routines. */
+
+/* Read the head table of a given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the head table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_head_table *
+sfnt_read_head_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_head_table *head;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_HEAD);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Read the entire table. */
+ head = xmalloc (sizeof *head);
+ rc = read (fd, head, sizeof *head);
+
+ if (rc < sizeof *head)
+ {
+ xfree (head);
+ return NULL;
+ }
+
+ /* Swap the header data. */
+ sfnt_swap32 (&head->version);
+ sfnt_swap32 (&head->revision);
+
+ if (head->version != 0x00010000)
+ {
+ xfree (head);
+ return NULL;
+ }
+
+ /* Swap the rest of the data. */
+ sfnt_swap32 (&head->checksum_adjustment);
+ sfnt_swap32 (&head->magic);
+
+ if (head->magic != 0x5f0f3cf5)
+ {
+ xfree (head);
+ return NULL;
+ }
+
+ sfnt_swap16 (&head->flags);
+ sfnt_swap16 (&head->units_per_em);
+ sfnt_swap32 (&head->created_high);
+ sfnt_swap32 (&head->created_low);
+ sfnt_swap32 (&head->modified_high);
+ sfnt_swap32 (&head->modified_low);
+ sfnt_swap16 (&head->xmin);
+ sfnt_swap16 (&head->xmax);
+ sfnt_swap16 (&head->ymin);
+ sfnt_swap16 (&head->ymax);
+ sfnt_swap16 (&head->mac_style);
+ sfnt_swap16 (&head->lowest_rec_ppem);
+ sfnt_swap16 (&head->font_direction_hint);
+ sfnt_swap16 (&head->index_to_loc_format);
+ sfnt_swap16 (&head->glyph_data_format);
+
+ return head;
+}
+
+/* Read the hhea table of a given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the head table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_hhea_table *
+sfnt_read_hhea_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_hhea_table *hhea;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_HHEA);
+
+ if (!directory)
+ return NULL;
+
+ /* Check the length is right. */
+ if (directory->length != sizeof *hhea)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Read the entire table. */
+ hhea = xmalloc (sizeof *hhea);
+ rc = read (fd, hhea, sizeof *hhea);
+
+ if (rc < sizeof *hhea)
+ {
+ xfree (hhea);
+ return NULL;
+ }
+
+ /* Swap the header data. */
+ sfnt_swap32 (&hhea->version);
+
+ if (hhea->version != 0x00010000)
+ {
+ xfree (hhea);
+ return NULL;
+ }
+
+ /* Swap the rest of the data. */
+ sfnt_swap16 (&hhea->ascent);
+ sfnt_swap16 (&hhea->descent);
+ sfnt_swap16 (&hhea->line_gap);
+ sfnt_swap16 (&hhea->advance_width_max);
+ sfnt_swap16 (&hhea->min_left_side_bearing);
+ sfnt_swap16 (&hhea->min_right_side_bearing);
+ sfnt_swap16 (&hhea->x_max_extent);
+ sfnt_swap16 (&hhea->caret_slope_rise);
+ sfnt_swap16 (&hhea->caret_slope_run);
+ sfnt_swap16 (&hhea->reserved1);
+ sfnt_swap16 (&hhea->reserved2);
+ sfnt_swap16 (&hhea->reserved3);
+ sfnt_swap16 (&hhea->reserved4);
+ sfnt_swap16 (&hhea->metric_data_format);
+ sfnt_swap16 (&hhea->num_of_long_hor_metrics);
+
+ return hhea;
+}
+
+/* Read a short loca table from the given font FD. Use the table
+ directory specified in SUBTABLE.
+
+ Return the short table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_loca_table_short *
+sfnt_read_loca_table_short (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_loca_table_short *loca;
+ ssize_t rc;
+ int i;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_LOCA);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out how many glyphs there are based on the length. */
+ loca = xmalloc (sizeof *loca + directory->length);
+ loca->offsets = (uint16_t *) (loca + 1);
+ loca->num_offsets = directory->length / 2;
+
+ /* Read the variable-length table data. */
+ rc = read (fd, loca->offsets, directory->length);
+ if (rc < directory->length)
+ {
+ xfree (loca);
+ return NULL;
+ }
+
+ /* Swap each of the offsets. */
+ for (i = 0; i < loca->num_offsets; ++i)
+ sfnt_swap16 (&loca->offsets[i]);
+
+ /* Return the table. */
+ return loca;
+}
+
+/* Read a long loca table from the given font FD. Use the table
+ directory specified in SUBTABLE.
+
+ Return the long table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_loca_table_long *
+sfnt_read_loca_table_long (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_loca_table_long *loca;
+ ssize_t rc;
+ int i;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_LOCA);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out how many glyphs there are based on the length. */
+ loca = xmalloc (sizeof *loca + directory->length);
+ loca->offsets = (uint32_t *) (loca + 1);
+ loca->num_offsets = directory->length / 4;
+
+ /* Read the variable-length table data. */
+ rc = read (fd, loca->offsets, directory->length);
+ if (rc < directory->length)
+ {
+ xfree (loca);
+ return NULL;
+ }
+
+ /* Swap each of the offsets. */
+ for (i = 0; i < loca->num_offsets; ++i)
+ sfnt_swap32 (&loca->offsets[i]);
+
+ /* Return the table. */
+ return loca;
+}
+
+/* Read the maxp table from the given font FD. Use the table
+ directory specified in SUBTABLE.
+
+ Return the maxp table upon success, else NULL. If the version is
+ 0.5, fields past num_glyphs will not be populated. */
+
+TEST_STATIC struct sfnt_maxp_table *
+sfnt_read_maxp_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_maxp_table *maxp;
+ size_t size;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_MAXP);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* If directory->length is not big enough for version 0.5, punt. */
+ if (directory->length < SFNT_ENDOF (struct sfnt_maxp_table,
+ num_glyphs, uint16_t))
+ return NULL;
+
+ /* Allocate the buffer to hold the data. Then, read
+ directory->length or sizeof *maxp bytes into it, whichever is
+ smaller. */
+
+ maxp = xmalloc (sizeof *maxp);
+ size = MIN (directory->length, sizeof *maxp);
+ rc = read (fd, maxp, size);
+
+ if (rc < size)
+ {
+ xfree (maxp);
+ return NULL;
+ }
+
+ /* Now, swap version and num_glyphs. */
+ sfnt_swap32 (&maxp->version);
+ sfnt_swap16 (&maxp->num_glyphs);
+
+ /* Reject version 1.0 tables that are too small. */
+ if (maxp->version > 0x00005000 && size < sizeof *maxp)
+ {
+ xfree (maxp);
+ return NULL;
+ }
+
+ /* If the table is version 0.5, then this function is done. */
+ if (maxp->version == 0x00005000)
+ return maxp;
+ else if (maxp->version != 0x00010000)
+ {
+ /* Reject invalid versions. */
+ xfree (maxp);
+ return NULL;
+ }
+
+ /* Otherwise, swap the rest of the fields. */
+ sfnt_swap16 (&maxp->max_points);
+ sfnt_swap16 (&maxp->max_contours);
+ sfnt_swap16 (&maxp->max_composite_points);
+ sfnt_swap16 (&maxp->max_composite_contours);
+ sfnt_swap16 (&maxp->max_zones);
+ sfnt_swap16 (&maxp->max_twilight_points);
+ sfnt_swap16 (&maxp->max_storage);
+ sfnt_swap16 (&maxp->max_function_defs);
+ sfnt_swap16 (&maxp->max_instruction_defs);
+ sfnt_swap16 (&maxp->max_stack_elements);
+ sfnt_swap16 (&maxp->max_size_of_instructions);
+ sfnt_swap16 (&maxp->max_component_elements);
+ sfnt_swap16 (&maxp->max_component_depth);
+
+ /* All done. */
+ return maxp;
+}
+
+
+
+/* Glyph outlining generation. */
+
+/* Read a glyf table from the given font FD. Use the table directory
+ specified in SUBTABLE. The glyph data is not swapped.
+
+ Return the glyf table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_glyf_table *
+sfnt_read_glyf_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_glyf_table *glyf;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_GLYF);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold everything. */
+ glyf = xmalloc (sizeof *glyf + directory->length);
+ glyf->size = directory->length;
+ glyf->glyphs = (unsigned char *) (glyf + 1);
+
+ /* Read the glyph data. */
+ rc = read (fd, glyf->glyphs, glyf->size);
+ if (rc < glyf->size)
+ {
+ xfree (glyf);
+ return NULL;
+ }
+
+ /* Return the table. */
+ return glyf;
+}
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Map a glyph table from the given font FD. Use the table directory
+ specified in SUBTABLE. The glyph data is not byte-swapped.
+
+ Value is the glyf table upon success, else NULL.
+ A mapped glyf table must be unmapped using `sfnt_unmap_glyf_table'.
+ The caller must correctly handle bus errors in between glyf->table
+ and glyf->size. */
+
+struct sfnt_glyf_table *
+sfnt_map_glyf_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_glyf_table *glyf;
+ void *glyphs;
+ size_t offset, page, map_offset;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_GLYF);
+
+ if (!directory)
+ return NULL;
+
+ /* Now try to map the glyph data. Make sure offset is a multiple of
+ the page size. */
+
+ page = getpagesize ();
+ offset = directory->offset & ~(page - 1);
+
+ /* Figure out how much larger the mapping should be. */
+ map_offset = directory->offset - offset;
+
+ /* Do the mmap. */
+ glyphs = mmap (NULL, directory->length + map_offset,
+ PROT_READ, MAP_PRIVATE, fd, offset);
+
+ if (glyphs == MAP_FAILED)
+ return NULL;
+
+ /* An observation is that glyphs tend to be accessed in sequential
+ order and immediately after the font's glyph table is loaded. */
+
+#ifdef HAVE_POSIX_MADVISE
+ posix_madvise (glyphs, directory->length,
+ POSIX_MADV_WILLNEED);
+#elif defined HAVE_MADVISE
+ madvise (glyphs, directory->length, MADV_WILLNEED);
+#endif
+
+ /* Allocate the glyf table. */
+ glyf = xmalloc (sizeof *glyf);
+ glyf->size = directory->length;
+ glyf->glyphs = (unsigned char *) glyphs + map_offset;
+ glyf->start = glyphs;
+
+ return glyf;
+}
+
+/* Unmap the mmap'ed glyf table GLYF, then free its associated data.
+ Value is 0 upon success, else 1, in which case GLYF is still freed
+ all the same. */
+
+int
+sfnt_unmap_glyf_table (struct sfnt_glyf_table *glyf)
+{
+ int rc;
+ size_t size;
+
+ /* Calculate the size of the mapping. */
+ size = glyf->size + (glyf->glyphs - glyf->start);
+
+ rc = munmap (glyf->start, size);
+ xfree (glyf);
+
+ return rc != 0;
+}
+
+#endif /* HAVE_MMAP */
+
+/* Read the simple glyph outline from the glyph GLYPH from the
+ specified glyf table at the given offset. Set GLYPH->simple to a
+ non-NULL value upon success, else set it to NULL. */
+
+static void
+sfnt_read_simple_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_glyf_table *glyf,
+ size_t offset)
+{
+ struct sfnt_simple_glyph *simple;
+ ssize_t min_size, min_size_2;
+ int i, number_of_points, repeat_count;
+ unsigned char *instructions_start;
+ unsigned char *flags_start, *flags_end;
+ unsigned char *vec_start;
+ int16_t delta, x, y;
+
+ /* Calculate the minimum size of the glyph data. This is the size
+ of the instruction length field followed by
+ glyph->number_of_contours * sizeof (uint16_t). */
+
+ min_size = (glyph->number_of_contours * sizeof (uint16_t)
+ + sizeof (uint16_t));
+
+ /* Check that the size is big enough. */
+ if (glyf->size < offset + min_size)
+ {
+ glyph->simple = NULL;
+ return;
+ }
+
+ /* Allocate enough to read at least that. */
+ simple = xmalloc (sizeof *simple + min_size);
+ simple->end_pts_of_contours = (uint16_t *) (simple + 1);
+ memcpy (simple->end_pts_of_contours, glyf->glyphs + offset,
+ min_size);
+
+ /* This is not really an index into simple->end_pts_of_contours.
+ Rather, it is reading the first word past it. */
+ simple->instruction_length
+ = simple->end_pts_of_contours[glyph->number_of_contours];
+
+ /* Swap the contour end point indices and the instruction
+ length. */
+
+ for (i = 0; i < glyph->number_of_contours; ++i)
+ sfnt_swap16 (&simple->end_pts_of_contours[i]);
+
+ sfnt_swap16 (&simple->instruction_length);
+
+ /* Based on those values, calculate the maximum size of the
+ following data. This is the instruction length + the last
+ contour point + the last contour point * uint16_t * 2. */
+
+ if (glyph->number_of_contours)
+ number_of_points
+ = simple->end_pts_of_contours[glyph->number_of_contours - 1] + 1;
+ else
+ number_of_points = 0;
+
+ min_size_2 = (simple->instruction_length
+ + number_of_points
+ + (number_of_points
+ * sizeof (uint16_t) * 2));
+
+ /* Set simple->number_of_points. */
+ simple->number_of_points = number_of_points;
+
+ /* Make simple big enough. */
+ simple = xrealloc (simple, sizeof *simple + min_size + min_size_2);
+ simple->end_pts_of_contours = (uint16_t *) (simple + 1);
+
+ /* Set the instruction data pointer and other pointers.
+ simple->instructions comes one word past number_of_contours,
+ because end_pts_of_contours also contains the instruction
+ length. */
+ simple->instructions = (uint8_t *) (simple->end_pts_of_contours
+ + glyph->number_of_contours + 1);
+ simple->flags = simple->instructions + simple->instruction_length;
+
+ /* Read instructions into the glyph. */
+ instructions_start = glyf->glyphs + offset + min_size;
+
+ if (instructions_start >= glyf->glyphs + glyf->size
+ || (instructions_start + simple->instruction_length
+ >= glyf->glyphs + glyf->size))
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ memcpy (simple->instructions, instructions_start,
+ simple->instruction_length);
+
+ /* Start reading flags. */
+ flags_start = (glyf->glyphs + offset
+ + min_size + simple->instruction_length);
+ flags_end = flags_start + number_of_points;
+
+ if (flags_start >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ i = 0;
+
+ while (flags_start < flags_end)
+ {
+ if (i == number_of_points)
+ break;
+
+ if (flags_start >= glyf->glyphs + glyf->size)
+ break;
+
+ simple->flags[i++] = *flags_start;
+
+ if (*flags_start & 010) /* REPEAT_FLAG */
+ {
+ /* The next byte specifies how many times this byte is to be
+ repeated. Check that it is in range. */
+
+ if (flags_start + 1 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ /* Repeat the current flag until
+ glyph->number_of_points. */
+
+ repeat_count = *(flags_start + 1);
+
+ while (i < number_of_points && repeat_count)
+ {
+ simple->flags[i++] = *flags_start;
+ repeat_count--;
+ }
+
+ /* Skip one byte in flags_start. */
+ flags_start++;
+ }
+
+ flags_start++;
+ }
+
+ /* If an insufficient number of flags have been read, then the
+ outline is invalid. */
+
+ if (i != number_of_points)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ /* Now that the flags have been decoded, start decoding the
+ vectors. */
+ simple->x_coordinates = (int16_t *) (simple->flags + number_of_points);
+ vec_start = flags_start;
+ i = 0;
+ x = 0;
+
+ /* flags_start is now repurposed to act as a pointer to the flags
+ for the current vector! */
+ flags_start = simple->flags;
+
+ while (i < number_of_points)
+ {
+ delta = 0;
+
+ if ((*flags_start) & 02) /* X_SHORT_VECTOR */
+ {
+ /* The next byte is a delta to apply to the previous
+ value. Make sure it is in bounds. */
+
+ if (vec_start + 1 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ delta = *vec_start++;
+
+ if (!(*flags_start & 020)) /* SAME_X */
+ delta = -delta;
+ }
+ else if (!(*flags_start & 020)) /* SAME_X */
+ {
+ /* The next word is a delta to apply to the previous value.
+ Make sure it is in bounds. */
+
+ if (vec_start + 2 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ /* Read the unaligned word and swap it. */
+ memcpy (&delta, vec_start, sizeof delta);
+ sfnt_swap16 (&delta);
+ vec_start += 2;
+ }
+
+ /* Apply the delta and set the X value. */
+ x += delta;
+ simple->x_coordinates[i++] = x;
+ flags_start++;
+ }
+
+ /* Decode the Y vector. flags_start is again repurposed to act as a
+ pointer to the flags for the current vector. */
+ flags_start = simple->flags;
+ y = 0;
+ simple->y_coordinates = simple->x_coordinates + i;
+ i = 0;
+
+ while (i < number_of_points)
+ {
+ delta = 0;
+
+ if (*flags_start & 04) /* Y_SHORT_VECTOR */
+ {
+ /* The next byte is a delta to apply to the previous
+ value. Make sure it is in bounds. */
+
+ if (vec_start + 1 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ delta = *vec_start++;
+
+ if (!(*flags_start & 040)) /* SAME_Y */
+ delta = -delta;
+ }
+ else if (!(*flags_start & 040)) /* SAME_Y */
+ {
+ /* The next word is a delta to apply to the previous value.
+ Make sure it is in bounds. */
+
+ if (vec_start + 2 >= glyf->glyphs + glyf->size)
+ {
+ glyph->simple = NULL;
+ xfree (simple);
+ return;
+ }
+
+ /* Read the unaligned word and swap it. */
+ memcpy (&delta, vec_start, sizeof delta);
+ sfnt_swap16 (&delta);
+ vec_start += 2;
+ }
+
+ /* Apply the delta and set the X value. */
+ y += delta;
+ simple->y_coordinates[i++] = y;
+ flags_start++;
+ }
+
+ /* All done. */
+ simple->y_coordinates_end = simple->y_coordinates + i;
+ glyph->simple = simple;
+ return;
+}
+
+/* Read the compound glyph outline from the glyph GLYPH from the
+ specified glyf table at the given offset. Set GLYPH->compound to a
+ non-NULL value upon success, else set it to NULL. */
+
+static void
+sfnt_read_compound_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_glyf_table *glyf,
+ size_t offset)
+{
+ uint16_t flags, instruction_length, words[2], words4[4];
+ size_t required_bytes, num_components, i;
+ unsigned char *data, *instruction_base;
+
+ /* Assume failure for now. Figure out how many bytes have to be
+ allocated by reading the compound data. */
+ glyph->compound = NULL;
+ required_bytes = 0;
+ num_components = 0;
+ data = glyf->glyphs + offset;
+
+ /* Offset could be unaligned. */
+ do
+ {
+ if (data + 2 > glyf->glyphs + glyf->size)
+ return;
+
+ memcpy (&flags, data, sizeof flags);
+ sfnt_swap16 (&flags);
+ data += sizeof flags;
+
+ /* Require at least one structure to hold this data. */
+ required_bytes += sizeof (struct sfnt_compound_glyph_component);
+ num_components++;
+
+ /* Skip past unused data. */
+ data += 2;
+
+ if (flags & 01) /* ARG_1_AND_2_ARE_WORDS */
+ data += sizeof (int16_t) * 2;
+ else
+ data += sizeof (int8_t) * 2;
+
+ if (flags & 010) /* WE_HAVE_A_SCALE */
+ data += sizeof (uint16_t);
+ else if (flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
+ data += sizeof (uint16_t) * 2;
+ else if (flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+ data += sizeof (uint16_t) * 4;
+ }
+ while (flags & 040); /* MORE_COMPONENTS */
+
+ if (flags & 0400) /* WE_HAVE_INSTRUCTIONS */
+ {
+ /* Figure out the instruction length. */
+ if (data + 2 > glyf->glyphs + glyf->size)
+ return;
+
+ /* Now see how much is required to hold the instruction
+ data. */
+ memcpy (&instruction_length, data,
+ sizeof instruction_length);
+ sfnt_swap16 (&instruction_length);
+ required_bytes += instruction_length;
+ data += sizeof data + instruction_length;
+ }
+
+ /* Now allocate the buffer to hold all the glyph data. */
+ glyph->compound = xmalloc (sizeof *glyph->compound
+ + required_bytes);
+ glyph->compound->components
+ = (struct sfnt_compound_glyph_component *) (glyph->compound + 1);
+ glyph->compound->num_components = num_components;
+
+ /* Figure out where instruction data starts. It comes after
+ glyph->compound->components ends. */
+ instruction_base
+ = (unsigned char *) (glyph->compound->components
+ + glyph->compound->num_components);
+
+ /* Start reading. */
+ i = 0;
+ data = glyf->glyphs + offset;
+ do
+ {
+ if (data + 4 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ memcpy (&flags, data, sizeof flags);
+ sfnt_swap16 (&flags);
+ data += sizeof flags;
+ glyph->compound->components[i].flags = flags;
+
+ memcpy (&glyph->compound->components[i].glyph_index,
+ data, sizeof glyph->compound->components[i].glyph_index);
+ sfnt_swap16 (&glyph->compound->components[i].glyph_index);
+ data += sizeof glyph->compound->components[i].glyph_index;
+
+ if (flags & 01) /* ARG_1_AND_2_ARE_WORDS. */
+ {
+ if (data + 4 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read two words into arg1 and arg2. */
+ memcpy (words, data, sizeof words);
+ sfnt_swap16 (&words[0]);
+ sfnt_swap16 (&words[1]);
+
+ glyph->compound->components[i].argument1.c = words[0];
+ glyph->compound->components[i].argument2.c = words[1];
+ data += sizeof words;
+ }
+ else
+ {
+ if (data + 2 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read two bytes into arg1 and arg2. */
+ glyph->compound->components[i].argument1.a = data[0];
+ glyph->compound->components[i].argument2.a = data[1];
+ data += 2;
+ }
+
+ if (flags & 010) /* WE_HAVE_A_SCALE */
+ {
+ if (data + 2 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read one word into scale. */
+ memcpy (&glyph->compound->components[i].u.scale, data,
+ sizeof glyph->compound->components[i].u.scale);
+ sfnt_swap16 (&glyph->compound->components[i].u.scale);
+ data += sizeof glyph->compound->components[i].u.scale;
+ }
+ else if (flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE. */
+ {
+ if (data + 4 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read two words into xscale and yscale. */
+ memcpy (words, data, sizeof words);
+ sfnt_swap16 (&words[0]);
+ sfnt_swap16 (&words[1]);
+
+ glyph->compound->components[i].u.a.xscale = words[0];
+ glyph->compound->components[i].u.a.yscale = words[1];
+ data += sizeof words;
+ }
+ else if (flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+ {
+ if (data + 8 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Read 4 words into the transformation matrix. */
+ memcpy (words4, data, sizeof words4);
+ sfnt_swap16 (&words4[0]);
+ sfnt_swap16 (&words4[1]);
+ sfnt_swap16 (&words4[2]);
+ sfnt_swap16 (&words4[3]);
+
+ glyph->compound->components[i].u.b.xscale = words4[0];
+ glyph->compound->components[i].u.b.scale01 = words4[1];
+ glyph->compound->components[i].u.b.scale10 = words4[2];
+ glyph->compound->components[i].u.b.yscale = words4[3];
+ data += sizeof words4;
+ }
+
+ /* Record the component flags. */
+ glyph->compound->components[i].flags = flags;
+
+ i++;
+ }
+ while (flags & 040); /* MORE_COMPONENTS */
+
+ if (flags & 0400) /* WE_HAVE_INSTR */
+ {
+ /* Figure out the instruction length. */
+ if (data + 2 > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ /* Now see how much is required to hold the instruction
+ data. */
+ memcpy (&glyph->compound->instruction_length,
+ data,
+ sizeof glyph->compound->instruction_length);
+ sfnt_swap16 (&glyph->compound->instruction_length);
+ data += 2;
+
+ /* Read the instructions. */
+ glyph->compound->instructions = instruction_base;
+
+ if (data + glyph->compound->instruction_length
+ > glyf->glyphs + glyf->size)
+ {
+ xfree (glyph->compound);
+ glyph->compound = NULL;
+ return;
+ }
+
+ memcpy (instruction_base, data,
+ glyph->compound->instruction_length);
+ }
+ else
+ {
+ glyph->compound->instructions = NULL;
+ glyph->compound->instruction_length = 0;
+ }
+
+ /* Data read successfully. */
+ return;
+}
+
+/* Read the description of the glyph GLYPH_CODE from the specified
+ glyf table, using the offsets of LOCA_SHORT or LOCA_LONG, depending
+ on which is non-NULL. */
+
+TEST_STATIC struct sfnt_glyph *
+sfnt_read_glyph (sfnt_glyph glyph_code,
+ struct sfnt_glyf_table *glyf,
+ struct sfnt_loca_table_short *loca_short,
+ struct sfnt_loca_table_long *loca_long)
+{
+ struct sfnt_glyph glyph, *memory;
+ size_t offset, next_offset;
+
+ /* Check the glyph code is within bounds. */
+ if (glyph_code > 65535)
+ return NULL;
+
+ if (loca_short)
+ {
+ /* Check that the glyph is within bounds. glyph_code + 1 is the
+ entry in the table which defines the length of the glyph. */
+ if (glyph_code + 1 >= loca_short->num_offsets)
+ return NULL;
+
+ offset = loca_short->offsets[glyph_code] * 2;
+ next_offset = loca_short->offsets[glyph_code + 1] * 2;
+ }
+ else if (loca_long)
+ {
+ if (glyph_code + 1 >= loca_long->num_offsets)
+ return NULL;
+
+ offset = loca_long->offsets[glyph_code];
+ next_offset = loca_long->offsets[glyph_code + 1];
+ }
+ else
+ abort ();
+
+ /* If offset - next_offset is 0, then the glyph is empty. Its
+ horizontal advance may still be provided by the hmtx table. */
+
+ if (offset == next_offset)
+ {
+ glyph.number_of_contours = 0;
+ glyph.xmin = 0;
+ glyph.ymin = 0;
+ glyph.xmax = 0;
+ glyph.ymax = 0;
+ glyph.simple = xmalloc (sizeof *glyph.simple);
+ glyph.compound = NULL;
+ memset (glyph.simple, 0, sizeof *glyph.simple);
+ memory = xmalloc (sizeof *memory);
+ *memory = glyph;
+ return memory;
+ }
+
+ /* Verify that GLYF is big enough to hold a glyph at OFFSET. */
+ if (glyf->size < offset + SFNT_ENDOF (struct sfnt_glyph,
+ ymax, sfnt_fword))
+ return NULL;
+
+ /* Copy over the glyph data. */
+ memcpy (&glyph, glyf->glyphs + offset,
+ SFNT_ENDOF (struct sfnt_glyph,
+ ymax, sfnt_fword));
+
+ /* Swap the glyph data. */
+ sfnt_swap16 (&glyph.number_of_contours);
+ sfnt_swap16 (&glyph.xmin);
+ sfnt_swap16 (&glyph.ymin);
+ sfnt_swap16 (&glyph.xmax);
+ sfnt_swap16 (&glyph.ymax);
+
+ /* This is set later on after `sfnt_vary_X_glyph'. */
+ glyph.advance_distortion = 0;
+ glyph.origin_distortion = 0;
+
+ /* Figure out what needs to be read based on
+ glyph.number_of_contours. */
+ if (glyph.number_of_contours >= 0)
+ {
+ /* Read the simple glyph. */
+
+ glyph.compound = NULL;
+ sfnt_read_simple_glyph (&glyph, glyf,
+ offset + SFNT_ENDOF (struct sfnt_glyph,
+ ymax, sfnt_fword));
+
+ if (glyph.simple)
+ {
+ memory = xmalloc (sizeof glyph);
+ *memory = glyph;
+
+ return memory;
+ }
+ }
+ else
+ {
+ /* Read the compound glyph. */
+
+ glyph.simple = NULL;
+ sfnt_read_compound_glyph (&glyph, glyf,
+ offset + SFNT_ENDOF (struct sfnt_glyph,
+ ymax, sfnt_fword));
+
+ if (glyph.compound)
+ {
+ memory = xmalloc (sizeof glyph);
+ *memory = glyph;
+
+ return memory;
+ }
+ }
+
+ return NULL;
+}
+
+/* Free a glyph returned from sfnt_read_glyph. GLYPH may be NULL. */
+
+TEST_STATIC void
+sfnt_free_glyph (struct sfnt_glyph *glyph)
+{
+ if (!glyph)
+ return;
+
+ xfree (glyph->simple);
+ xfree (glyph->compound);
+ xfree (glyph);
+}
+
+
+
+/* Glyph outline decomposition. */
+
+/* Apply the transform in the compound glyph component COMPONENT to
+ the array of points of length NUM_COORDINATES given as X and Y.
+
+ Also, apply the fixed point offsets X_OFF and Y_OFF to each X and Y
+ coordinate.
+
+ See sfnt_decompose_compound_glyph for an explanation of why offsets
+ might be applied here, and not while reading the subglyph
+ itself. */
+
+static void
+sfnt_transform_coordinates (struct sfnt_compound_glyph_component *component,
+ sfnt_fixed *restrict x, sfnt_fixed *restrict y,
+ size_t num_coordinates,
+ sfnt_fixed x_off, sfnt_fixed y_off)
+{
+ double m1, m2, m3;
+ double m4, m5, m6;
+ size_t i;
+
+ if (component->flags & 010) /* WE_HAVE_A_SCALE */
+ {
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] *= component->u.scale / 16384.0;
+ y[i] *= component->u.scale / 16384.0;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+ else if (component->flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
+ {
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] *= component->u.a.xscale / 16384.0;
+ y[i] *= component->u.a.yscale / 16384.0;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+ else if (component->flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+ {
+ /* Apply the specified affine transformation.
+ A transform looks like:
+
+ M1 M2 M3 X
+ M4 M5 M6 * Y
+
+ =
+
+ M1*X + M2*Y + M3*1 = X1
+ M4*X + M5*Y + M6*1 = Y1
+
+ (In most transforms, there is another row at the bottom for
+ mathematical reasons. Since Z1 is always 1.0, the row is
+ simply implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 =
+ 1.0. See the definition of matrix3x3 in image.c for some
+ more explanations about this.) */
+ m1 = component->u.b.xscale / 16384.0;
+ m2 = component->u.b.scale01 / 16384.0;
+ m3 = 0;
+ m4 = component->u.b.scale10 / 16384.0;
+ m5 = component->u.b.yscale / 16384.0;
+ m6 = 0;
+
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] = m1 * x[i] + m2 * y[i] + m3 * 1;
+ y[i] = m4 * x[i] + m5 * y[i] + m6 * 1;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+}
+
+struct sfnt_compound_glyph_context
+{
+ /* Arrays of points. The underlying type is actually sfnt_f26dot6
+ when instructing a compound glyph. */
+ sfnt_fixed *x_coordinates, *y_coordinates;
+
+ /* Array of flags for the points. */
+ unsigned char *flags;
+
+ /* Number of points in that array, and the size of that array. */
+ size_t num_points, points_size;
+
+ /* Array of contour end points. */
+ size_t *contour_end_points;
+
+ /* Number of elements in and the size of that array. */
+ size_t num_end_points, end_points_size;
+};
+
+/* Extend the arrays inside the compound glyph decomposition context
+ CONTEXT. NUMBER_OF_CONTOURS is the number of contours to add.
+ NUMBER_OF_POINTS is the number of points to add.
+
+ Return pointers to the beginning of the extension in *X_BASE,
+ *Y_BASE, *FLAGS_BASE and *CONTOUR_BASE. Value zero upon success,
+ and something else on failure. */
+
+static int
+sfnt_expand_compound_glyph_context (struct sfnt_compound_glyph_context *context,
+ size_t number_of_contours,
+ size_t number_of_points,
+ sfnt_fixed **x_base, sfnt_fixed **y_base,
+ unsigned char **flags_base,
+ size_t **contour_base)
+{
+ size_t size_bytes;
+
+ /* Add each field while checking for overflow. */
+ if (INT_ADD_WRAPV (number_of_contours, context->num_end_points,
+ &context->num_end_points))
+ return 1;
+
+ if (INT_ADD_WRAPV (number_of_points, context->num_points,
+ &context->num_points))
+ return 1;
+
+ /* Reallocate each array to the new size if necessary. */
+ if (context->points_size < context->num_points)
+ {
+ if (INT_MULTIPLY_WRAPV (context->num_points, 2,
+ &context->points_size))
+ context->points_size = context->num_points;
+
+ if (INT_MULTIPLY_WRAPV (context->points_size,
+ sizeof *context->x_coordinates,
+ &size_bytes))
+ return 1;
+
+ context->x_coordinates = xrealloc (context->x_coordinates,
+ size_bytes);
+ context->y_coordinates = xrealloc (context->y_coordinates,
+ size_bytes);
+ context->flags = xrealloc (context->flags,
+ context->points_size);
+ }
+
+ /* Set x_base and y_base. */
+ *x_base = (context->x_coordinates
+ + context->num_points
+ - number_of_points);
+ *y_base = (context->y_coordinates
+ + context->num_points
+ - number_of_points);
+ *flags_base = (context->flags
+ + context->num_points
+ - number_of_points);
+
+ if (context->end_points_size < context->num_end_points)
+ {
+ if (INT_MULTIPLY_WRAPV (context->num_end_points, 2,
+ &context->end_points_size))
+ context->end_points_size = context->num_end_points;
+
+ if (INT_MULTIPLY_WRAPV (context->end_points_size,
+ sizeof *context->contour_end_points,
+ &size_bytes))
+ return 1;
+
+ context->contour_end_points
+ = xrealloc (context->contour_end_points,
+ size_bytes);
+ }
+
+ /* Set contour_base. */
+ *contour_base = (context->contour_end_points
+ + context->num_end_points
+ - number_of_contours);
+ return 0;
+}
+
+/* Round the 16.16 fixed point number NUMBER to the nearest integral
+ value. */
+
+static int32_t
+sfnt_round_fixed (int32_t number)
+{
+ /* Add 0.5... */
+ number += (1 << 15);
+
+ /* Remove the fractional. */
+ return number & ~0xffff;
+}
+
+/* Decompose GLYPH, a compound glyph, into an array of points and
+ contours.
+
+ CONTEXT should be zeroed and put on the stack. OFF_X and OFF_Y
+ should be zero, as should RECURSION_COUNT. GET_GLYPH and
+ FREE_GLYPH, along with DCONTEXT, mean the same as in
+ sfnt_decompose_glyph. */
+
+static int
+sfnt_decompose_compound_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_compound_glyph_context *context,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ sfnt_fixed off_x, sfnt_fixed off_y,
+ int recursion_count,
+ void *dcontext)
+{
+ struct sfnt_glyph *subglyph;
+ int i, j, rc;
+ bool need_free;
+ struct sfnt_compound_glyph_component *component;
+ sfnt_fixed x, y, xtemp, ytemp;
+ size_t point, point2, index;
+ uint16_t last_point, number_of_contours;
+ sfnt_fixed *x_base, *y_base;
+ size_t *contour_base;
+ unsigned char *flags_base;
+ size_t base_index, contour_start;
+ bool defer_offsets;
+
+ /* Set up the base index. This is the index from where on point
+ renumbering starts.
+
+ In other words, point 0 in this glyph will be 0 + base_index,
+ point 1 will be 1 + base_index, and so on. */
+ base_index = context->num_points;
+
+ /* Prevent infinite loops. */
+ if (recursion_count > 12)
+ return 1;
+
+ /* Don't defer offsets. */
+ defer_offsets = false;
+
+ for (j = 0; j < glyph->compound->num_components; ++j)
+ {
+ /* Look up the associated subglyph. */
+ component = &glyph->compound->components[j];
+ subglyph = get_glyph (component->glyph_index,
+ dcontext, &need_free);
+
+ if (!subglyph)
+ return -1;
+
+ /* Record the size of the point array before expansion. This
+ will be the base to apply to all points coming from this
+ subglyph. */
+ contour_start = context->num_points;
+
+ /* Compute the offset for the component. */
+ if (component->flags & 02) /* ARGS_ARE_XY_VALUES */
+ {
+ /* Component offsets are X/Y values as opposed to points
+ GLYPH. */
+
+ if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+ {
+ /* X and Y are signed bytes. */
+ x = component->argument1.b * 65536;
+ y = component->argument2.b * 65536;
+ }
+ else
+ {
+ /* X and Y are signed words. */
+ x = component->argument1.d * 65536;
+ y = component->argument2.d * 65536;
+ }
+
+ /* If there is some kind of scale and component offsets are
+ scaled, then apply the transform to the offset. */
+ if (component->flags & 04000) /* SCALED_COMPONENT_OFFSET */
+ sfnt_transform_coordinates (component, &x, &y, 1,
+ 0, 0);
+
+ if (component->flags & 04) /* ROUND_XY_TO_GRID */
+ {
+ x = sfnt_round_fixed (x);
+ y = sfnt_round_fixed (y);
+ }
+ }
+ else
+ {
+ /* The offset is determined by matching a point location in
+ a preceeding component with a point location in the
+ current component. The index of the point in the
+ previous component can be determined by adding
+ component->argument1.a or component->argument1.c to
+ point. argument2 contains the index of the point in the
+ current component. */
+
+ if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+ {
+ point = base_index + component->argument1.a;
+ point2 = component->argument2.a;
+ }
+ else
+ {
+ point = base_index + component->argument1.c;
+ point2 = component->argument2.c;
+ }
+
+ /* Now, check that the anchor point specified lies inside
+ the glyph. */
+
+ if (point >= contour_start)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ if (!subglyph->compound)
+ {
+ if (point2 >= subglyph->simple->number_of_points)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ /* Get the points and use them to compute the offsets. */
+ xtemp = context->x_coordinates[point];
+ ytemp = context->y_coordinates[point];
+ x = (xtemp - subglyph->simple->x_coordinates[point2] * 65536);
+ y = (ytemp - subglyph->simple->y_coordinates[point2] * 65536);
+ }
+ else
+ {
+ /* First, set offsets to 0, because it is not yet
+ possible to determine the position of the anchor
+ point in the child. */
+ x = 0;
+ y = 0;
+
+ /* Set a flag which indicates that offsets must be
+ resolved from the child glyph after it is loaded, but
+ before it is incorporated into the parent glyph. */
+ defer_offsets = true;
+ }
+ }
+
+ if (subglyph->simple)
+ {
+ /* Simple subglyph. Copy over the points and contours, and
+ transform them. */
+ if (subglyph->number_of_contours)
+ {
+ index = subglyph->number_of_contours - 1;
+ last_point
+ = subglyph->simple->end_pts_of_contours[index];
+ number_of_contours = subglyph->number_of_contours;
+
+
+ /* Grow various arrays. */
+ rc = sfnt_expand_compound_glyph_context (context,
+ /* Number of
+ new contours
+ required. */
+ number_of_contours,
+ /* Number of new
+ points
+ required. */
+ last_point + 1,
+ &x_base,
+ &y_base,
+ &flags_base,
+ &contour_base);
+ if (rc)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ for (i = 0; i <= last_point; ++i)
+ {
+ x_base[i] = ((subglyph->simple->x_coordinates[i] * 65536)
+ + off_x + x);
+ y_base[i] = ((subglyph->simple->y_coordinates[i] * 65536)
+ + off_y + y);
+ flags_base[i] = subglyph->simple->flags[i];
+ }
+
+ /* Apply the transform to the points. */
+ sfnt_transform_coordinates (component, x_base, y_base,
+ last_point + 1, 0, 0);
+
+ /* Copy over the contours. */
+ for (i = 0; i < number_of_contours; ++i)
+ contour_base[i] = (contour_start
+ + subglyph->simple->end_pts_of_contours[i]);
+ }
+ }
+ else
+ {
+ /* Compound subglyph. Decompose the glyph recursively, and
+ then apply the transform. */
+ rc = sfnt_decompose_compound_glyph (subglyph,
+ context,
+ get_glyph,
+ free_glyph,
+ off_x + x,
+ off_y + y,
+ recursion_count + 1,
+ dcontext);
+
+ if (rc)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ /* When an anchor point is being used to translate the
+ glyph, and the subglyph in question is actually a
+ compound glyph, it is impossible to know which offset to
+ use until the compound subglyph has actually been
+ loaded.
+
+ As a result, the offset is calculated here, using the
+ points in the loaded child compound glyph. But first, X
+ and Y must be reset to 0, as otherwise the translation
+ might be applied twice if defer_offsets is not set. */
+
+ x = 0;
+ y = 0;
+
+ if (defer_offsets)
+ {
+ /* Renumber the non renumbered point2 to point into the
+ decomposed component. */
+ point2 += contour_start;
+
+ /* Next, check that the non-renumbered point being
+ anchored lies inside the glyph data that was
+ decomposed. */
+
+ if (point2 >= context->num_points)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return 1;
+ }
+
+ /* Get the points and use them to compute the
+ offsets. */
+
+ xtemp = context->x_coordinates[point];
+ ytemp = context->y_coordinates[point];
+ x = (xtemp - context->x_coordinates[point2]);
+ y = (ytemp - context->y_coordinates[point2]);
+ }
+
+ sfnt_transform_coordinates (component,
+ context->x_coordinates + contour_start,
+ context->y_coordinates + contour_start,
+ contour_start - context->num_points,
+ x, y);
+ }
+
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+ }
+
+ /* Decomposition is complete. CONTEXT now contains the adjusted
+ outlines of the entire compound glyph. */
+ return 0;
+}
+
+/* Linear-interpolate to a point halfway between the points specified
+ by CONTROL1 and CONTROL2. Put the result in RESULT. */
+
+static void
+sfnt_lerp_half (struct sfnt_point *control1, struct sfnt_point *control2,
+ struct sfnt_point *result)
+{
+ result->x = control1->x + ((control2->x - control1->x) >> 1);
+ result->y = control1->y + ((control2->y - control1->y) >> 1);
+}
+
+/* Decompose contour data inside X, Y and FLAGS, between the indices
+ HERE and LAST. Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
+ with DCONTEXT as an argument. Apply SCALE to each point; SCALE
+ should be the factor necessary to turn points into 16.16 fixed
+ point.
+
+ Value is 1 upon failure, else 0. */
+
+static int
+sfnt_decompose_glyph_1 (size_t here, size_t last,
+ sfnt_move_to_proc move_to,
+ sfnt_line_to_proc line_to,
+ sfnt_curve_to_proc curve_to,
+ void *dcontext,
+ sfnt_fword *x,
+ sfnt_fword *y, unsigned char *flags,
+ int scale)
+{
+ struct sfnt_point control1, control2, start, mid;
+ size_t i;
+
+ /* The contour is empty. */
+
+ if (here == last)
+ return 1;
+
+ /* Move the pen to the start of the contour. Apparently some fonts
+ have off the curve points as the start of a contour, so when that
+ happens lerp between the first and last points. */
+
+ if (flags[here] & 01) /* On Curve */
+ {
+ control1.x = x[here] * scale;
+ control1.y = y[here] * scale;
+ start = control1;
+ }
+ else if (flags[last] & 01)
+ {
+ /* Start at the last point if it is on the curve. Here, the
+ start really becomes the middle of a spline. */
+ control1.x = x[last] * scale;
+ control1.y = y[last] * scale;
+ start = control1;
+
+ /* Curve back one point early. */
+ last -= 1;
+ here -= 1;
+ }
+ else
+ {
+ /* Lerp between the start and the end. */
+ control1.x = x[here] * scale;
+ control1.y = y[here] * scale;
+ control2.x = x[last] * scale;
+ control2.y = y[last] * scale;
+ sfnt_lerp_half (&control1, &control2, &start);
+
+ /* In either of these cases, start iterating from just here as
+ opposed to here + 1, since logically the contour now starts
+ from the last curve. */
+ here -= 1;
+ }
+
+ /* Move to the start. */
+ move_to (start, dcontext);
+
+ /* Now handle each point between here + 1 and last. */
+
+ i = here;
+ while (++i <= last)
+ {
+ /* If the point is on the curve, then draw a line here from the
+ last control point. */
+
+ if (flags[i] & 01)
+ {
+ control1.x = x[i] * scale;
+ control1.y = y[i] * scale;
+
+ line_to (control1, dcontext);
+
+ /* Move to the next point. */
+ continue;
+ }
+
+ /* Off the curve points are more interesting. They are handled
+ one by one, with points in between being interpolated, until
+ either the last point is reached or an on-curve point is
+ processed. First, load the initial control points. */
+
+ control1.x = x[i] * scale;
+ control1.y = y[i] * scale;
+
+ while (++i <= last)
+ {
+ /* Load this point. */
+ control2.x = x[i] * scale;
+ control2.y = y[i] * scale;
+
+ /* If this point is on the curve, curve directly to this
+ point. */
+
+ if (flags[i] & 01)
+ {
+ curve_to (control1, control2, dcontext);
+ goto continue_loop;
+ }
+
+ /* Calculate the point between here and the previous
+ point. */
+ sfnt_lerp_half (&control1, &control2, &mid);
+
+ /* Curve over there. */
+ curve_to (control1, mid, dcontext);
+
+ /* Reload the control point. */
+ control1 = control2;
+ }
+
+ /* Close the contour by curving back to start. */
+ curve_to (control1, start, dcontext);
+
+ /* Don't close the contour twice. */
+ goto exit;
+
+ continue_loop:
+ continue;
+ }
+
+ /* Close the contour with a line back to start. */
+ line_to (start, dcontext);
+
+ exit:
+ return 0;
+}
+
+/* Decompose contour data inside X, Y and FLAGS, between the indices
+ HERE and LAST. Call LINE_TO, CURVE_TO and MOVE_TO as appropriate,
+ with DCONTEXT as an argument. Apply SCALE to each point; SCALE
+ should be the factor necessary to turn points into 16.16 fixed
+ point.
+
+ This is the version of sfnt_decompose_glyph_1 which takes
+ sfnt_fixed (or sfnt_f26dot6) as opposed to sfnt_fword.
+
+ Value is 1 upon failure, else 0. */
+
+static int
+sfnt_decompose_glyph_2 (size_t here, size_t last,
+ sfnt_move_to_proc move_to,
+ sfnt_line_to_proc line_to,
+ sfnt_curve_to_proc curve_to,
+ void *dcontext,
+ sfnt_fixed *x,
+ sfnt_fixed *y, unsigned char *flags,
+ int scale)
+{
+ struct sfnt_point control1, control2, start, mid;
+ size_t i;
+
+ /* The contour is empty. */
+
+ if (here == last)
+ return 1;
+
+ /* Move the pen to the start of the contour. Apparently some fonts
+ have off the curve points as the start of a contour, so when that
+ happens lerp between the first and last points. */
+
+ if (flags[here] & 01) /* On Curve */
+ {
+ control1.x = x[here] * scale;
+ control1.y = y[here] * scale;
+ start = control1;
+ }
+ else if (flags[last] & 01)
+ {
+ /* Start at the last point if it is on the curve. Here, the
+ start really becomes the middle of a spline. */
+ control1.x = x[last] * scale;
+ control1.y = y[last] * scale;
+ start = control1;
+
+ /* Curve back one point early. */
+ last -= 1;
+ here -= 1;
+ }
+ else
+ {
+ /* Lerp between the start and the end. */
+ control1.x = x[here] * scale;
+ control1.y = y[here] * scale;
+ control2.x = x[last] * scale;
+ control2.y = y[last] * scale;
+ sfnt_lerp_half (&control1, &control2, &start);
+
+ /* In either of these cases, start iterating from just here as
+ opposed to here + 1, since logically the contour now starts
+ from the last curve. */
+ here -= 1;
+ }
+
+ /* Move to the start. */
+ move_to (start, dcontext);
+
+ /* Now handle each point between here + 1 and last. */
+
+ i = here;
+ while (++i <= last)
+ {
+ /* If the point is on the curve, then draw a line here from the
+ last control point. */
+
+ if (flags[i] & 01)
+ {
+ control1.x = x[i] * scale;
+ control1.y = y[i] * scale;
+
+ line_to (control1, dcontext);
+
+ /* Move to the next point. */
+ continue;
+ }
+
+ /* Off the curve points are more interesting. They are handled
+ one by one, with points in between being interpolated, until
+ either the last point is reached or an on-curve point is
+ processed. First, load the initial control points. */
+
+ control1.x = x[i] * scale;
+ control1.y = y[i] * scale;
+
+ while (++i <= last)
+ {
+ /* Load this point. */
+ control2.x = x[i] * scale;
+ control2.y = y[i] * scale;
+
+ /* If this point is on the curve, curve directly to this
+ point. */
+
+ if (flags[i] & 01)
+ {
+ curve_to (control1, control2, dcontext);
+ goto continue_loop;
+ }
+
+ /* Calculate the point between here and the previous
+ point. */
+ sfnt_lerp_half (&control1, &control2, &mid);
+
+ /* Curve over there. */
+ curve_to (control1, mid, dcontext);
+
+ /* Reload the control point. */
+ control1 = control2;
+ }
+
+ /* Close the contour by curving back to start. */
+ curve_to (control1, start, dcontext);
+
+ /* Don't close the contour twice. */
+ goto exit;
+
+ continue_loop:
+ continue;
+ }
+
+ /* Close the contour with a line back to start. */
+ line_to (start, dcontext);
+
+ exit:
+ return 0;
+}
+
+/* Decompose GLYPH into its individual components. Call MOVE_TO to
+ move to a specific location. For each line encountered, call
+ LINE_TO to draw a line to that location. For each spline
+ encountered, call CURVE_TO to draw the curves comprising the
+ spline.
+
+ If GLYPH is compound, use GET_GLYPH to obtain subglyphs. PROC must
+ return whether or not FREE_GLYPH will be called with the glyph
+ after sfnt_decompose_glyph is done with it.
+
+ All functions will be called with DCONTEXT as an argument.
+
+ The winding rule used to fill the resulting lines is described in
+ chapter 2 of the TrueType reference manual, under the heading
+ "distinguishing the inside from the outside of a glyph."
+
+ Value is 0 upon success, or some non-zero value upon failure, which
+ can happen if the glyph is invalid. */
+
+static int
+sfnt_decompose_glyph (struct sfnt_glyph *glyph,
+ sfnt_move_to_proc move_to,
+ sfnt_line_to_proc line_to,
+ sfnt_curve_to_proc curve_to,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ void *dcontext)
+{
+ size_t here, last, n;
+ struct sfnt_compound_glyph_context context;
+
+ if (glyph->simple)
+ {
+ if (!glyph->number_of_contours)
+ /* No contours. Nothing needs to be decomposed. */
+ return 0;
+
+ here = 0;
+
+ for (n = 0; n < glyph->number_of_contours; ++n)
+ {
+ /* here is the first index into the glyph's point arrays
+ belonging to the contour in question. last is the index
+ of the last point in the contour. */
+ last = glyph->simple->end_pts_of_contours[n];
+
+ /* Make sure here and last make sense. */
+
+ if (here > last || last >= glyph->simple->number_of_points)
+ return 1;
+
+ /* Now perform the decomposition. */
+ if (sfnt_decompose_glyph_1 (here, last, move_to,
+ line_to, curve_to,
+ dcontext,
+ glyph->simple->x_coordinates,
+ glyph->simple->y_coordinates,
+ glyph->simple->flags,
+ 65536))
+ return 1;
+
+ /* Move forward to the start of the next contour. */
+ here = last + 1;
+ }
+
+ return 0;
+ }
+
+ /* Decompose the specified compound glyph. */
+ memset (&context, 0, sizeof context);
+
+ if (sfnt_decompose_compound_glyph (glyph, &context,
+ get_glyph, free_glyph,
+ 0, 0, 0, dcontext))
+ {
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+
+ return 1;
+ }
+
+ /* Now, generate the outlines. */
+
+ if (!context.num_end_points)
+ /* No contours. */
+ goto early;
+
+ here = 0;
+
+ for (n = 0; n < context.num_end_points; ++n)
+ {
+ /* here is the first index into the glyph's point arrays
+ belonging to the contour in question. last is the index
+ of the last point in the contour. */
+ last = context.contour_end_points[n];
+
+ /* Make sure here and last make sense. */
+
+ if (here > last || last >= context.num_points)
+ goto fail;
+
+ /* Now perform the decomposition. */
+ if (sfnt_decompose_glyph_2 (here, last, move_to,
+ line_to, curve_to,
+ dcontext,
+ context.x_coordinates,
+ context.y_coordinates,
+ context.flags, 1))
+ goto fail;
+
+ /* Move forward. */
+ here = last + 1;
+ }
+
+ early:
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+ return 0;
+
+ fail:
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+ return 1;
+}
+
+struct sfnt_build_glyph_outline_context
+{
+ /* The outline being built. */
+ struct sfnt_glyph_outline *outline;
+
+ /* Factor to multiply positions by to get the pixel width. */
+ sfnt_fixed factor;
+
+ /* The position of the pen in 16.16 fixed point format. */
+ sfnt_fixed x, y;
+};
+
+/* Global state for sfnt_build_glyph_outline and related
+ functions. */
+static struct sfnt_build_glyph_outline_context build_outline_context;
+
+/* Append the given three words FLAGS, X, and Y to the outline
+ currently being built. Value is the new pointer to outline
+ memory. */
+
+static struct sfnt_glyph_outline *
+sfnt_build_append (int flags, sfnt_fixed x, sfnt_fixed y)
+{
+ struct sfnt_glyph_outline *outline;
+
+ if (x == build_outline_context.x
+ && y == build_outline_context.y)
+ /* Ignore redundant motion. */
+ return build_outline_context.outline;
+
+ outline = build_outline_context.outline;
+ outline->outline_used++;
+
+ /* See if the outline has to be extended. Checking for overflow
+ should not be necessary. */
+
+ if (outline->outline_used > outline->outline_size)
+ {
+ outline->outline_size = outline->outline_used * 2;
+
+ /* Extend the outline to some size past the new size. */
+ outline = xrealloc (outline, (sizeof *outline
+ + (outline->outline_size
+ * sizeof *outline->outline)));
+ outline->outline
+ = (struct sfnt_glyph_outline_command *) (outline + 1);
+ }
+
+ /* Write the outline data. */
+ outline->outline[outline->outline_used - 1].flags = flags;
+ outline->outline[outline->outline_used - 1].x = x;
+ outline->outline[outline->outline_used - 1].y = y;
+
+ /* Extend outline bounding box. */
+
+ if (outline->outline_used == 1)
+ {
+ /* These are the first points in the outline. */
+ outline->xmin = outline->xmax = x;
+ outline->ymin = outline->ymax = y;
+ }
+ else
+ {
+ outline->xmin = MIN ((sfnt_fixed) x, outline->xmin);
+ outline->ymin = MIN ((sfnt_fixed) y, outline->ymin);
+ outline->xmax = MAX ((sfnt_fixed) x, outline->xmax);
+ outline->ymax = MAX ((sfnt_fixed) y, outline->ymax);
+ }
+
+ return outline;
+}
+
+#ifndef INT64_MAX
+
+/* 64 bit integer type. */
+
+struct sfnt_large_integer
+{
+ unsigned int high, low;
+};
+
+/* Calculate (A * B), placing the result in *VALUE. */
+
+static void
+sfnt_multiply_divide_1 (unsigned int a, unsigned int b,
+ struct sfnt_large_integer *value)
+{
+ unsigned int lo1, hi1, lo2, hi2, lo, hi, i1, i2;
+
+ lo1 = a & 0x0000ffffu;
+ hi1 = a >> 16;
+ lo2 = b & 0x0000ffffu;
+ hi2 = b >> 16;
+
+ lo = lo1 * lo2;
+ i1 = lo1 * hi2;
+ i2 = lo2 * hi1;
+ hi = hi1 * hi2;
+
+ /* Check carry overflow of i1 + i2. */
+ i1 += i2;
+ hi += (unsigned int) (i1 < i2) << 16;
+
+ hi += i1 >> 16;
+ i1 = i1 << 16;
+
+ /* Check carry overflow of i1 + lo. */
+ lo += i1;
+ hi += (lo < i1);
+
+ value->low = lo;
+ value->high = hi;
+}
+
+/* Count the number of most significant zero bits in N. */
+
+static unsigned int
+sfnt_count_leading_zero_bits (unsigned int n)
+{
+ int shift;
+
+ shift = 0;
+
+ if (n & 0xffff0000ul)
+ {
+ n >>= 16;
+ shift += 16;
+ }
+
+ if (n & 0x0000ff00ul)
+ {
+ n >>= 8;
+ shift += 8;
+ }
+
+ if (n & 0x000000f0ul)
+ {
+ n >>= 4;
+ shift += 4;
+ }
+
+ if (n & 0x0000000cul)
+ {
+ n >>= 2;
+ shift += 2;
+ }
+
+ if (n & 0x00000002ul)
+ shift += 1;
+
+ return shift;
+}
+
+/* Calculate AB / C. Value is a 32 bit unsigned integer. */
+
+static unsigned int
+sfnt_multiply_divide_2 (struct sfnt_large_integer *ab,
+ unsigned int c)
+{
+ unsigned int hi, lo;
+ int i;
+ unsigned int r, q; /* Remainder and quotient. */
+
+ hi = ab->high;
+ lo = ab->low;
+
+ i = 31 - sfnt_count_leading_zero_bits (hi);
+ r = (hi << i) | (lo >> (32 - i));
+ lo <<= i;
+ q = r / c;
+ r -= q * c;
+ i = 32 - i;
+
+ do
+ {
+ q <<= 1;
+ r = (r << 1) | (lo >> 31);
+ lo <<= 1;
+
+ if (r >= c)
+ {
+ r -= c;
+ q |= 1;
+ }
+ }
+ while (--i);
+
+ return q;
+}
+
+#endif
+
+/* Calculate (A * B) / C with no rounding and return the result, using
+ a 64 bit integer if necessary. */
+
+static unsigned int
+sfnt_multiply_divide (unsigned int a, unsigned int b, unsigned int c)
+{
+#ifndef INT64_MAX
+ struct sfnt_large_integer temp;
+
+ sfnt_multiply_divide_1 (a, b, &temp);
+ return sfnt_multiply_divide_2 (&temp, c);
+#else
+ uint64_t temp;
+
+ temp = (uint64_t) a * (uint64_t) b;
+ return temp / c;
+#endif
+}
+
+#ifndef INT64_MAX
+
+/* Add the specified unsigned 32-bit N to the large integer
+ INTEGER. */
+
+static void
+sfnt_large_integer_add (struct sfnt_large_integer *integer,
+ uint32_t n)
+{
+ struct sfnt_large_integer number;
+
+ number.low = integer->low + n;
+ number.high = integer->high + (number.low
+ < integer->low);
+
+ *integer = number;
+}
+
+/* Calculate (A * B) / C, rounding the result with a threshold of N.
+ Use a 64 bit temporary. */
+
+static unsigned int
+sfnt_multiply_divide_round (unsigned int a, unsigned int b,
+ unsigned int n, unsigned int c)
+{
+ struct sfnt_large_integer temp;
+
+ sfnt_multiply_divide_1 (a, b, &temp);
+ sfnt_large_integer_add (&temp, n);
+ return sfnt_multiply_divide_2 (&temp, c);
+}
+
+#endif /* INT64_MAX */
+
+/* The same as sfnt_multiply_divide, but handle signed values
+ instead. */
+
+MAYBE_UNUSED static int
+sfnt_multiply_divide_signed (int a, int b, int c)
+{
+ int sign;
+
+ sign = 1;
+
+ if (a < 0)
+ sign = -sign;
+
+ if (b < 0)
+ sign = -sign;
+
+ if (c < 0)
+ sign = -sign;
+
+ return (sfnt_multiply_divide (abs (a), abs (b), abs (c))
+ * sign);
+}
+
+/* Multiply the two 16.16 fixed point numbers X and Y. Return the
+ result regardless of overflow. */
+
+static sfnt_fixed
+sfnt_mul_fixed (sfnt_fixed x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+ int64_t product;
+
+ product = (int64_t) x * (int64_t) y;
+
+ /* This can be done quickly with int64_t. */
+ return product / (int64_t) 65536;
+#else
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ return sfnt_multiply_divide (abs (x), abs (y),
+ 65536) * sign;
+#endif
+}
+
+/* Multiply the two 16.16 fixed point numbers X and Y, with rounding
+ of the result. */
+
+static sfnt_fixed
+sfnt_mul_fixed_round (sfnt_fixed x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+ int64_t product, round;
+
+ product = (int64_t) x * (int64_t) y;
+ round = product < 0 ? -32768 : 32768;
+
+ /* This can be done quickly with int64_t. */
+ return (product + round) / (int64_t) 65536;
+#else
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ return sfnt_multiply_divide_round (abs (x), abs (y),
+ 32768, 65536) * sign;
+#endif
+}
+
+/* Set the pen size to the specified point and return. POINT will be
+ scaled up to the pixel size. */
+
+static void
+sfnt_move_to_and_build (struct sfnt_point point, void *dcontext)
+{
+ sfnt_fixed x, y;
+
+ x = sfnt_mul_fixed (build_outline_context.factor, point.x);
+ y = sfnt_mul_fixed (build_outline_context.factor, point.y);
+
+ build_outline_context.outline = sfnt_build_append (0, x, y);
+ build_outline_context.x = x;
+ build_outline_context.y = y;
+}
+
+/* Record a line to the specified point and return. POINT will be
+ scaled up to the pixel size. */
+
+static void
+sfnt_line_to_and_build (struct sfnt_point point, void *dcontext)
+{
+ sfnt_fixed x, y;
+
+ x = sfnt_mul_fixed (build_outline_context.factor, point.x);
+ y = sfnt_mul_fixed (build_outline_context.factor, point.y);
+
+ build_outline_context.outline
+ = sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO,
+ x, y);
+ build_outline_context.x = x;
+ build_outline_context.y = y;
+}
+
+/* Divide the two 16.16 fixed point numbers X and Y. Return the
+ result regardless of overflow. */
+
+static sfnt_fixed
+sfnt_div_fixed (sfnt_fixed x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+ int64_t result;
+
+ result = ((int64_t) x * 65536) / y;
+
+ return result;
+#else
+ int sign;
+ unsigned int a, b;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ a = abs (x);
+ b = abs (y);
+
+ return sfnt_multiply_divide (a, 65536, b) * sign;
+#endif
+}
+
+/* Return the ceiling value of the specified fixed point number X. */
+
+static sfnt_fixed
+sfnt_ceil_fixed (sfnt_fixed x)
+{
+ return (x + 0177777) & 037777600000;
+}
+
+/* Return the floor value of the specified fixed point number X. */
+
+static sfnt_fixed
+sfnt_floor_fixed (sfnt_fixed x)
+{
+ return x & 037777600000;
+}
+
+/* Given a curve consisting of three points CONTROL0, CONTROL1 and
+ ENDPOINT, return whether or not the curve is sufficiently small to
+ be approximated by a line between CONTROL0 and ENDPOINT. */
+
+static bool
+sfnt_curve_is_flat (struct sfnt_point control0,
+ struct sfnt_point control1,
+ struct sfnt_point endpoint)
+{
+ struct sfnt_point g, h;
+
+ g.x = control1.x - control0.x;
+ g.y = control1.y - control0.y;
+ h.x = endpoint.x - control0.x;
+ h.y = endpoint.y - control0.y;
+
+ /* 2.0 is a constant describing the area covered at which point the
+ curve is considered "flat". */
+ return (abs (sfnt_mul_fixed (g.x, h.y)
+ - sfnt_mul_fixed (g.y, h.x))
+ <= 0400000);
+}
+
+/* Recursively split the splines in the bezier curve formed from
+ CONTROL0, CONTROL1 and ENDPOINT until the area between the curve's
+ two ends is small enough to be considered ``flat''. Then, turn
+ those ``flat'' curves into lines. */
+
+static void
+sfnt_curve_to_and_build_1 (struct sfnt_point control0,
+ struct sfnt_point control1,
+ struct sfnt_point endpoint)
+{
+ struct sfnt_point ab, bc, abbc;
+
+ /* control0, control and endpoint make up the spline. Figure out
+ its distance from a line. */
+ if (sfnt_curve_is_flat (control0, control1, endpoint))
+ {
+ /* Draw a line to endpoint. */
+ build_outline_context.outline
+ = sfnt_build_append (SFNT_GLYPH_OUTLINE_LINETO,
+ endpoint.x, endpoint.y);
+ build_outline_context.x = endpoint.x;
+ build_outline_context.y = endpoint.y;
+ }
+ else
+ {
+ /* Calculate new control points.
+ Maybe apply a recursion limit here? */
+ sfnt_lerp_half (&control0, &control1, &ab);
+ sfnt_lerp_half (&control1, &endpoint, &bc);
+ sfnt_lerp_half (&ab, &bc, &abbc);
+
+ /* Keep splitting until a flat enough spline results. */
+ sfnt_curve_to_and_build_1 (control0, ab, abbc);
+
+ /* Then go on with the spline between control1 and endpoint. */
+ sfnt_curve_to_and_build_1 (abbc, bc, endpoint);
+ }
+}
+
+/* Scale and decompose the specified bezier curve into individual
+ lines. Then, record each of those lines into the outline being
+ built. */
+
+static void
+sfnt_curve_to_and_build (struct sfnt_point control,
+ struct sfnt_point endpoint,
+ void *dcontext)
+{
+ struct sfnt_point control0;
+
+ control0.x = build_outline_context.x;
+ control0.y = build_outline_context.y;
+ control.x = sfnt_mul_fixed (control.x,
+ build_outline_context.factor);
+ control.y = sfnt_mul_fixed (control.y,
+ build_outline_context.factor);
+ endpoint.x = sfnt_mul_fixed (endpoint.x,
+ build_outline_context.factor);
+ endpoint.y = sfnt_mul_fixed (endpoint.y,
+ build_outline_context.factor);
+
+ sfnt_curve_to_and_build_1 (control0, control, endpoint);
+}
+
+/* Non-reentrantly build the outline for the specified GLYPH at the
+ given scale factor. Return the outline data with a refcount of 0
+ upon success, or NULL upon failure.
+
+ SCALE is a scale factor that converts between em space and device
+ space.
+
+ Use the unscaled glyph METRICS to determine the origin point of the
+ outline.
+
+ Call GET_GLYPH and FREE_GLYPH with the specified DCONTEXT to obtain
+ glyphs for compound glyph subcomponents. */
+
+TEST_STATIC struct sfnt_glyph_outline *
+sfnt_build_glyph_outline (struct sfnt_glyph *glyph,
+ sfnt_fixed scale,
+ struct sfnt_glyph_metrics *metrics,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ void *dcontext)
+{
+ struct sfnt_glyph_outline *outline;
+ int rc;
+ sfnt_fword origin;
+
+ memset (&build_outline_context, 0, sizeof build_outline_context);
+
+ /* Allocate the outline now with enough for 44 words at the end. */
+ outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline));
+ outline->outline_size = 40;
+ outline->outline_used = 0;
+ outline->refcount = 0;
+ outline->outline
+ = (struct sfnt_glyph_outline_command *) (outline + 1);
+
+ /* DCONTEXT will be passed to GET_GLYPH and FREE_GLYPH, so global
+ variables must be used to communicate with the decomposition
+ functions. */
+ build_outline_context.outline = outline;
+
+ /* Clear outline bounding box. */
+ outline->xmin = 0;
+ outline->ymin = 0;
+ outline->xmax = 0;
+ outline->ymax = 0;
+
+ /* Set the scale factor. */
+ build_outline_context.factor = scale;
+
+ /* Decompose the outline. */
+ rc = sfnt_decompose_glyph (glyph, sfnt_move_to_and_build,
+ sfnt_line_to_and_build,
+ sfnt_curve_to_and_build,
+ get_glyph, free_glyph, dcontext);
+
+ /* Synchronize the outline object with what might have changed
+ inside sfnt_decompose_glyph. */
+ outline = build_outline_context.outline;
+
+ if (rc)
+ {
+ xfree (outline);
+ return NULL;
+ }
+
+ /* Compute the origin position. Note that the original glyph xmin
+ is first used to calculate the origin point, and the origin
+ distortion is applied to it to get the distorted origin. */
+
+ origin = glyph->xmin - metrics->lbearing + glyph->origin_distortion;
+ outline->origin = sfnt_mul_fixed (origin, scale);
+
+ return outline;
+}
+
+
+
+/* Glyph rasterization. The algorithm used here is fairly simple.
+ Each contour is decomposed into lines, which turn into a polygon.
+ Then, a bog standard edge filler is used to turn them into
+ spans. */
+
+/* Coverage table. This is a four dimensional array indiced by the Y,
+ then X axis fractional, shifted down to 2 bits. */
+
+static const unsigned char sfnt_poly_coverage[8][9] =
+ {
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 3, 7, 11, 15, 19, 23, 27, 31, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ { 0, 4, 8, 12, 16, 20, 24, 28, 32, },
+ };
+
+/* Return the nearest coordinate on the sample grid no less than
+ F. */
+
+static sfnt_fixed
+sfnt_poly_grid_ceil (sfnt_fixed f)
+{
+ return (((f + (SFNT_POLY_START - 1))
+ & ~(SFNT_POLY_STEP - 1)) + SFNT_POLY_START);
+}
+
+enum
+ {
+ SFNT_POLY_ALIGNMENT = 4,
+ };
+
+/* Initialize the specified RASTER in preparation for displaying spans
+ for OUTLINE, and set RASTER->refcount to 0. The caller must then
+ set RASTER->cells to a zeroed array of size RASTER->stride *
+ RASTER->height, aligned to RASTER. */
+
+TEST_STATIC void
+sfnt_prepare_raster (struct sfnt_raster *raster,
+ struct sfnt_glyph_outline *outline)
+{
+ raster->width
+ = (sfnt_ceil_fixed (outline->xmax)
+ - sfnt_floor_fixed (outline->xmin)) / 65536;
+ raster->height
+ = (sfnt_ceil_fixed (outline->ymax)
+ - sfnt_floor_fixed (outline->ymin)) / 65536;
+ raster->refcount = 0;
+
+ /* Align the raster to a SFNT_POLY_ALIGNMENT byte boundary. */
+ raster->stride = ((raster->width
+ + (SFNT_POLY_ALIGNMENT - 1))
+ & ~(SFNT_POLY_ALIGNMENT - 1));
+
+ /* Apply outline->origin. This is 0 by convention in most fonts.
+ However, variable fonts typically change this as variations are
+ applied. */
+ raster->offx = sfnt_floor_fixed (outline->xmin
+ - outline->origin) / 65536;
+ raster->offy = sfnt_floor_fixed (outline->ymin) / 65536;
+}
+
+typedef void (*sfnt_edge_proc) (struct sfnt_edge *, size_t,
+ void *);
+typedef void (*sfnt_span_proc) (struct sfnt_edge *, sfnt_fixed, void *);
+
+/* Move EDGE->x forward, assuming that the scanline has moved upwards
+ by SFNT_POLY_STEP. */
+
+static void
+sfnt_step_edge (struct sfnt_edge *edge)
+{
+ /* Add step. */
+ edge->x += edge->step_x;
+}
+
+/* Build a list of edges for each contour in OUTLINE, applying xmin
+ and ymin as the offset to each edge. Call EDGE_PROC with DCONTEXT
+ and the resulting edges as arguments. It is OK to modify the edges
+ given to EDGE_PROC. Align all edges to the sub-pixel grid. */
+
+static void
+sfnt_build_outline_edges (struct sfnt_glyph_outline *outline,
+ sfnt_edge_proc edge_proc, void *dcontext)
+{
+ struct sfnt_edge *edges;
+ size_t i, edge, next_vertex;
+ sfnt_fixed dx, dy, bot, step_x, ymin, xmin;
+ size_t top, bottom, y;
+
+ edges = alloca (outline->outline_used * sizeof *edges);
+ edge = 0;
+
+ /* ymin and xmin must be the same as the offset used to set offy and
+ offx in rasters. */
+ ymin = sfnt_floor_fixed (outline->ymin);
+ xmin = sfnt_floor_fixed (outline->xmin);
+
+ for (i = 0; i < outline->outline_used; ++i)
+ {
+ /* Set NEXT_VERTEX to the next point (vertex) in this contour.
+
+ If i is past the end of the contour, then don't build edges
+ for this point. */
+ next_vertex = i + 1;
+
+ if (next_vertex == outline->outline_used
+ || !(outline->outline[next_vertex].flags
+ & SFNT_GLYPH_OUTLINE_LINETO))
+ continue;
+
+ /* Skip past horizontal vertices. */
+ if (outline->outline[next_vertex].y == outline->outline[i].y)
+ continue;
+
+ /* Figure out the winding direction. */
+ if (outline->outline[next_vertex].y < outline->outline[i].y)
+ /* Vector will cross imaginary ray from its bottom from the
+ left of the ray. Winding is thus 1. */
+ edges[edge].winding = 1;
+ else
+ /* Moving clockwise. Winding is thus -1. */
+ edges[edge].winding = -1;
+
+ /* Figure out the top and bottom values of this edge. If the
+ next edge is below, top is here and bot is the edge below.
+ If the next edge is above, then top is there and this is the
+ bottom. */
+
+ if (outline->outline[next_vertex].y < outline->outline[i].y)
+ {
+ /* End of edge is below this one (keep in mind this is a
+ cartesian coordinate system, so smaller values are below
+ larger ones.) */
+ top = i;
+ bottom = next_vertex;
+ }
+ else
+ {
+ /* End of edge is above this one. */
+ bottom = i;
+ top = next_vertex;
+ }
+
+ bot = (outline->outline[bottom].y - ymin);
+ edges[edge].top = (outline->outline[top].y - ymin);
+
+ /* Record the edge. Rasterization happens from bottom to
+ up, so record the X at the bottom. */
+ edges[edge].x = (outline->outline[bottom].x - xmin);
+ dx = (outline->outline[top].x - outline->outline[bottom].x);
+ dy = abs (outline->outline[top].y
+ - outline->outline[bottom].y);
+
+ /* Step to first grid point. */
+ y = sfnt_poly_grid_ceil (bot);
+
+ /* If rounding would make the edge not cover any area, skip this
+ edge. */
+
+ if (y >= edges[edge].top)
+ continue;
+
+ /* Compute the step X. This is how much X changes for each
+ increase in Y. */
+
+ step_x = sfnt_div_fixed (dx, dy);
+ edges[edge].next = NULL;
+
+ /* Compute the step X scaled to the poly step. */
+ edges[edge].step_x
+ = sfnt_mul_fixed (step_x, SFNT_POLY_STEP);
+
+ /* Step to the grid point. */
+ edges[edge].x += sfnt_mul_fixed (step_x, bot - y);
+
+ /* Set the bottom position. */
+ edges[edge].bottom = y;
+
+ edge++;
+ }
+
+ if (edge)
+ edge_proc (edges, edge, dcontext);
+}
+
+/* Sort an array of SIZE edges to increase by bottom Y position, in
+ preparation for building spans.
+
+ Insertion sort is used because there are usually not very many
+ edges, and anything larger would bloat up the code. */
+
+static void
+sfnt_edge_sort (struct sfnt_edge *edges, size_t size)
+{
+ ssize_t i, j;
+ struct sfnt_edge edge;
+
+ for (i = 1; i < size; ++i)
+ {
+ edge = edges[i];
+ j = i - 1;
+
+ while (j >= 0 && (edges[j].bottom > edge.bottom))
+ {
+ edges[j + 1] = edges[j];
+ j--;
+ }
+
+ edges[j + 1] = edge;
+ }
+}
+
+/* Draw EDGES, an unsorted array of polygon edges of size SIZE. For
+ each scanline, call SPAN_FUNC with a list of active edges and
+ coverage information, and DCONTEXT.
+
+ Sort each edge in ascending order by the bottommost Y coordinate to
+ which it applies. Start a loop on the Y coordinate, which starts
+ out at that of the bottommost edge. For each iteration, add edges
+ that now overlap with Y, keeping them sorted by X. Poly those
+ edges through SPAN_FUNC. Then, move upwards by SFNT_POLY_STEP,
+ remove edges that no longer apply, and interpolate the remaining
+ edges' X coordinates. Repeat until all the edges have been polyed.
+
+ Or alternatively, think of this as such: each edge is actually a
+ vector from its bottom position towards its top most position.
+ Every time Y moves upwards, the position of each edge intersecting
+ with Y is interpolated and added to a list of spans along with
+ winding information that is then given to EDGE_FUNC.
+
+ Anti-aliasing is performed using a coverage map for fractional
+ coordinates, and incrementing the Y axis by SFNT_POLY_STEP instead
+ of 1. SFNT_POLY_STEP is chosen to always keep Y aligned to a grid
+ placed such that there are always 1 << SFNT_POLY_SHIFT positions
+ available for each integral pixel coordinate. */
+
+static void
+sfnt_poly_edges (struct sfnt_edge *edges, size_t size,
+ sfnt_span_proc span_func, void *dcontext)
+{
+ sfnt_fixed y;
+ size_t e;
+ struct sfnt_edge *active, **prev, *a, *n;
+
+ if (!size)
+ return;
+
+ /* Sort edges to ascend by Y-order. Once again, remember: cartesian
+ coordinates. */
+ sfnt_edge_sort (edges, size);
+
+ /* Step down line by line. Find active edges. */
+
+ y = edges[0].bottom;
+ active = 0;
+ active = NULL;
+ e = 0;
+
+ for (;;)
+ {
+ /* Add in new edges keeping them sorted. */
+ for (; e < size && edges[e].bottom <= y; ++e)
+ {
+ /* Find where to place this edge. */
+ for (prev = &active; (a = *prev); prev = &(a->next))
+ {
+ if (a->x > edges[e].x)
+ break;
+ }
+
+ edges[e].next = *prev;
+ *prev = &edges[e];
+ }
+
+ /* Draw this span at the current position. Y axis antialiasing
+ is expected to be handled by SPAN_FUNC. */
+ span_func (active, y, dcontext);
+
+ /* Compute the next Y position. */
+ y += SFNT_POLY_STEP;
+
+ /* Strip out edges that no longer have effect. */
+
+ for (prev = &active; (a = *prev);)
+ {
+ if (a->top <= y)
+ *prev = a->next;
+ else
+ prev = &a->next;
+ }
+
+ /* Break if all is done. */
+ if (!active && e == size)
+ break;
+
+ /* Step all edges. */
+ for (a = active; a; a = a->next)
+ sfnt_step_edge (a);
+
+ /* Resort on X axis. */
+ for (prev = &active; (a = *prev) && (n = a->next);)
+ {
+ if (a->x > n->x)
+ {
+ a->next = n->next;
+ n->next = a;
+ *prev = n;
+ prev = &active;
+ }
+ else
+ prev = &a->next;
+ }
+ }
+}
+
+/* Saturate and convert the given unsigned short value X to an
+ unsigned char. */
+
+static unsigned char
+sfnt_saturate_short (unsigned short x)
+{
+ if (x > 255)
+ return 255;
+
+ return x;
+}
+
+/* Fill a single span of pixels between X0 and X1 at Y, a raster
+ coordinate, onto RASTER. */
+
+static void
+sfnt_fill_span (struct sfnt_raster *raster, sfnt_fixed y,
+ sfnt_fixed x0, sfnt_fixed x1)
+{
+ unsigned char *start;
+ const unsigned char *coverage;
+ sfnt_fixed left, right, end;
+ unsigned short w, a;
+ int row;
+
+ /* Clip bounds to pixmap. */
+ if (x0 < 0)
+ x0 = 0;
+
+ if (x1 >= raster->width << 16)
+ x1 = (raster->width - 1) << 16;
+
+ /* Check for empty bounds. */
+ if (x1 <= x0)
+ return;
+
+ /* Figure out coverage based on Y axis fractional. */
+ coverage = sfnt_poly_coverage[(y >> (16 - SFNT_POLY_SHIFT))
+ & SFNT_POLY_MASK];
+ row = y >> 16;
+
+ /* Don't fill out of bounds rows. */
+ if (row < 0 || row >= raster->height)
+ return;
+
+ /* Set start, then start filling according to coverage. left and
+ right are now .3. */
+ left = x0 >> (16 - SFNT_POLY_SHIFT);
+ right = x1 >> (16 - SFNT_POLY_SHIFT);
+ start = raster->cells + row * raster->stride;
+ start += left >> SFNT_POLY_SHIFT;
+
+ /* If left and right actually lie in the same pixel, just fill with
+ the coverage of both and return. */
+
+ if ((left & ~SFNT_POLY_MASK) == (right & ~SFNT_POLY_MASK))
+ {
+ w = coverage[right - left];
+ a = *start + w;
+
+ *start = sfnt_saturate_short (a);
+ return;
+ }
+
+ /* Compute coverage for first pixel, then poly. The code from here
+ onwards assumes that left and right are on two different
+ pixels. */
+
+ if (left & SFNT_POLY_MASK)
+ {
+ /* Compute the coverage for the first pixel, and move left past
+ it. The coverage is a number from 1 to 7 describing how
+ ``partially'' covered this pixel is. */
+
+ end = (left + SFNT_POLY_SAMPLE - 1) & ~SFNT_POLY_MASK;
+ end = MIN (right, end);
+
+ w = coverage[end - left];
+ a = *start + w;
+
+ /* Now move left past. */
+ left = end;
+ *start++ = sfnt_saturate_short (a);
+ }
+
+ /* Clear coverage info for first pixel. Compute coverage for center
+ pixels. Note that SFNT_POLY_SAMPLE is used and not
+ SFNT_POLY_MASK, because coverage has a blank column at the
+ start. */
+ w = coverage[SFNT_POLY_SAMPLE];
+
+ /* Fill pixels between left and right. */
+ while (left + SFNT_POLY_MASK < right)
+ {
+ a = *start + w;
+ *start++ = sfnt_saturate_short (a);
+ left += SFNT_POLY_SAMPLE;
+ }
+
+ /* Fill rightmost pixel with any partial coverage. */
+
+ if (right & SFNT_POLY_MASK)
+ {
+ w = coverage[right - left];
+ a = *start + w;
+ *start = sfnt_saturate_short (a);
+ }
+
+ /* All done. */
+}
+
+/* Poly each span starting from START onto RASTER, at position Y. Y
+ here is still a cartesian coordinate, where the bottom of the
+ raster is 0. But that is no longer true by the time sfnt_span_fill
+ is called. */
+
+static void
+sfnt_poly_span (struct sfnt_edge *start, sfnt_fixed y,
+ struct sfnt_raster *raster)
+{
+ struct sfnt_edge *edge;
+ int winding;
+ sfnt_fixed x0, x1;
+
+ /* Pacify -Wmaybe-uninitialized; x1 and x0 are only used when edge
+ != start, at which point x0 has already been set. */
+ x0 = x1 = 0;
+
+ /* Generate the X axis coverage map. Then poly it onto RASTER.
+ winding on each edge determines the winding direction: when it is
+ positive, winding is 1. When it is negative, winding is -1.
+
+ Fill each consecutive stretch of spans that are inside the glyph;
+ otherwise, coverage will overlap for some spans, but not
+ others.
+
+ The spans must be terminated with an edge that causes an
+ off-transition, or some spans will not be filled. */
+
+ winding = 0;
+
+ for (edge = start; edge; edge = edge->next)
+ {
+ if (!winding)
+ {
+ if (edge != start && x0 != x1)
+ /* Draw this section of spans that are on. */
+ sfnt_fill_span (raster, (raster->height << 16) - y,
+ x0, x1);
+
+ x0 = x1 = edge->x;
+ }
+ else
+ x1 = edge->x;
+
+ winding += edge->winding;
+ }
+
+ /* Draw the last span following the last off-transition. */
+
+ if (!winding && edge != start && x0 != x1)
+ sfnt_fill_span (raster, (raster->height << 16) - y,
+ x0, x1);
+}
+
+
+
+/* Main entry point for outline rasterization. */
+
+/* Raster the spans between START and its end to the raster specified
+ as DCONTEXT. The span's position is Y. */
+
+static void
+sfnt_raster_span (struct sfnt_edge *start, sfnt_fixed y,
+ void *dcontext)
+{
+ sfnt_poly_span (start, y, dcontext);
+}
+
+/* Generate and poly each span in EDGES onto the raster specified as
+ DCONTEXT. */
+
+static void
+sfnt_raster_edge (struct sfnt_edge *edges, size_t num_edges,
+ void *dcontext)
+{
+ sfnt_poly_edges (edges, num_edges, sfnt_raster_span,
+ dcontext);
+}
+
+/* Generate an alpha mask for the glyph outline OUTLINE. Value is the
+ alpha mask upon success, NULL upon failure. */
+
+TEST_STATIC struct sfnt_raster *
+sfnt_raster_glyph_outline (struct sfnt_glyph_outline *outline)
+{
+ struct sfnt_raster raster, *data;
+
+ /* Get the raster parameters. */
+ sfnt_prepare_raster (&raster, outline);
+
+ /* Allocate the raster data. */
+ data = xmalloc (sizeof *data + raster.stride * raster.height);
+ *data = raster;
+ data->cells = (unsigned char *) (data + 1);
+ memset (data->cells, 0, raster.stride * raster.height);
+
+ /* Generate edges for the outline, polying each array of edges to
+ the raster. */
+ sfnt_build_outline_edges (outline, sfnt_raster_edge, data);
+
+ /* All done. */
+ return data;
+}
+
+
+
+/* Glyph metrics computation. */
+
+/* Read an hmtx table from the font FD, using the table directory
+ specified as SUBTABLE, the maxp table MAXP, and the hhea table
+ HHEA.
+
+ Return NULL upon failure, and the hmtx table otherwise.
+ HHEA->num_of_long_hor_metrics determines the number of horizontal
+ metrics present, and MAXP->num_glyphs -
+ HHEA->num_of_long_hor_metrics determines the number of left-side
+ bearings present. */
+
+TEST_STATIC struct sfnt_hmtx_table *
+sfnt_read_hmtx_table (int fd, struct sfnt_offset_subtable *subtable,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_maxp_table *maxp)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_hmtx_table *hmtx;
+ size_t size;
+ ssize_t rc;
+ int i;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_HMTX);
+
+ if (!directory)
+ return NULL;
+
+ /* Figure out how many bytes are required. */
+ size = ((hhea->num_of_long_hor_metrics
+ * sizeof (struct sfnt_long_hor_metric))
+ + (MAX (0, ((int) maxp->num_glyphs
+ - hhea->num_of_long_hor_metrics))
+ * sizeof (int16_t)));
+
+ /* Check the length matches exactly. */
+ if (directory->length != size)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Now allocate enough to hold all of that along with the table
+ directory structure. */
+
+ hmtx = xmalloc (sizeof *hmtx + size);
+
+ /* Read into hmtx + 1. */
+ rc = read (fd, hmtx + 1, size);
+ if (rc < size)
+ {
+ xfree (hmtx);
+ return NULL;
+ }
+
+ /* Set pointers to data. */
+ hmtx->h_metrics = (struct sfnt_long_hor_metric *) (hmtx + 1);
+ hmtx->left_side_bearing
+ = (int16_t *) (hmtx->h_metrics
+ + hhea->num_of_long_hor_metrics);
+
+ /* Swap what was read. */
+
+ for (i = 0; i < hhea->num_of_long_hor_metrics; ++i)
+ {
+ sfnt_swap16 (&hmtx->h_metrics[i].advance_width);
+ sfnt_swap16 (&hmtx->h_metrics[i].left_side_bearing);
+ }
+
+ for (; i < maxp->num_glyphs; ++i)
+ sfnt_swap16 (&hmtx->left_side_bearing[i - hhea->num_of_long_hor_metrics]);
+
+ /* All done. */
+ return hmtx;
+}
+
+/* Obtain glyph metrics for the glyph indiced by GLYPH at the
+ specified PIXEL_SIZE. Return 0 and the metrics in *METRICS if
+ metrics could be found, else 1.
+
+ If PIXEL_SIZE is -1, do not perform any scaling on the glyph
+ metrics; HEAD need not be specified in that case.
+
+ HMTX, HHEA, HEAD and MAXP should be the hmtx, hhea, head, and maxp
+ tables of the font respectively. */
+
+TEST_STATIC int
+sfnt_lookup_glyph_metrics (sfnt_glyph glyph, int pixel_size,
+ struct sfnt_glyph_metrics *metrics,
+ struct sfnt_hmtx_table *hmtx,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_head_table *head,
+ struct sfnt_maxp_table *maxp)
+{
+ short lbearing;
+ unsigned short advance;
+ sfnt_fixed factor;
+
+ if (glyph < hhea->num_of_long_hor_metrics)
+ {
+ /* There is a long entry in the hmtx table. */
+ lbearing = hmtx->h_metrics[glyph].left_side_bearing;
+ advance = hmtx->h_metrics[glyph].advance_width;
+ }
+ else if (hhea->num_of_long_hor_metrics
+ && glyph < maxp->num_glyphs)
+ {
+ /* There is a short entry in the hmtx table. */
+ lbearing
+ = hmtx->left_side_bearing[glyph
+ - hhea->num_of_long_hor_metrics];
+ advance
+ = hmtx->h_metrics[hhea->num_of_long_hor_metrics - 1].advance_width;
+ }
+ else
+ /* No entry corresponds to the glyph. */
+ return 1;
+
+ if (pixel_size == -1)
+ {
+ /* Return unscaled metrics in this case. */
+ metrics->lbearing = lbearing;
+ metrics->advance = advance;
+ return 0;
+ }
+
+ /* Now scale lbearing and advance up to the pixel size. */
+ factor = sfnt_div_fixed (pixel_size, head->units_per_em);
+
+ /* Save them. */
+ metrics->lbearing = sfnt_mul_fixed (lbearing * 65536, factor);
+ metrics->advance = sfnt_mul_fixed (advance * 65536, factor);
+
+ /* All done. */
+ return 0;
+}
+
+/* Scale the specified glyph metrics by FACTOR.
+ Set METRICS->lbearing and METRICS->advance to their current
+ values times factor. */
+
+MAYBE_UNUSED TEST_STATIC void
+sfnt_scale_metrics (struct sfnt_glyph_metrics *metrics,
+ sfnt_fixed factor)
+{
+ metrics->lbearing
+ = sfnt_mul_fixed (metrics->lbearing * 65536, factor);
+ metrics->advance
+ = sfnt_mul_fixed (metrics->advance * 65536, factor);
+}
+
+/* Calculate the factor used to convert em space to device space for a
+ font with the specified HEAD table and PPEM value. */
+
+MAYBE_UNUSED TEST_STATIC sfnt_fixed
+sfnt_get_scale (struct sfnt_head_table *head, int ppem)
+{
+ /* Figure out how to convert from font unit-space to pixel space.
+ To turn one unit to its corresponding pixel size given a ppem of
+ 1, the unit must be divided by head->units_per_em. Then, it must
+ be multipled by the ppem. So,
+
+ PIXEL = UNIT / UPEM * PPEM
+
+ which means:
+
+ PIXEL = UNIT * PPEM / UPEM */
+
+ return sfnt_div_fixed (ppem, head->units_per_em);
+}
+
+
+
+/* Font style parsing. */
+
+/* Read the name table from the given font FD, using the table
+ directory specified as SUBTABLE. Perform validation on the offsets
+ in the name records. Return NULL upon failure, else the name
+ table. */
+
+TEST_STATIC struct sfnt_name_table *
+sfnt_read_name_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_name_table *name;
+ size_t required;
+ ssize_t rc;
+ int i;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_NAME);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum that has to be read. */
+ required = SFNT_ENDOF (struct sfnt_name_table,
+ string_offset, uint16_t);
+
+ if (directory->length < required)
+ return NULL;
+
+ /* Allocate enough to hold the name table and variable length
+ data. */
+ name = xmalloc (sizeof *name + directory->length);
+
+ /* Read the fixed length data. */
+ rc = read (fd, name, required);
+ if (rc < required)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap16 (&name->format);
+ sfnt_swap16 (&name->count);
+ sfnt_swap16 (&name->string_offset);
+
+ /* Reject unsupported formats. */
+ if (name->format)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Set the pointer to the start of the variable length data. */
+ name->name_records
+ = (struct sfnt_name_record *) (name + 1);
+
+ /* Check there is enough for the name records. */
+ required = directory->length - required;
+ if (required < name->count * sizeof *name->name_records)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Read the variable length data. First, read the name records. */
+ rc = read (fd, name->name_records,
+ (name->count
+ * sizeof *name->name_records));
+ if (rc < (name->count
+ * sizeof *name->name_records))
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Swap each of the name records. */
+ for (i = 0; i < name->count; ++i)
+ {
+ sfnt_swap16 (&name->name_records[i].platform_id);
+ sfnt_swap16 (&name->name_records[i].platform_specific_id);
+ sfnt_swap16 (&name->name_records[i].language_id);
+ sfnt_swap16 (&name->name_records[i].name_id);
+ sfnt_swap16 (&name->name_records[i].length);
+ sfnt_swap16 (&name->name_records[i].offset);
+ }
+
+ /* Now, read the name data. */
+
+ if (name->string_offset > directory->length)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ required = directory->length - name->string_offset;
+
+ /* It can happen that the string offset comes before the name
+ records, and as a result exceeds the number of bytes
+ previously allocated. Extend name if that is the case. */
+
+ if (required > (directory->length
+ - (name->count
+ * sizeof *name->name_records)))
+ {
+ name = xrealloc (name, (sizeof *name
+ + (name->count
+ * sizeof *name->name_records)
+ + required));
+ name->name_records = (struct sfnt_name_record *) (name + 1);
+ }
+
+ /* There is enough space past name->name_records to hold REQUIRED
+ bytes. Seek to the right offset. */
+
+ if (lseek (fd, directory->offset + name->string_offset,
+ SEEK_SET) == (off_t) -1)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Read REQURIED bytes into the string data. */
+ name->data = (unsigned char *) (name->name_records
+ + name->count);
+ rc = read (fd, name->data, required);
+ if (rc < required)
+ {
+ xfree (name);
+ return NULL;
+ }
+
+ /* Now validate each of the name records. */
+ for (i = 0; i < name->count; ++i)
+ {
+ if (((int) name->name_records[i].offset
+ + name->name_records[i].length) > required)
+ {
+ /* The name is out of bounds! */
+ xfree (name);
+ return NULL;
+ }
+ }
+
+ /* Return the name table. */
+ return name;
+}
+
+/* Return a pointer to the name data corresponding with CODE under the
+ name table NAME. Return the start of the data and the name record
+ under *RECORD upon success, and NULL otherwise. */
+
+TEST_STATIC unsigned char *
+sfnt_find_name (struct sfnt_name_table *name,
+ enum sfnt_name_identifier_code code,
+ struct sfnt_name_record *record)
+{
+ int i;
+
+ for (i = 0; i < name->count; ++i)
+ {
+ if (name->name_records[i].name_id == code)
+ {
+ /* The offsets within have already been validated. */
+ *record = name->name_records[i];
+ return name->data + record->offset;
+ }
+ }
+
+ return NULL;
+}
+
+/* Read the meta table from the given font FD, using the table
+ directory specified as SUBTABLE. Perform validation on the offsets
+ in each metadata record. Return NULL upon failure, else the meta
+ table. */
+
+TEST_STATIC struct sfnt_meta_table *
+sfnt_read_meta_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_meta_table *meta;
+ size_t required, i, data_size, map_size, offset;
+ ssize_t rc;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_META);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum that has to be read. */
+ required = SFNT_ENDOF (struct sfnt_meta_table,
+ num_data_maps, uint32_t);
+
+ if (directory->length < required)
+ return NULL;
+
+ /* Allocate enough to hold it. */
+ meta = xmalloc (sizeof *meta);
+
+ /* Read the header. */
+ rc = read (fd, meta, required);
+ if (rc < required)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Swap what has been read so far. */
+ sfnt_swap32 (&meta->version);
+ sfnt_swap32 (&meta->flags);
+ sfnt_swap32 (&meta->data_offset);
+ sfnt_swap32 (&meta->num_data_maps);
+
+ /* Make sure the meta is supported. */
+ if (meta->version != 1)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Reallocate the table to hold sizeof *meta + meta->num_data_maps
+ times sizeof meta->data_maps + directory->length bytes. This is
+ because it is ok for metadata to point into the data map itself,
+ so an unswapped copy of the whole meta contents must be
+ retained. */
+
+ if (INT_MULTIPLY_WRAPV (sizeof *meta->data_maps, meta->num_data_maps,
+ &map_size)
+ /* Do so while checking for overflow from bad sfnt files. */
+ || INT_ADD_WRAPV (map_size, sizeof *meta, &data_size)
+ || INT_ADD_WRAPV (data_size, directory->length, &data_size))
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Do the reallocation. */
+ meta = xrealloc (meta, data_size);
+
+ /* Check that the remaining data is big enough to hold the data
+ maps. */
+ if (directory->length - required < map_size)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Set pointers to data_maps and data. */
+ meta->data_maps = (struct sfnt_meta_data_map *) (meta + 1);
+ meta->data = (unsigned char *) (meta->data_maps
+ + meta->num_data_maps);
+
+ /* Now, seek back. Read the entire table into meta->data. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ rc = read (fd, meta->data, directory->length);
+ if (rc < directory->length)
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ /* Copy the data maps into meta->data_maps and swap them one by
+ one. */
+ memcpy (meta->data_maps, meta->data + required,
+ map_size);
+
+ for (i = 0; i < meta->num_data_maps; ++i)
+ {
+ sfnt_swap32 (&meta->data_maps[i].tag);
+ sfnt_swap32 (&meta->data_maps[i].data_offset);
+ sfnt_swap32 (&meta->data_maps[i].data_length);
+
+ /* Verify the data offsets. Overflow checking is particularly
+ important here. */
+
+ if (INT_ADD_WRAPV (meta->data_maps[i].data_offset,
+ meta->data_maps[i].data_length,
+ &offset))
+ {
+ xfree (meta);
+ return NULL;
+ }
+
+ if (offset > directory->length)
+ {
+ xfree (meta);
+ return NULL;
+ }
+ }
+
+ /* All done. */
+ return meta;
+}
+
+/* Return a pointer to the metadata corresponding to TAG under the
+ meta table META. Return the start of the data and the metadata map
+ under *MAP upon success, and NULL otherwise. */
+
+MAYBE_UNUSED TEST_STATIC char *
+sfnt_find_metadata (struct sfnt_meta_table *meta,
+ enum sfnt_meta_data_tag tag,
+ struct sfnt_meta_data_map *map)
+{
+ int i;
+
+ for (i = 0; i < meta->num_data_maps; ++i)
+ {
+ if (meta->data_maps[i].tag == tag)
+ {
+ *map = meta->data_maps[i];
+ return (char *) meta->data + map->data_offset;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+/* TrueType collection format support. */
+
+/* Read a TrueType collection header from the font file FD.
+ FD must currently at the start of the file.
+
+ Value is the header upon success, else NULL. */
+
+TEST_STATIC struct sfnt_ttc_header *
+sfnt_read_ttc_header (int fd)
+{
+ struct sfnt_ttc_header *ttc;
+ size_t size, i;
+ ssize_t rc;
+
+ /* First, allocate only as much as required. */
+
+ ttc = xmalloc (sizeof *ttc);
+
+ /* Read the version 1.0 data. */
+
+ size = SFNT_ENDOF (struct sfnt_ttc_header, num_fonts,
+ uint32_t);
+ rc = read (fd, ttc, size);
+ if (rc < size)
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ /* Now swap what was read. */
+ sfnt_swap32 (&ttc->ttctag);
+ sfnt_swap32 (&ttc->version);
+ sfnt_swap32 (&ttc->num_fonts);
+
+ /* Verify that the tag is as expected. */
+ if (ttc->ttctag != SFNT_TTC_TTCF)
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ /* Now, read the variable length data. Make sure to check for
+ overflow. */
+
+ if (INT_MULTIPLY_WRAPV (ttc->num_fonts,
+ sizeof *ttc->offset_table,
+ &size))
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ ttc = xrealloc (ttc, sizeof *ttc + size);
+ ttc->offset_table = (uint32_t *) (ttc + 1);
+ rc = read (fd, ttc->offset_table, size);
+ if (rc < size)
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ /* Swap each of the offsets read. */
+ for (i = 0; i < ttc->num_fonts; ++i)
+ sfnt_swap32 (&ttc->offset_table[i]);
+
+ /* Now, look at the version. If it is earlier than 2.0, then
+ reading is finished. */
+
+ if (ttc->version < 0x00020000)
+ return ttc;
+
+ /* If it is 2.0 or later, then continue to read ul_dsig_tag to
+ ul_dsig_offset. */
+
+ size = (SFNT_ENDOF (struct sfnt_ttc_header, ul_dsig_offset,
+ uint32_t)
+ - offsetof (struct sfnt_ttc_header, ul_dsig_tag));
+ rc = read (fd, &ttc->ul_dsig_offset, size);
+ if (rc < size)
+ {
+ xfree (ttc);
+ return NULL;
+ }
+
+ /* Swap what was read. */
+ sfnt_swap32 (&ttc->ul_dsig_tag);
+ sfnt_swap32 (&ttc->ul_dsig_length);
+ sfnt_swap32 (&ttc->ul_dsig_offset);
+
+ /* All done. */
+ return ttc;
+}
+
+
+
+/* TrueType hinting support.
+
+ If you do not read the code in this section in conjunction with
+ Apple's TrueType Reference Manual, you will get very confused!
+
+ TrueType fonts don't provide simple hinting meta data, unlike Type
+ 2 or CFF fonts.
+
+ Instead, they come with a ``font program'', a bytecode program
+ which is executed upon loading the font, a ``control value
+ program'', executed upon font metrics changing, and then a ``glyph
+ program'' for each glyph, which is run to fit its glyph after
+ scaling.
+
+ The virtual machine which runs this bytecode is arranged as such:
+
+ Firstly, there is a set of registers known as the ``graphics
+ state''. Each time the point size of a font changes, the ``control
+ value program'' is run to establish the default values of the
+ ``graphics state''. Then, before each glyph program is run, the
+ ``graphics state'' is set back to the default values.
+
+ Secondly, there is an address space which contains all instructions
+ being run for the current program, which is addressed by the
+ interpreter through its program counter and also by the
+ instructions which push data on to the stack.
+
+ Thirdly, there is a single stack, from which most instructions take
+ their operands and store data.
+
+ Then, there is some memory set aside for each font, the ``storage
+ area'', which is addressed through the RS[] and WS[] instructions,
+ and a ``control value table'', which is the `cvt ' table of the
+ font.
+
+ And finally, there is a ``glyph zone'' which holds points from a
+ scaled glyph outline, and a ``twilight zone'', which holds points
+ used by the font program itself. Both are addressed indirectly
+ through one of three ``zone pointer'' registers, and are accessible
+ only when a program is being run on behalf of a glyph. */
+
+
+
+/* Functions for reading tables used by the TrueType interpreter. */
+
+/* Read the cvt table (control value table) from the given font FD,
+ using the table directory specified as SUBTABLE. Swap all values
+ in the control value table. Return NULL upon failure, else the cvt
+ table. */
+
+TEST_STATIC struct sfnt_cvt_table *
+sfnt_read_cvt_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ size_t required, i;
+ ssize_t rc;
+ struct sfnt_cvt_table *cvt;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_CVT );
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum amount that has to be read. */
+ if (INT_ADD_WRAPV (sizeof *cvt, directory->length, &required))
+ return NULL;
+
+ /* Allocate enough for that much data. */
+ cvt = xmalloc (required);
+
+ /* Now set cvt->num_elements as appropriate, and make cvt->values
+ point into the values. */
+ cvt->num_elements = directory->length / 2;
+ cvt->values = (sfnt_fword *) (cvt + 1);
+
+ /* Read into cvt. */
+ rc = read (fd, cvt->values, directory->length);
+ if (rc != directory->length)
+ {
+ xfree (cvt);
+ return NULL;
+ }
+
+ /* Swap each element in the control value table. */
+ for (i = 0; i < cvt->num_elements; ++i)
+ sfnt_swap16 (&cvt->values[i]);
+
+ /* All done. */
+ return cvt;
+}
+
+/* Read the fpgm table from the given font FD, using the table
+ directory specified as SUBTABLE. Value is NULL upon failure, else
+ the fpgm table. */
+
+TEST_STATIC struct sfnt_fpgm_table *
+sfnt_read_fpgm_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ size_t required;
+ ssize_t rc;
+ struct sfnt_fpgm_table *fpgm;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_FPGM);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum amount that has to be read. */
+ if (INT_ADD_WRAPV (sizeof *fpgm, directory->length, &required))
+ return NULL;
+
+ /* Allocate enough for that much data. */
+ fpgm = xmalloc (sizeof *fpgm + directory->length);
+
+ /* Now set fpgm->num_instructions as appropriate, and make
+ fpgm->instructions point to the right place. */
+
+ fpgm->num_instructions = directory->length;
+ fpgm->instructions = (unsigned char *) (fpgm + 1);
+
+ /* Read into fpgm. */
+ rc = read (fd, fpgm->instructions, directory->length);
+ if (rc != directory->length)
+ {
+ xfree (fpgm);
+ return NULL;
+ }
+
+ /* All done. */
+ return fpgm;
+}
+
+/* Read the prep table from the given font FD, using the table
+ directory specified as SUBTABLE. Value is NULL upon failure, else
+ the prep table. */
+
+TEST_STATIC struct sfnt_prep_table *
+sfnt_read_prep_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ size_t required;
+ ssize_t rc;
+ struct sfnt_prep_table *prep;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_PREP);
+
+ if (!directory)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Figure out the minimum amount that has to be read. */
+ if (INT_ADD_WRAPV (sizeof *prep, directory->length, &required))
+ return NULL;
+
+ /* Allocate enough for that much data. */
+ prep = xmalloc (sizeof *prep + directory->length);
+
+ /* Now set prep->num_instructions as appropriate, and make
+ prep->instructions point to the right place. */
+
+ prep->num_instructions = directory->length;
+ prep->instructions = (unsigned char *) (prep + 1);
+
+ /* Read into prep. */
+ rc = read (fd, prep->instructions, directory->length);
+ if (rc != directory->length)
+ {
+ xfree (prep);
+ return NULL;
+ }
+
+ /* All done. */
+ return prep;
+}
+
+
+
+/* Interpreter execution environment. */
+
+/* Divide the specified two 26.6 fixed point numbers X and Y.
+ Return the result. */
+
+static sfnt_f26dot6
+sfnt_div_f26dot6 (sfnt_f26dot6 x, sfnt_f26dot6 y)
+{
+#ifdef INT64_MAX
+ int64_t result;
+
+ result = ((int64_t) x * 64) / y;
+
+ return result;
+#else
+ int sign;
+ unsigned int a, b;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ a = abs (x);
+ b = abs (y);
+
+ return sfnt_multiply_divide (a, 64, b) * sign;
+#endif
+}
+
+/* Multiply the specified two 26.6 fixed point numbers A and B.
+ Return the result, or an undefined value upon overflow. */
+
+static sfnt_f26dot6
+sfnt_mul_f26dot6 (sfnt_f26dot6 a, sfnt_f26dot6 b)
+{
+#ifdef INT64_MAX
+ int64_t product;
+
+ product = (int64_t) a * (int64_t) b;
+
+ /* This can be done quickly with int64_t. */
+ return product / (int64_t) 64;
+#else
+ int sign;
+
+ sign = 1;
+
+ if (a < 0)
+ sign = -sign;
+
+ if (b < 0)
+ sign = -sign;
+
+ return sfnt_multiply_divide (abs (a), abs (b),
+ 64) * sign;
+#endif
+}
+
+/* Multiply the specified 2.14 number with another signed 32 bit
+ number. Return the result as a signed 32 bit number. */
+
+static int32_t
+sfnt_mul_f2dot14 (sfnt_f2dot14 a, int32_t b)
+{
+#ifdef INT64_MAX
+ int64_t product;
+
+ product = (int64_t) a * (int64_t) b;
+
+ return product / (int64_t) 16384;
+#else
+ int sign;
+
+ sign = 1;
+
+ if (a < 0)
+ sign = -sign;
+
+ if (b < 0)
+ sign = -sign;
+
+ return sfnt_multiply_divide (abs (a), abs (b),
+ 16384) * sign;
+#endif
+}
+
+/* Multiply the specified 26.6 fixed point number X by the specified
+ 16.16 fixed point number Y with symmetric rounding.
+
+ The 26.6 fixed point number must fit inside -32768 to 32767.ffff.
+ Value is otherwise undefined. */
+
+static sfnt_f26dot6
+sfnt_mul_f26dot6_fixed (sfnt_f26dot6 x, sfnt_fixed y)
+{
+#ifdef INT64_MAX
+ uint64_t product;
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ {
+ x = -x;
+ sign = -sign;
+ }
+
+ if (y < 0)
+ {
+ y = -y;
+ sign = -sign;
+ }
+
+ product = (uint64_t) y * (uint64_t) x;
+
+ /* This can be done quickly with int64_t. */
+ return ((int64_t) (product + 32676) / (int64_t) 65536) * sign;
+#else
+ struct sfnt_large_integer temp;
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ sign = -sign;
+
+ if (y < 0)
+ sign = -sign;
+
+ sfnt_multiply_divide_1 (abs (x), abs (y), &temp);
+ sfnt_large_integer_add (&temp, 32676);
+ return sfnt_multiply_divide_2 (&temp, 65536) * sign;
+#endif
+}
+
+/* Return the floor of the specified 26.6 fixed point value X. */
+
+static sfnt_f26dot6
+sfnt_floor_f26dot6 (sfnt_f26dot6 x)
+{
+ return x & 037777777700;
+}
+
+/* Return the ceiling of the specified 26.6 fixed point value X. */
+
+static sfnt_f26dot6
+sfnt_ceil_f26dot6 (sfnt_f26dot6 x)
+{
+ return (x + 077) & ~077;
+}
+
+/* Return the 26.6 fixed point value X rounded to the nearest integer
+ value. */
+
+static sfnt_f26dot6
+sfnt_round_f26dot6 (sfnt_f26dot6 x)
+{
+ /* Add 0.5. */
+ x += 040;
+
+ /* Remove the fractional. */
+ return x & ~077;
+}
+
+/* Needed by sfnt_init_graphics_state. */
+
+static void sfnt_validate_gs (struct sfnt_graphics_state *);
+
+/* Set up default values for the interpreter graphics state. Return
+ them in STATE. */
+
+static void
+sfnt_init_graphics_state (struct sfnt_graphics_state *state)
+{
+ state->auto_flip = true;
+ state->cvt_cut_in = 0104;
+ state->delta_base = 9;
+ state->delta_shift = 3;
+ state->dual_projection_vector.x = 040000; /* 1.0 */
+ state->dual_projection_vector.y = 0;
+ state->freedom_vector.x = 040000; /* 1.0 */
+ state->freedom_vector.y = 0;
+ state->instruct_control = 0;
+ state->loop = 1;
+ state->minimum_distance = 0100;
+ state->projection_vector.x = 040000; /* 1.0 */
+ state->projection_vector.y = 0;
+ state->round_state = 1;
+ state->rp0 = 0;
+ state->rp1 = 0;
+ state->rp2 = 0;
+ state->scan_control = 0;
+ state->sw_cut_in = 0;
+ state->single_width_value = 0;
+ state->zp0 = 1;
+ state->zp1 = 1;
+ state->zp2 = 1;
+
+ /* Validate the graphics state. */
+ sfnt_validate_gs (state);
+}
+
+/* Set up an interpreter to be used with a font. Use the resource
+ limits specified in the MAXP table, the values specified in the
+ CVT, HEAD and FVAR tables, the pixel size PIXEL_SIZE, and the point
+ size POINT_SIZE. CVT may be NULL, in which case the interpreter
+ will not have access to a control value table.
+
+ POINT_SIZE should be PIXEL_SIZE, converted to 1/72ths of an inch.
+
+ Value is the interpreter, with all state initialized to default
+ values, or NULL upon failure. */
+
+TEST_STATIC struct sfnt_interpreter *
+sfnt_make_interpreter (struct sfnt_maxp_table *maxp,
+ struct sfnt_cvt_table *cvt,
+ struct sfnt_head_table *head,
+ struct sfnt_fvar_table *fvar,
+ int pixel_size, int point_size)
+{
+ size_t size, temp, i, storage_size, pad;
+ struct sfnt_interpreter *interpreter;
+
+ /* Detect CFF maxp tables. */
+ if (maxp->version != 0x00010000)
+ return NULL;
+
+ /* Use the contents of the MAXP table to determine the size of the
+ interpreter structure. */
+ size = sizeof (*interpreter);
+
+ /* Add program stack. */
+ if (INT_ADD_WRAPV ((maxp->max_stack_elements
+ * sizeof *interpreter->stack),
+ size, &size))
+ return NULL;
+
+ /* Add twilight zone. */
+
+ if (INT_ADD_WRAPV ((maxp->max_twilight_points
+ * sizeof *interpreter->twilight_x),
+ size, &size))
+ return NULL;
+
+ if (INT_ADD_WRAPV ((maxp->max_twilight_points
+ * sizeof *interpreter->twilight_y),
+ size, &size))
+ return NULL;
+
+ if (INT_ADD_WRAPV ((maxp->max_twilight_points
+ * sizeof *interpreter->twilight_y),
+ size, &size))
+ return NULL;
+
+ if (INT_ADD_WRAPV ((maxp->max_twilight_points
+ * sizeof *interpreter->twilight_y),
+ size, &size))
+ return NULL;
+
+ /* Add the storage area. */
+ storage_size = maxp->max_storage * sizeof *interpreter->storage;
+ if (INT_ADD_WRAPV (storage_size, size, &size))
+ return NULL;
+
+ /* Add padding for the storage area. */
+ pad = alignof (struct sfnt_interpreter_definition);
+ pad -= size & (pad - 1);
+ if (INT_ADD_WRAPV (pad, size, &size))
+ return NULL;
+
+ /* Add function and instruction definitions. */
+ if (INT_ADD_WRAPV ((((int) maxp->max_instruction_defs
+ + maxp->max_function_defs)
+ * sizeof *interpreter->function_defs),
+ size, &size))
+ return NULL;
+
+ /* Add control value table. */
+
+ if (cvt)
+ {
+ if (INT_MULTIPLY_WRAPV (cvt->num_elements,
+ sizeof *interpreter->cvt,
+ &temp)
+ || INT_ADD_WRAPV (temp, size, &size))
+ return NULL;
+ }
+
+ /* Allocate the interpreter. */
+ interpreter = xmalloc (size);
+
+#ifdef TEST
+ interpreter->run_hook = NULL;
+ interpreter->push_hook = NULL;
+ interpreter->pop_hook = NULL;
+#endif
+
+ /* Fill in pointers and default values. */
+ interpreter->max_stack_elements = maxp->max_stack_elements;
+ interpreter->num_instructions = 0;
+ interpreter->IP = 0;
+ interpreter->storage_size = maxp->max_storage;
+ interpreter->function_defs_size = maxp->max_function_defs;
+ interpreter->instruction_defs_size = maxp->max_instruction_defs;
+ interpreter->twilight_zone_size = maxp->max_twilight_points;
+ interpreter->scale = 0; /* This should be set later. */
+
+ interpreter->stack = (uint32_t *) (interpreter + 1);
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = NULL;
+ interpreter->twilight_x
+ = (sfnt_f26dot6 *) (interpreter->stack
+ + maxp->max_stack_elements);
+ interpreter->twilight_y = (interpreter->twilight_x
+ + maxp->max_twilight_points);
+ interpreter->twilight_original_x = (interpreter->twilight_y
+ + maxp->max_twilight_points);
+ interpreter->twilight_original_y
+ = (interpreter->twilight_original_x
+ + maxp->max_twilight_points);
+ interpreter->glyph_zone = NULL;
+ interpreter->advance_width = 0;
+ interpreter->storage
+ = (uint32_t *) (interpreter->twilight_original_y
+ + maxp->max_twilight_points);
+ interpreter->function_defs
+ = (struct sfnt_interpreter_definition *) (interpreter->storage
+ + maxp->max_storage);
+ interpreter->function_defs
+ = ((struct sfnt_interpreter_definition *)
+ ((unsigned char *) interpreter->function_defs + pad));
+ interpreter->instruction_defs = (interpreter->function_defs
+ + maxp->max_function_defs);
+ interpreter->cvt
+ = (sfnt_f26dot6 *) (interpreter->instruction_defs
+ + maxp->max_instruction_defs);
+
+ if (cvt)
+ interpreter->cvt_size = cvt->num_elements;
+ else
+ interpreter->cvt_size = 0;
+
+ /* Now compute the scale. Then, scale up the control value table
+ values. */
+ interpreter->scale
+ = sfnt_div_fixed (pixel_size, head->units_per_em);
+
+ /* Set the PPEM. */
+ interpreter->ppem = pixel_size;
+ interpreter->point_size = point_size;
+
+ /* Zero out the interpreter state from the stack to the end of the
+ instruction definitions. */
+ memset (interpreter->stack, 0, size - sizeof *interpreter);
+
+ /* Initialize the interpreter graphics state. */
+ sfnt_init_graphics_state (&interpreter->state);
+
+ /* Load the control value table. */
+ for (i = 0; i < interpreter->cvt_size; ++i)
+ interpreter->cvt[i]
+ = sfnt_mul_f26dot6_fixed (cvt->values[i] * 64,
+ interpreter->scale);
+
+ /* Fill in the default values for phase, period and threshold. */
+ interpreter->period = 64;
+ interpreter->phase = 0;
+ interpreter->threshold = 0;
+
+ /* Fill in the current call depth. */
+ interpreter->call_depth = 0;
+
+ /* Clear variation axes. They will be set upon a call to
+ `sfnt_vary_interpreter'. */
+ interpreter->n_axis = 0;
+ interpreter->norm_coords = NULL;
+
+ /* Set n_axis now if a fvar table was provided. This way, GXAXIS
+ pushes the correct number of values even if no blend is
+ provided. */
+
+ if (fvar)
+ interpreter->n_axis = fvar->axis_count;
+
+ /* Return the interpreter. */
+ return interpreter;
+}
+
+/* These enums are used to determine why the interpreter is being
+ run. They have the following meanings:
+
+ - The interpreter is being run to interpret a font program.
+ - The interpreter is being run to interpret the control
+ value program.
+ - The interpreter is being run to fit a glyph.
+ - The interpreter is being run as part of an automated test. */
+
+enum sfnt_interpreter_run_context
+ {
+ SFNT_RUN_CONTEXT_FONT_PROGRAM,
+ SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM,
+ SFNT_RUN_CONTEXT_GLYPH_PROGRAM,
+#ifdef TEST
+ SFNT_RUN_CONTEXT_TEST,
+#endif
+ };
+
+/* Cancel execution of the program in INTERPRETER with the specified
+ error REASON and reset the loop counter to 1.
+
+ After this is called, it is probably okay to reuse INTERPRETER.
+ However, instructions must always be reloaded. */
+
+_Noreturn static void
+sfnt_interpret_trap (struct sfnt_interpreter *interpreter,
+ const char *reason)
+{
+ interpreter->trap_reason = reason;
+ interpreter->call_depth = 0;
+ interpreter->state.loop = 1;
+ longjmp (interpreter->trap, 1);
+}
+
+#define STACKSIZE() \
+ (interpreter->SP - interpreter->stack)
+
+#define TRAP(why) \
+ sfnt_interpret_trap (interpreter, (why))
+
+#define MOVE(a, b, n) \
+ memmove (a, b, (n) * sizeof (uint32_t))
+
+#define CHECK_STACK_ELEMENTS(n) \
+ { \
+ if ((interpreter->SP \
+ - interpreter->stack) < n) \
+ TRAP ("stack underflow"); \
+ }
+
+#define CHECK_PREP() \
+ if (!is_prep) \
+ TRAP ("instruction executed not valid" \
+ " outside control value program") \
+
+#define sfnt_add(a, b) \
+ ((int) ((unsigned int) (a) + (unsigned int) (b)))
+
+#define sfnt_sub(a, b) \
+ ((int) ((unsigned int) (a) - (unsigned int) (b)))
+
+#define sfnt_mul(a, b) \
+ ((int) ((unsigned int) (a) * (unsigned int) (b)))
+
+
+
+/* Register, alu and logic instructions. */
+
+#ifndef TEST
+
+#define POP() \
+ (interpreter->SP == interpreter->stack \
+ ? (TRAP ("stack underflow"), 0) \
+ : (*(--interpreter->SP)))
+
+#define POP_UNCHECKED() (*(--interpreter->SP))
+
+#else
+
+#define POP() \
+ (interpreter->SP == interpreter->stack \
+ ? (TRAP ("stack underflow"), 0) \
+ : ({uint32_t _value; \
+ _value = *(--interpreter->SP); \
+ if (interpreter->pop_hook) \
+ interpreter->pop_hook (interpreter, \
+ _value); \
+ _value;}))
+
+#define POP_UNCHECKED() POP ()
+
+#endif
+
+#define LOOK() \
+ (interpreter->SP == interpreter->stack \
+ ? (TRAP ("stack underflow"), 0) \
+ : *(interpreter->SP - 1))
+
+#ifndef TEST
+
+#define PUSH(value) \
+ { \
+ if ((char *) (interpreter->SP + 1) \
+ > (char *) interpreter->twilight_x) \
+ TRAP ("stack overflow"); \
+ \
+ *interpreter->SP = (value); \
+ interpreter->SP++; \
+ }
+
+#define PUSH_UNCHECKED(value) \
+ { \
+ *interpreter->SP = (value); \
+ interpreter->SP++; \
+ }
+
+#else
+
+#define PUSH(value) \
+ { \
+ if ((char *) (interpreter->SP + 1) \
+ > (char *) interpreter->twilight_x) \
+ TRAP ("stack overflow"); \
+ \
+ if (interpreter->push_hook) \
+ interpreter->push_hook (interpreter, \
+ value); \
+ \
+ *interpreter->SP = value; \
+ interpreter->SP++; \
+ }
+
+#define PUSH_UNCHECKED(value) PUSH (value)
+
+#endif
+
+#define PUSH2(high, low) \
+ { \
+ PUSH ((int16_t) ((int8_t) high) << 8 \
+ | low); \
+ } \
+
+#define SRP0() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ interpreter->state.rp0 = p; \
+ }
+
+#define SRP1() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ interpreter->state.rp1 = p; \
+ }
+
+#define SRP2() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ interpreter->state.rp2 = p; \
+ }
+
+#define SZP0() \
+ { \
+ uint32_t zone; \
+ \
+ zone = POP (); \
+ \
+ if (zone > 1) \
+ TRAP ("invalid zone"); \
+ \
+ interpreter->state.zp0 = zone; \
+ }
+
+#define SZP1() \
+ { \
+ uint32_t zone; \
+ \
+ zone = POP (); \
+ \
+ if (zone > 1) \
+ TRAP ("invalid zone"); \
+ \
+ interpreter->state.zp1 = zone; \
+ }
+
+#define SZP2() \
+ { \
+ uint32_t zone; \
+ \
+ zone = POP (); \
+ \
+ if (zone > 1) \
+ TRAP ("invalid zone"); \
+ \
+ interpreter->state.zp2 = zone; \
+ }
+
+#define SZPS() \
+ { \
+ uint32_t zone; \
+ \
+ zone = POP (); \
+ \
+ if (zone > 1) \
+ TRAP ("invalid zone"); \
+ \
+ interpreter->state.zp0 = zone; \
+ interpreter->state.zp1 = zone; \
+ interpreter->state.zp2 = zone; \
+ }
+
+#define SLOOP() \
+ { \
+ uint32_t loop; \
+ \
+ loop = POP (); \
+ \
+ if (!loop) \
+ TRAP ("loop set to 0"); \
+ \
+ interpreter->state.loop = loop; \
+ }
+
+#define SMD() \
+ { \
+ sfnt_f26dot6 md; \
+ \
+ md = POP (); \
+ \
+ interpreter->state.minimum_distance = md; \
+ }
+
+#define ELSE() \
+ { \
+ sfnt_interpret_else (interpreter); \
+ goto skip_step; \
+ }
+
+#define JMPR() \
+ { \
+ int32_t offset; \
+ \
+ offset = POP (); \
+ \
+ if (interpreter->IP + offset < 0 \
+ || (interpreter->IP + offset \
+ > interpreter->num_instructions)) \
+ TRAP ("JMPR out of bounds"); \
+ \
+ interpreter->IP += offset; \
+ goto skip_step; \
+ }
+
+#define SCVTCI() \
+ { \
+ sfnt_f26dot6 cutin; \
+ \
+ cutin = POP (); \
+ \
+ interpreter->state.cvt_cut_in = cutin; \
+ }
+
+#define SSWCI() \
+ { \
+ sfnt_f26dot6 cutin; \
+ \
+ cutin = POP (); \
+ \
+ interpreter->state.sw_cut_in = cutin; \
+ }
+
+#define SSW() \
+ { \
+ int32_t single_width; \
+ \
+ single_width = POP (); \
+ \
+ interpreter->state.single_width_value \
+ = (interpreter->scale * single_width \
+ / 1024); \
+ }
+
+#define DUP() \
+ { \
+ uint32_t value; \
+ \
+ value = LOOK (); \
+ PUSH (value); \
+ }
+
+#define CLEAR() \
+ { \
+ interpreter->SP = interpreter->stack; \
+ }
+
+#define SWAP() \
+ { \
+ uint32_t a, b; \
+ \
+ a = POP (); \
+ b = POP (); \
+ \
+ PUSH_UNCHECKED (a); \
+ PUSH_UNCHECKED (b); \
+ }
+
+#define DEPTH() \
+ { \
+ ptrdiff_t diff; \
+ \
+ diff = (interpreter->SP \
+ - interpreter->stack); \
+ PUSH (diff); \
+ }
+
+#define CINDEX() \
+ { \
+ int32_t index; \
+ \
+ index = POP (); \
+ \
+ if (index <= 0 || index > STACKSIZE ()) \
+ TRAP ("stack overflow"); \
+ \
+ PUSH_UNCHECKED (*(interpreter->SP \
+ - index)); \
+ }
+
+#define MINDEX() \
+ { \
+ int32_t index, what; \
+ \
+ index = POP (); \
+ \
+ if (index <= 0 || index > STACKSIZE ()) \
+ TRAP ("stack overflow"); \
+ \
+ what = *(interpreter->SP - index); \
+ MOVE (interpreter->SP - index, \
+ interpreter->SP - index + 1, \
+ index - 1); \
+ *(interpreter->SP - 1) = what; \
+ }
+
+#define RAW() \
+ { \
+ if (why != SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
+ TRAP ("Read Advance Width without loaded" \
+ " glyph"); \
+ PUSH (interpreter->advance_width); \
+ }
+
+#define CALL() \
+ { \
+ uint32_t id, i; \
+ struct sfnt_interpreter_definition *def; \
+ \
+ id = POP (); \
+ \
+ for (i = 0; \
+ i < interpreter->function_defs_size; \
+ ++i) \
+ { \
+ def = &interpreter->function_defs[i]; \
+ \
+ if (!def->instructions) \
+ TRAP ("invalid function"); \
+ \
+ if (def->opcode == id) \
+ sfnt_interpret_call (def, \
+ interpreter, \
+ why); \
+ if (def->opcode == id) \
+ break; \
+ } \
+ \
+ if (i == interpreter->function_defs_size) \
+ TRAP ("invalid function"); \
+ }
+
+#define LOOPCALL() \
+ { \
+ uint32_t id, i; \
+ int32_t n; \
+ struct sfnt_interpreter_definition *def; \
+ \
+ id = POP (); \
+ n = POP (); \
+ def = NULL; /* Pacify -fanalyzer. */ \
+ \
+ if (n > 65535) \
+ TRAP ("invalid LOOPCALL count"); \
+ \
+ for (i = 0; \
+ i < interpreter->function_defs_size; \
+ ++i) \
+ { \
+ def = &interpreter->function_defs[i]; \
+ \
+ if (!def->instructions) \
+ TRAP ("invalid function"); \
+ \
+ if (def->opcode == id) \
+ break; \
+ } \
+ \
+ if (i == interpreter->function_defs_size) \
+ TRAP ("invalid function"); \
+ \
+ loopcall_begin: \
+ if (n-- <= 0) \
+ break; \
+ \
+ sfnt_interpret_call (def, interpreter, \
+ why); \
+ goto loopcall_begin; \
+ }
+
+#define FDEF() \
+ { \
+ if (why == SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
+ TRAP ("FDEF inside glyph program"); \
+ \
+ sfnt_interpret_fdef (interpreter, POP ()); \
+ goto skip_step; \
+ }
+
+#define ENDF() \
+ { \
+ TRAP ("stray ENDF"); \
+ }
+
+#define NPUSHB() \
+ { \
+ int b, nbytes, IP; \
+ \
+ if ((IP = interpreter->IP + 1) \
+ >= interpreter->num_instructions) \
+ TRAP ("Missing arg to NPUSHB"); \
+ \
+ nbytes \
+ = interpreter->instructions[IP]; \
+ \
+ if (IP + 1 + nbytes \
+ > interpreter->num_instructions) \
+ TRAP ("args to NPUSHB lie outside IS"); \
+ \
+ for (b = IP + 1; b < IP + 1 + nbytes; ++b) \
+ PUSH (interpreter->instructions[b]); \
+ \
+ interpreter->IP += nbytes + 1; \
+ }
+
+#define NPUSHW() \
+ { \
+ int b, nbytes, IP; \
+ \
+ if ((IP = interpreter->IP + 1) \
+ >= interpreter->num_instructions) \
+ TRAP ("Missing arg to NPUSHW"); \
+ \
+ nbytes \
+ = interpreter->instructions[IP] * 2; \
+ \
+ if (IP + 1 + nbytes \
+ > interpreter->num_instructions) \
+ TRAP ("args to NPUSHW lie outside IS"); \
+ \
+ for (b = IP + 1; b < IP + 1 + nbytes; \
+ b += 2) \
+ PUSH2 (interpreter->instructions[b], \
+ interpreter->instructions[b + 1]); \
+ \
+ interpreter->IP += nbytes + 1; \
+ }
+
+#define WS() \
+ { \
+ uint32_t address, value; \
+ \
+ value = POP (); \
+ address = POP (); \
+ \
+ if (address >= interpreter->storage_size) \
+ TRAP ("invalid WS"); \
+ \
+ interpreter->storage[address] = value; \
+ }
+
+#define RS() \
+ { \
+ uint32_t address, value; \
+ \
+ address = POP (); \
+ \
+ if (address >= interpreter->storage_size) \
+ TRAP ("invalid RS"); \
+ \
+ value = interpreter->storage[address]; \
+ PUSH_UNCHECKED (value); \
+ }
+
+#define WCVTP() \
+ { \
+ sfnt_f26dot6 value; \
+ uint32_t location; \
+ \
+ value = POP (); \
+ location = POP (); \
+ \
+ if (location >= interpreter->cvt_size) \
+ TRAP ("WCVTP out of bounds"); \
+ \
+ interpreter->cvt[location] = value; \
+ }
+
+#define RCVT() \
+ { \
+ sfnt_f26dot6 value; \
+ uint32_t location; \
+ \
+ location = POP (); \
+ \
+ if (location >= interpreter->cvt_size) \
+ TRAP ("out of bounds RCVT"); \
+ \
+ value = interpreter->cvt[location]; \
+ PUSH_UNCHECKED (value); \
+ }
+
+#define MPPEM() \
+ { \
+ PUSH (interpreter->ppem); \
+ }
+
+#define MPS() \
+ { \
+ PUSH (interpreter->point_size); \
+ }
+
+#define FLIPON() \
+ { \
+ interpreter->state.auto_flip = true; \
+ }
+
+#define FLIPOFF() \
+ { \
+ interpreter->state.auto_flip = false; \
+ }
+
+#define DEBUG() \
+ { \
+ POP (); /* Value is ignored. */ \
+ }
+
+#define LT() \
+ { \
+ int32_t e1, e2; \
+ \
+ e2 = POP (); \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 < e2 ? 1 : 0); \
+ }
+
+#define LTEQ() \
+ { \
+ int32_t e1, e2; \
+ \
+ e2 = POP (); \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 <= e2 ? 1 : 0); \
+ }
+
+#define GT() \
+ { \
+ int32_t e1, e2; \
+ \
+ e2 = POP (); \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 > e2 ? 1 : 0); \
+ }
+
+#define GTEQ() \
+ { \
+ int32_t e1, e2; \
+ \
+ e2 = POP (); \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 >= e2 ? 1 : 0); \
+ }
+
+#define EQ() \
+ { \
+ uint32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 == e2 ? 1 : 0); \
+ }
+
+#define NEQ() \
+ { \
+ uint32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 != e2 ? 1 : 0); \
+ }
+
+#define ODD() \
+ { \
+ sfnt_f26dot6 e1, result; \
+ \
+ e1 = POP (); \
+ result = abs (e1); \
+ \
+ result \
+ = interpreter->state.round (result, \
+ interpreter); \
+ PUSH_UNCHECKED (((result & 127) \
+ == 64) ? 1 : 0); \
+ }
+
+#define EVEN() \
+ { \
+ sfnt_f26dot6 e1, result; \
+ uint32_t value; \
+ \
+ e1 = POP (); \
+ result = abs (e1); \
+ \
+ result \
+ = interpreter->state.round (result, \
+ interpreter); \
+ value = ((result & 127) == 64) ? 0 : 1; \
+ PUSH_UNCHECKED (value); \
+ }
+
+#define IF() \
+ { \
+ uint32_t condition; \
+ \
+ condition = POP (); \
+ sfnt_interpret_if (interpreter, condition); \
+ goto skip_step; \
+ }
+
+#define EIF() \
+ { \
+ \
+ }
+
+#define AND() \
+ { \
+ uint32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 && e2 ? 1 : 0); \
+ }
+
+#define OR() \
+ { \
+ uint32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (e1 || e2 ? 1 : 0); \
+ }
+
+#define NOT() \
+ { \
+ uint32_t e1; \
+ \
+ e1 = POP (); \
+ \
+ PUSH_UNCHECKED (!e1 ? 1 : 0); \
+ }
+
+#define SDB() \
+ { \
+ uint32_t base; \
+ \
+ base = POP (); \
+ \
+ interpreter->state.delta_base = base; \
+ }
+
+#define SDS() \
+ { \
+ uint32_t shift; \
+ \
+ shift = POP (); \
+ \
+ if (shift > 6) \
+ TRAP ("invalid delta shift"); \
+ \
+ interpreter->state.delta_shift = shift; \
+ }
+
+#define ADD() \
+ { \
+ sfnt_f26dot6 n1, n2; \
+ \
+ n1 = POP (); \
+ n2 = POP (); \
+ \
+ PUSH_UNCHECKED (sfnt_add (n1, n2)); \
+ }
+
+#define SUB() \
+ { \
+ sfnt_f26dot6 n2, n1; \
+ \
+ n2 = POP (); \
+ n1 = POP (); \
+ \
+ PUSH_UNCHECKED (sfnt_sub (n1, n2)); \
+ }
+
+#define DIV() \
+ { \
+ sfnt_f26dot6 n2, n1; \
+ \
+ n2 = POP (); \
+ n1 = POP (); \
+ \
+ if (!n2) \
+ TRAP ("DIV by 0"); \
+ \
+ PUSH_UNCHECKED (sfnt_div_f26dot6 (n1, n2)); \
+ }
+
+#define MUL() \
+ { \
+ sfnt_f26dot6 n2, n1; \
+ \
+ n2 = POP (); \
+ n1 = POP (); \
+ \
+ PUSH_UNCHECKED (sfnt_mul_f26dot6 (n2, n1)); \
+ }
+
+#define ABS() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ \
+ if (n == INT32_MIN) \
+ PUSH_UNCHECKED (0) \
+ else \
+ PUSH_UNCHECKED (n < 0 ? -n : n) \
+ }
+
+#define NEG() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ \
+ if (n == INT32_MIN) \
+ PUSH_UNCHECKED (0) \
+ else \
+ PUSH_UNCHECKED (-n) \
+ }
+
+#define FLOOR() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ PUSH_UNCHECKED (sfnt_floor_f26dot6 (n)); \
+ }
+
+#define CEILING() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ PUSH_UNCHECKED (sfnt_ceil_f26dot6 (n)); \
+ }
+
+#define WCVTF() \
+ { \
+ int32_t value; \
+ uint32_t location; \
+ \
+ value = POP (); \
+ location = POP (); \
+ \
+ if (location >= interpreter->cvt_size) \
+ TRAP ("WCVTF out of bounds"); \
+ \
+ interpreter->cvt[location] \
+ = (interpreter->scale * value \
+ / 1024); \
+ }
+
+#define JROT() \
+ { \
+ uint32_t e; \
+ int32_t offset; \
+ \
+ e = POP (); \
+ offset = POP (); \
+ \
+ if (!e) \
+ break; \
+ \
+ if (interpreter->IP + offset < 0 \
+ || (interpreter->IP + offset \
+ > interpreter->num_instructions)) \
+ TRAP ("JMPR out of bounds"); \
+ \
+ interpreter->IP += offset; \
+ goto skip_step; \
+ }
+
+#define JROF() \
+ { \
+ uint32_t e; \
+ int32_t offset; \
+ \
+ e = POP (); \
+ offset = POP (); \
+ \
+ if (e) \
+ break; \
+ \
+ if (interpreter->IP + offset < 0 \
+ || (interpreter->IP + offset \
+ > interpreter->num_instructions)) \
+ TRAP ("JMPR out of bounds"); \
+ \
+ interpreter->IP += offset; \
+ goto skip_step; \
+ }
+
+#define ILLEGAL_INSTRUCTION() \
+ { \
+ TRAP ("MS reserved illegal instruction"); \
+ }
+
+#define SCANCTRL() \
+ { \
+ uint32_t value; \
+ \
+ value = POP (); \
+ interpreter->state.scan_control = value; \
+ }
+
+/* Selector bit 8 is undocumented, but present in the Macintosh
+ rasterizer. 02000 is returned if there is a variation axis in
+ use. */
+
+#define GETINFO() \
+ { \
+ uint32_t selector, k; \
+ \
+ selector = POP (); \
+ \
+ k = 0; \
+ \
+ if (selector & 1) \
+ k |= 02; \
+ \
+ if (selector & 8 \
+ && interpreter->norm_coords) \
+ k |= 02000; \
+ \
+ PUSH_UNCHECKED (k); \
+ }
+
+#define IDEF() \
+ { \
+ if (why == SFNT_RUN_CONTEXT_GLYPH_PROGRAM) \
+ TRAP ("IDEF inside glyph program"); \
+ \
+ sfnt_interpret_idef (interpreter, POP ()); \
+ goto skip_step; \
+ }
+
+#define ROLL() \
+ { \
+ uint32_t a, b, c; \
+ \
+ CHECK_STACK_ELEMENTS (3); \
+ \
+ a = POP_UNCHECKED (); \
+ b = POP_UNCHECKED (); \
+ c = POP_UNCHECKED (); \
+ \
+ PUSH_UNCHECKED (b); \
+ PUSH_UNCHECKED (a); \
+ PUSH_UNCHECKED (c); \
+ }
+
+#define _MAX() \
+ { \
+ int32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (MAX (e1, e2)); \
+ }
+
+#define _MIN() \
+ { \
+ int32_t e1, e2; \
+ \
+ e1 = POP (); \
+ e2 = POP (); \
+ \
+ PUSH_UNCHECKED (MIN (e1, e2)); \
+ }
+
+#define SCANTYPE() \
+ { \
+ POP (); \
+ }
+
+#define INSTCTRL() \
+ { \
+ uint32_t s, v; \
+ \
+ CHECK_PREP (); \
+ s = POP (); \
+ v = POP (); \
+ \
+ if (!s || s > 2) \
+ break; \
+ \
+ interpreter->state.instruct_control \
+ &= ~(1 << s); \
+ \
+ if (v) \
+ interpreter->state.instruct_control \
+ |= (1 << s); \
+ }
+
+/* GXAXIS is undocumented. It seems to return each axis in shortFrac
+ format. */
+
+#define GXAXIS() \
+ { \
+ uint32_t v; \
+ int i; \
+ \
+ for (i = 0; i < interpreter->n_axis; ++i) \
+ { \
+ if (interpreter->norm_coords) \
+ v = interpreter->norm_coords[i] / 4; \
+ else \
+ v = 0; \
+ \
+ PUSH (v); \
+ } \
+ }
+
+#define PUSHB() \
+ { \
+ int b, nbytes, IP; \
+ \
+ IP = interpreter->IP; \
+ nbytes = opcode - 0xb0 + 1; \
+ \
+ if (IP + nbytes + 1 \
+ > interpreter->num_instructions) \
+ TRAP ("args to PUSHB lie outside IS"); \
+ \
+ for (b = IP + 1; b < IP + nbytes + 1; ++b) \
+ PUSH (interpreter->instructions[b]); \
+ \
+ interpreter->IP += nbytes; \
+ }
+
+#define PUSHW() \
+ { \
+ int b, nbytes, IP; \
+ \
+ IP = interpreter->IP; \
+ nbytes = (opcode - 0xb8 + 1) * 2; \
+ \
+ if (IP + 1 + nbytes \
+ > interpreter->num_instructions) \
+ TRAP ("args to PUSHW lie outside IS"); \
+ \
+ for (b = IP + 1; b < IP + nbytes + 1; \
+ b += 2) \
+ PUSH2 (interpreter->instructions[b], \
+ interpreter->instructions[b + 1]); \
+ \
+ interpreter->IP += nbytes; \
+ }
+
+
+
+/* Rounding instructions. */
+
+#define ROUND() \
+ { \
+ sfnt_f26dot6 n, result; \
+ \
+ n = POP (); \
+ result = abs (n); \
+ \
+ result \
+ = interpreter->state.round (result, \
+ interpreter); \
+ PUSH_UNCHECKED (n < 0 ? -result : result); \
+ }
+
+#define NROUND() \
+ { \
+ sfnt_f26dot6 n; \
+ \
+ n = POP (); \
+ PUSH_UNCHECKED (n); \
+ }
+
+#define ROFF() \
+ { \
+ interpreter->state.round_state = 5; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RUTG() \
+ { \
+ interpreter->state.round_state = 4; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RDTG() \
+ { \
+ interpreter->state.round_state = 3; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RTG() \
+ { \
+ interpreter->state.round_state = 1; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RTHG() \
+ { \
+ interpreter->state.round_state = 0; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define RTDG() \
+ { \
+ interpreter->state.round_state = 2; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define SROUND() \
+ { \
+ uint32_t operand; \
+ \
+ operand = POP (); \
+ sfnt_set_srounding_state (interpreter, \
+ operand, \
+ 0x4000); \
+ interpreter->state.round_state = 6; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define S45ROUND() \
+ { \
+ uint32_t operand; \
+ \
+ operand = POP (); \
+ sfnt_set_srounding_state (interpreter, \
+ operand, \
+ 0x5a82); \
+ interpreter->state.round_state = 7; \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+
+
+/* CVT and point delta exception instructions.
+
+ ``Exceptions'' can be placed directly inside the control value
+ table, as it is reloaded every time the point size changes. */
+
+#define DELTAC1() \
+ { \
+ uint32_t operand1, operand2, n; \
+ \
+ n = POP (); \
+ \
+ deltac1_start: \
+ if (!n) \
+ break; \
+ \
+ operand1 = POP (); \
+ operand2 = POP (); \
+ sfnt_deltac (1, interpreter, operand1, \
+ operand2); \
+ n--; \
+ goto deltac1_start; \
+ }
+
+#define DELTAC2() \
+ { \
+ uint32_t operand1, operand2, n; \
+ \
+ n = POP (); \
+ \
+ deltac2_start: \
+ if (!n) \
+ break; \
+ \
+ operand1 = POP (); \
+ operand2 = POP (); \
+ sfnt_deltac (2, interpreter, operand1, \
+ operand2); \
+ n--; \
+ goto deltac2_start; \
+ }
+
+#define DELTAC3() \
+ { \
+ uint32_t operand1, operand2, n; \
+ \
+ n = POP (); \
+ \
+ deltac3_start: \
+ if (!n) \
+ break; \
+ \
+ operand1 = POP (); \
+ operand2 = POP (); \
+ sfnt_deltac (3, interpreter, operand1, \
+ operand2); \
+ n--; \
+ goto deltac3_start; \
+ }
+
+#define DELTAP1() \
+ { \
+ uint32_t n, argn, pn; \
+ \
+ n = POP (); \
+ \
+ deltap1_start: \
+ if (!n) \
+ break; \
+ \
+ pn = POP (); \
+ argn = POP (); \
+ sfnt_deltap (1, interpreter, argn, pn); \
+ n--; \
+ goto deltap1_start; \
+ }
+
+#define DELTAP2() \
+ { \
+ uint32_t n, argn, pn; \
+ \
+ n = POP (); \
+ \
+ deltap2_start: \
+ if (!n) \
+ break; \
+ \
+ pn = POP (); \
+ argn = POP (); \
+ sfnt_deltap (2, interpreter, argn, pn); \
+ n--; \
+ goto deltap2_start; \
+ }
+
+#define DELTAP3() \
+ { \
+ uint32_t n, argn, pn; \
+ \
+ n = POP (); \
+ \
+ deltap3_start: \
+ if (!n) \
+ break; \
+ \
+ pn = POP (); \
+ argn = POP (); \
+ sfnt_deltap (3, interpreter, argn, pn); \
+ n--; \
+ goto deltap3_start; \
+ }
+
+
+
+/* Anachronistic angle instructions. */
+
+#define AA() \
+ { \
+ POP (); \
+ }
+
+#define SANGW() \
+ { \
+ POP (); \
+ }
+
+
+
+/* Projection and freedom vector operations. */
+
+#define PROJECT(x, y) \
+ sfnt_project_vector (interpreter, x, y)
+
+#define DUAL_PROJECT(x, y) \
+ sfnt_dual_project_vector (interpreter, x, y)
+
+#define SVTCAy() \
+ { \
+ sfnt_set_freedom_vector (interpreter, \
+ 0, 040000); \
+ sfnt_set_projection_vector (interpreter, \
+ 0, 040000); \
+ }
+
+#define SVTCAx() \
+ { \
+ sfnt_set_freedom_vector (interpreter, \
+ 040000, 0); \
+ sfnt_set_projection_vector (interpreter, \
+ 040000, 0); \
+ }
+
+#define SPvTCAy() \
+ { \
+ sfnt_set_projection_vector (interpreter, \
+ 0, 040000); \
+ }
+
+#define SPvTCAx() \
+ { \
+ sfnt_set_projection_vector (interpreter, \
+ 040000, 0); \
+ }
+
+#define SFvTCAy() \
+ { \
+ sfnt_set_freedom_vector (interpreter, \
+ 0, 040000); \
+ }
+
+#define SFvTCAx() \
+ { \
+ sfnt_set_freedom_vector (interpreter, \
+ 040000, 0); \
+ }
+
+#define SPVTL() \
+ { \
+ struct sfnt_unit_vector vector; \
+ uint32_t p2, p1; \
+ \
+ p2 = POP (); \
+ p1 = POP (); \
+ \
+ sfnt_line_to_vector (interpreter, \
+ p2, p1, &vector, \
+ opcode == 0x07, \
+ false); \
+ \
+ sfnt_save_projection_vector (interpreter, \
+ &vector, \
+ false); \
+ }
+
+#define SFVTL() \
+ { \
+ struct sfnt_unit_vector vector; \
+ uint32_t p2, p1; \
+ \
+ p2 = POP (); \
+ p1 = POP (); \
+ \
+ sfnt_line_to_vector (interpreter, \
+ p2, p1, &vector, \
+ opcode == 0x09, \
+ false); \
+ \
+ sfnt_save_freedom_vector (interpreter, \
+ &vector); \
+ }
+
+#define SPVFS() \
+ { \
+ uint32_t y, x; \
+ \
+ y = POP (); \
+ x = POP (); \
+ \
+ sfnt_set_projection_vector (interpreter, x, \
+ y); \
+ }
+
+#define SFVFS() \
+ { \
+ uint16_t y, x; \
+ \
+ y = POP (); \
+ x = POP (); \
+ \
+ sfnt_set_freedom_vector (interpreter, x, \
+ y); \
+ }
+
+#define GPV() \
+ { \
+ struct sfnt_unit_vector vector; \
+ \
+ vector \
+ = interpreter->state.projection_vector; \
+ \
+ PUSH ((uint16_t) vector.x); \
+ PUSH ((uint16_t) vector.y); \
+ }
+
+#define GFV() \
+ { \
+ struct sfnt_unit_vector vector; \
+ \
+ vector \
+ = interpreter->state.freedom_vector; \
+ \
+ PUSH ((uint16_t) vector.x); \
+ PUSH ((uint16_t) vector.y); \
+ }
+
+#define SFVTPV() \
+ { \
+ interpreter->state.freedom_vector \
+ = interpreter->state.projection_vector; \
+ \
+ sfnt_validate_gs (&interpreter->state); \
+ }
+
+#define ISECT() \
+ { \
+ uint32_t a0, a1, b0, b1, p; \
+ \
+ a0 = POP (); \
+ a1 = POP (); \
+ b0 = POP (); \
+ b1 = POP (); \
+ p = POP (); \
+ \
+ sfnt_interpret_isect (interpreter, \
+ a0, a1, b0, b1, p); \
+ }
+
+#define ALIGNPTS() \
+ { \
+ uint32_t p1, p2; \
+ \
+ p1 = POP (); \
+ p2 = POP (); \
+ \
+ sfnt_interpret_alignpts (interpreter, p1, \
+ p2); \
+ }
+
+#define UTP() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ sfnt_interpret_utp (interpreter, p); \
+ }
+
+#define MDAP() \
+ { \
+ uint32_t p; \
+ \
+ p = POP (); \
+ sfnt_interpret_mdap (interpreter, p, \
+ opcode); \
+ }
+
+#define IUP() \
+ { \
+ sfnt_interpret_iup (interpreter, opcode); \
+ }
+
+#define SHP() \
+ { \
+ sfnt_interpret_shp (interpreter, opcode); \
+ }
+
+#define SHC() \
+ { \
+ uint32_t contour; \
+ \
+ contour = POP (); \
+ \
+ sfnt_interpret_shc (interpreter, contour, \
+ opcode); \
+ }
+
+#define SHZ() \
+ { \
+ uint32_t e; \
+ \
+ e = POP (); \
+ \
+ if (e > 1) \
+ TRAP ("invalid zone!"); \
+ \
+ sfnt_interpret_shz (interpreter, e, \
+ opcode); \
+ }
+
+#define SHPIX() \
+ { \
+ sfnt_f26dot6 pixels, dx, dy; \
+ uint32_t p; \
+ \
+ pixels = POP (); \
+ sfnt_scale_by_freedom_vector (interpreter, \
+ pixels, &dx, \
+ &dy); \
+ \
+ while (interpreter->state.loop--) \
+ { \
+ p = POP (); \
+ sfnt_direct_move_zp2 (interpreter, \
+ p, dx, dy); \
+ } \
+ \
+ interpreter->state.loop = 1; \
+ }
+
+#define IP() \
+ { \
+ sfnt_interpret_ip (interpreter); \
+ }
+
+#define MSIRP() \
+ { \
+ sfnt_f26dot6 d; \
+ uint32_t p; \
+ \
+ d = POP (); \
+ p = POP (); \
+ \
+ sfnt_interpret_msirp (interpreter, d, p, \
+ opcode); \
+ }
+
+#define ALIGNRP() \
+ { \
+ sfnt_interpret_alignrp (interpreter); \
+ }
+
+#define MIAP() \
+ { \
+ uint32_t cvt; \
+ uint32_t p; \
+ \
+ cvt = POP (); \
+ p = POP (); \
+ \
+ sfnt_interpret_miap (interpreter, cvt, p, \
+ opcode); \
+ }
+
+#define GC() \
+ { \
+ uint32_t p; \
+ sfnt_f26dot6 x, y, value; \
+ sfnt_f26dot6 org_x, org_y; \
+ \
+ p = POP (); \
+ \
+ sfnt_address_zp2 (interpreter, p, &x, &y, \
+ &org_x, &org_y); \
+ \
+ if (opcode == 0x47) \
+ value = DUAL_PROJECT (org_x, org_y); \
+ else \
+ value = PROJECT (x, y); \
+ \
+ PUSH_UNCHECKED (value); \
+ }
+
+#define SCFS() \
+ { \
+ uint32_t p; \
+ sfnt_f26dot6 c; \
+ \
+ c = POP (); \
+ p = POP (); \
+ \
+ sfnt_interpret_scfs (interpreter, p, c); \
+ }
+
+#define MD() \
+ { \
+ uint32_t p1, p2; \
+ sfnt_f26dot6 distance; \
+ \
+ p2 = POP (); \
+ p1 = POP (); \
+ \
+ distance \
+ = sfnt_measure_distance (interpreter, \
+ p1, p2, \
+ opcode); \
+ PUSH_UNCHECKED (distance); \
+ }
+
+#define FLIPPT() \
+ { \
+ sfnt_interpret_flippt (interpreter); \
+ }
+
+#define FLIPRGOFF() \
+ { \
+ uint32_t h, l; \
+ \
+ h = POP (); \
+ l = POP (); \
+ \
+ sfnt_interpret_fliprgoff (interpreter, \
+ h, l); \
+ }
+
+#define FLIPRGON() \
+ { \
+ uint32_t h, l; \
+ \
+ h = POP (); \
+ l = POP (); \
+ \
+ sfnt_interpret_fliprgon (interpreter, \
+ h, l); \
+ }
+
+#define SDPVTL() \
+ { \
+ struct sfnt_unit_vector vector; \
+ uint32_t p2, p1; \
+ \
+ p2 = POP (); \
+ p1 = POP (); \
+ \
+ sfnt_line_to_vector (interpreter, \
+ p2, p1, &vector, \
+ opcode == 0x87, \
+ false); \
+ \
+ sfnt_save_projection_vector (interpreter, \
+ &vector, \
+ false); \
+ \
+ sfnt_line_to_vector (interpreter, \
+ p2, p1, &vector, \
+ opcode == 0x87, \
+ true); \
+ \
+ sfnt_save_projection_vector (interpreter, \
+ &vector, \
+ true); \
+ }
+
+#define MIRP() \
+ { \
+ sfnt_interpret_mirp (interpreter, opcode); \
+ }
+
+#define MDRP() \
+ { \
+ sfnt_interpret_mdrp (interpreter, opcode); \
+ }
+
+
+
+#define NOT_IMPLEMENTED() \
+ sfnt_interpret_unimplemented (interpreter, \
+ opcode, why)
+
+
+
+/* Multiply the specified MAGNITUDE by the contents of INTERPRETER's
+ freedom vector and return the result in *DX and *DY. */
+
+static void
+sfnt_scale_by_freedom_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 magnitude, sfnt_f26dot6 *dx,
+ sfnt_f26dot6 *dy)
+{
+ struct sfnt_unit_vector *vector;
+
+ vector = &interpreter->state.freedom_vector;
+ *dx = sfnt_mul_f2dot14 (vector->x, magnitude);
+ *dy = sfnt_mul_f2dot14 (vector->y, magnitude);
+}
+
+/* Interpret a UTP instruction with the point P in INTERPRETER.
+ Unset any ``touched'' flag inside the point P, relative to the
+ zone in INTERPRETER's ZP0 register.
+
+ Trap upon encountering an out of bounds point. */
+
+static void
+sfnt_interpret_utp (struct sfnt_interpreter *interpreter,
+ uint32_t p)
+{
+ if (!interpreter->state.zp0)
+ {
+ if (p >= interpreter->twilight_zone_size)
+ TRAP ("UTP[] p lies outside twilight zone");
+
+ /* There are no flags in the twilight zone. */
+ return;
+ }
+
+ if (!interpreter->glyph_zone
+ || p >= interpreter->glyph_zone->num_points)
+ TRAP ("UTP[] p lies outside glyph zone");
+
+ interpreter->glyph_zone->flags[p] &= ~SFNT_POINT_TOUCHED_X;
+}
+
+/* Save the specified unit VECTOR into INTERPRETER's graphics state as
+ both the projection and the dual projection vectors.
+
+ If not DUAL_ONLY, set VECTOR as both the projection and dual
+ projection vectors. Otherwise, only set VECTOR as the dual
+ projection vector. */
+
+static void
+sfnt_save_projection_vector (struct sfnt_interpreter *interpreter,
+ struct sfnt_unit_vector *vector,
+ bool dual_only)
+{
+ if (!dual_only)
+ interpreter->state.projection_vector = *vector;
+
+ interpreter->state.dual_projection_vector = *vector;
+
+ sfnt_validate_gs (&interpreter->state);
+}
+
+/* Save the specified unit VECTOR into INTERPRETER's graphics
+ state. */
+
+static void
+sfnt_save_freedom_vector (struct sfnt_interpreter *interpreter,
+ struct sfnt_unit_vector *vector)
+{
+ interpreter->state.freedom_vector = *vector;
+
+ sfnt_validate_gs (&interpreter->state);
+}
+
+/* Return the values of the point NUMBER in the zone pointed to by
+ INTERPRETER's ZP2 register.
+
+ If X_ORG and Y_ORG are set, return the original values (prior to
+ any instruction interpretations) in those two locations.
+
+ Trap if NUMBER is out of bounds or the zone is inaccessible. */
+
+static void
+sfnt_address_zp2 (struct sfnt_interpreter *interpreter,
+ uint32_t number,
+ sfnt_f26dot6 *x, sfnt_f26dot6 *y,
+ sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
+{
+ if (!interpreter->state.zp2)
+ {
+ /* Address the twilight zone. */
+ if (number >= interpreter->twilight_zone_size)
+ TRAP ("address to ZP2 (twilight zone) out of bounds");
+
+ *x = interpreter->twilight_x[number];
+ *y = interpreter->twilight_y[number];
+
+ if (!x_org || !y_org)
+ return;
+
+ /* The twilight zone is initially all zero, but initial
+ positions can still be changed. */
+ *x_org = interpreter->twilight_original_x[number];
+ *y_org = interpreter->twilight_original_y[number];
+ return;
+ }
+
+ /* Address the glyph zone. */
+ if (!interpreter->glyph_zone)
+ TRAP ("address to ZP2 (glyph zone) points into unset"
+ " zone");
+
+ if (number >= interpreter->glyph_zone->num_points)
+ TRAP ("address to ZP2 (glyph zone) out of bounds");
+
+ *x = interpreter->glyph_zone->x_current[number];
+ *y = interpreter->glyph_zone->y_current[number];
+
+ if (x_org && y_org)
+ {
+ *x_org = interpreter->glyph_zone->x_points[number];
+ *y_org = interpreter->glyph_zone->y_points[number];
+ }
+}
+
+/* Return the values of the point NUMBER in the zone pointed to by
+ INTERPRETER's ZP1 register.
+
+ Trap if NUMBER is out of bounds or the zone is inaccessible. */
+
+static void
+sfnt_address_zp1 (struct sfnt_interpreter *interpreter,
+ uint32_t number,
+ sfnt_f26dot6 *x, sfnt_f26dot6 *y,
+ sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
+{
+ if (!interpreter->state.zp1)
+ {
+ /* Address the twilight zone. */
+ if (number >= interpreter->twilight_zone_size)
+ TRAP ("address to ZP1 (twilight zone) out of bounds");
+
+ *x = interpreter->twilight_x[number];
+ *y = interpreter->twilight_y[number];
+
+ if (!x_org || !y_org)
+ return;
+
+ /* The twilight zone is initially all zero, but initial
+ positions can still be changed. */
+ *x_org = interpreter->twilight_original_x[number];
+ *y_org = interpreter->twilight_original_y[number];
+ return;
+ }
+
+ /* Address the glyph zone. */
+ if (!interpreter->glyph_zone)
+ TRAP ("address to ZP1 (glyph zone) points into unset"
+ " zone");
+
+ if (number >= interpreter->glyph_zone->num_points)
+ TRAP ("address to ZP1 (glyph zone) out of bounds");
+
+ *x = interpreter->glyph_zone->x_current[number];
+ *y = interpreter->glyph_zone->y_current[number];
+
+ if (x_org && y_org)
+ {
+ *x_org = interpreter->glyph_zone->x_points[number];
+ *y_org = interpreter->glyph_zone->y_points[number];
+ }
+}
+
+/* Return the values of the point NUMBER in the zone pointed to by
+ INTERPRETER's ZP0 register.
+
+ Trap if NUMBER is out of bounds or the zone is inaccessible. */
+
+static void
+sfnt_address_zp0 (struct sfnt_interpreter *interpreter,
+ uint32_t number,
+ sfnt_f26dot6 *x, sfnt_f26dot6 *y,
+ sfnt_f26dot6 *x_org, sfnt_f26dot6 *y_org)
+{
+ if (!interpreter->state.zp0)
+ {
+ /* Address the twilight zone. */
+ if (number >= interpreter->twilight_zone_size)
+ TRAP ("address to ZP0 (twilight zone) out of bounds");
+
+ *x = interpreter->twilight_x[number];
+ *y = interpreter->twilight_y[number];
+
+ if (!x_org || !y_org)
+ return;
+
+ /* The twilight zone is initially all zero, but initial
+ positions can still be changed. */
+ *x_org = interpreter->twilight_original_x[number];
+ *y_org = interpreter->twilight_original_y[number];
+ return;
+ }
+
+ /* Address the glyph zone. */
+ if (!interpreter->glyph_zone)
+ TRAP ("address to ZP0 (glyph zone) points into unset"
+ " zone");
+
+ if (number >= interpreter->glyph_zone->num_points)
+ TRAP ("address to ZP0 (glyph zone) out of bounds");
+
+ *x = interpreter->glyph_zone->x_current[number];
+ *y = interpreter->glyph_zone->y_current[number];
+
+ if (x_org && y_org)
+ {
+ *x_org = interpreter->glyph_zone->x_points[number];
+ *y_org = interpreter->glyph_zone->y_points[number];
+ }
+}
+
+/* Set the point NUMBER in the zone referenced by INTERPRETER's ZP2
+ register to the specified X and Y.
+
+ Apply FLAGS to NUMBER's flags in that zone. Trap if NUMBER is out
+ of bounds. */
+
+static void
+sfnt_store_zp2 (struct sfnt_interpreter *interpreter,
+ uint32_t number, sfnt_f26dot6 x, sfnt_f26dot6 y,
+ int flags)
+{
+ if (!interpreter->state.zp2)
+ {
+ /* Address the twilight zone. */
+ if (number >= interpreter->twilight_zone_size)
+ TRAP ("address to ZP2 (twilight zone) out of bounds");
+
+ interpreter->twilight_x[number] = x;
+ interpreter->twilight_y[number] = y;
+ return;
+ }
+
+ /* Address the glyph zone. */
+ if (!interpreter->glyph_zone)
+ TRAP ("address to ZP0 (glyph zone) points into unset"
+ " zone");
+
+ if (number >= interpreter->glyph_zone->num_points)
+ TRAP ("address to ZP0 (glyph zone) out of bounds");
+
+ interpreter->glyph_zone->x_current[number] = x;
+ interpreter->glyph_zone->y_current[number] = y;
+ interpreter->glyph_zone->flags[number] |= flags;
+}
+
+#if 0
+
+/* Convert the line between the points X1, Y1 and X2, Y2 to standard
+ form.
+
+ Return the two coefficients in *A0 and *B0, and the constant in
+ *C. */
+
+static void
+sfnt_line_to_standard_form (sfnt_f26dot6 x1, sfnt_f26dot6 y1,
+ sfnt_f26dot6 x2, sfnt_f26dot6 y2,
+ sfnt_f26dot6 *a, sfnt_f26dot6 *b,
+ sfnt_f26dot6 *c)
+{
+ sfnt_f26dot6 a_temp, b_temp, c_temp;
+
+ a_temp = sfnt_sub (y2, y1);
+ b_temp = sfnt_sub (x1, x2);
+ c_temp = sfnt_sub (sfnt_mul_f26dot6 (x1, y2),
+ sfnt_mul_f26dot6 (x2, y1));
+
+ *a = a_temp;
+ *b = b_temp;
+ *c = c_temp;
+}
+
+#endif
+
+/* Check that the specified POINT lies within the zone addressed by
+ INTERPRETER's ZP2 register. Trap if it does not. */
+
+static void
+sfnt_check_zp2 (struct sfnt_interpreter *interpreter, uint32_t point)
+{
+ if (!interpreter->state.zp2)
+ {
+ if (point >= interpreter->twilight_zone_size)
+ TRAP ("point lies outside twilight zone (ZP2)");
+ }
+ else if (!interpreter->glyph_zone
+ || point >= interpreter->glyph_zone->num_points)
+ TRAP ("point lies outside glyph zone (ZP2)");
+}
+
+/* Check that the specified POINT lies within the zone addressed by
+ INTERPRETER's ZP0 register. Trap if it does not. */
+
+static void
+sfnt_check_zp0 (struct sfnt_interpreter *interpreter, uint32_t point)
+{
+ if (!interpreter->state.zp0)
+ {
+ if (point >= interpreter->twilight_zone_size)
+ TRAP ("point lies outside twilight zone (ZP0)");
+ }
+ else if (!interpreter->glyph_zone
+ || point >= interpreter->glyph_zone->num_points)
+ TRAP ("point lies outside glyph zone (ZP0)");
+}
+
+/* Check that the specified POINT lies within the zone addressed by
+ INTERPRETER's ZP1 register. Trap if it does not. */
+
+static void
+sfnt_check_zp1 (struct sfnt_interpreter *interpreter, uint32_t point)
+{
+ if (!interpreter->state.zp1)
+ {
+ if (point >= interpreter->twilight_zone_size)
+ TRAP ("point lies outside twilight zone (ZP0)");
+ }
+ else if (!interpreter->glyph_zone
+ || point >= interpreter->glyph_zone->num_points)
+ TRAP ("point lies outside glyph zone (ZP0)");
+}
+
+/* Move N points starting from the specified POINT in the zone
+ addressed by INTERPRETER's ZP0 register by the given DISTANCE along
+ the freedom vector.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_zp0 (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ if (!interpreter->state.zp0)
+ interpreter->state.move (&interpreter->twilight_x[point],
+ &interpreter->twilight_y[point],
+ n, interpreter, distance, NULL);
+ else
+ interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+ &interpreter->glyph_zone->y_current[point],
+ n, interpreter, distance,
+ &interpreter->glyph_zone->flags[point]);
+}
+
+/* Move N points starting from the specified POINT in the zone
+ addressed by INTERPRETER's ZP1 register by the given DISTANCE along
+ the freedom vector.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_zp1 (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ if (!interpreter->state.zp1)
+ interpreter->state.move (&interpreter->twilight_x[point],
+ &interpreter->twilight_y[point],
+ n, interpreter, distance, NULL);
+ else
+ interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+ &interpreter->glyph_zone->y_current[point],
+ n, interpreter, distance,
+ &interpreter->glyph_zone->flags[point]);
+}
+
+/* Move N points starting from the specified POINT in the zone
+ addressed by INTERPRETER's ZP2 register by the given DISTANCE along
+ the freedom vector.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_zp2 (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ if (!interpreter->state.zp2)
+ interpreter->state.move (&interpreter->twilight_x[point],
+ &interpreter->twilight_y[point],
+ n, interpreter, distance, NULL);
+ else
+ interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+ &interpreter->glyph_zone->y_current[point],
+ n, interpreter, distance,
+ &interpreter->glyph_zone->flags[point]);
+}
+
+/* Move N points from the specified POINT in INTERPRETER's glyph zone
+ by the given DISTANCE along the freedom vector.
+
+ Do not touch the points that are moved.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_glyph_zone (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ interpreter->state.move (&interpreter->glyph_zone->x_current[point],
+ &interpreter->glyph_zone->y_current[point],
+ n, interpreter, distance, NULL);
+}
+
+/* Move N points from the specified POINT in INTERPRETER's twilight
+ zone by the given DISTANCE along the freedom vector.
+
+ Do not touch the points that are moved.
+
+ No checking is done to ensure that POINT lies inside the zone, or
+ even that the zone exists at all. */
+
+static void
+sfnt_move_twilight_zone (struct sfnt_interpreter *interpreter, uint32_t point,
+ size_t n, sfnt_f26dot6 distance)
+{
+ interpreter->state.move (&interpreter->twilight_x[point],
+ &interpreter->twilight_y[point],
+ n, interpreter, distance, NULL);
+}
+
+/* Move the point P in the zone pointed to by the ZP2 register in
+ INTERPRETER's graphics state by DX, and DY.
+
+ Touch the point P in the directions of the movement.
+
+ Check that P is valid; if not, trap. Else, perform the move
+ directly without converting it from the projection vector or to the
+ freedom vector. */
+
+static void
+sfnt_direct_move_zp2 (struct sfnt_interpreter *interpreter, uint32_t p,
+ sfnt_f26dot6 dx, sfnt_f26dot6 dy)
+{
+ if (!interpreter->state.zp2)
+ {
+ if (p >= interpreter->twilight_zone_size)
+ TRAP ("point out of bounds");
+
+ interpreter->twilight_x[p]
+ = sfnt_add (interpreter->twilight_x[p], dx);
+ interpreter->twilight_y[p]
+ = sfnt_add (interpreter->twilight_y[p], dy);
+ }
+ else
+ {
+ if (!interpreter->glyph_zone
+ || p >= interpreter->glyph_zone->num_points)
+ TRAP ("point out of bounds");
+
+ interpreter->glyph_zone->x_current[p]
+ = sfnt_add (interpreter->glyph_zone->x_current[p], dx);
+ interpreter->glyph_zone->y_current[p]
+ = sfnt_add (interpreter->glyph_zone->y_current[p], dy);
+
+ if (dx)
+ interpreter->glyph_zone->flags[p] |= SFNT_POINT_TOUCHED_X;
+
+ if (dy)
+ interpreter->glyph_zone->flags[p] |= SFNT_POINT_TOUCHED_Y;
+ }
+}
+
+/* Project the vector VX, VY onto INTERPRETER's projection vector.
+ Return the magnitude of the projection. */
+
+static sfnt_f26dot6
+sfnt_project_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 vx, sfnt_f26dot6 vy)
+{
+ return interpreter->state.project (vx, vy, interpreter);
+}
+
+/* Project the vector VX, VY onto INTERPRETER's dual projection
+ vector. Return the magnitude of the projection. */
+
+static sfnt_f26dot6
+sfnt_dual_project_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 vx, sfnt_f26dot6 vy)
+{
+ return interpreter->state.dual_project (vx, vy, interpreter);
+}
+
+/* Interpret a FLIPRGOFF instruction in INTERPRTER. Make each point
+ in ZP0 between L and H an off-curve point. */
+
+static void
+sfnt_interpret_fliprgoff (struct sfnt_interpreter *interpreter,
+ uint32_t l, uint32_t h)
+{
+ uint32_t i;
+
+ sfnt_check_zp0 (interpreter, l);
+ sfnt_check_zp0 (interpreter, h);
+
+ if (!interpreter->state.zp0)
+ return;
+
+ for (i = l; i < h; ++i)
+ interpreter->glyph_zone->flags[i] &= ~01;
+}
+
+/* Interpret a FLIPRGON instruction in INTERPRTER. Make each point in
+ ZP0 between L and H an on-curve point. */
+
+static void
+sfnt_interpret_fliprgon (struct sfnt_interpreter *interpreter,
+ uint32_t l, uint32_t h)
+{
+ uint32_t i;
+
+ sfnt_check_zp0 (interpreter, l);
+ sfnt_check_zp0 (interpreter, h);
+
+ if (!interpreter->state.zp0)
+ return;
+
+ for (i = l; i < h; ++i)
+ interpreter->glyph_zone->flags[i] |= ~01;
+}
+
+/* Interpret a FLIPPT instruction in INTERPRETER. For loop times, pop
+ a point in ZP0. If it is an on-curve point, make it an off-curve
+ one, and vice versa. */
+
+static void
+sfnt_interpret_flippt (struct sfnt_interpreter *interpreter)
+{
+ uint32_t point;
+
+ while (interpreter->state.loop--)
+ {
+ point = POP ();
+
+ /* There are no flags in the twilight zone.
+ But first check that the point is within bounds. */
+
+ sfnt_check_zp0 (interpreter, point);
+
+ if (!interpreter->state.zp0)
+ continue;
+
+ /* If POINT is on the curve, make it off the curve and vice
+ versa. */
+
+ if (interpreter->glyph_zone->flags[point] & 01)
+ interpreter->glyph_zone->flags[point] &= ~01;
+ else
+ interpreter->glyph_zone->flags[point] |= 01;
+ }
+
+ /* Restore loop. */
+ interpreter->state.loop = 1;
+}
+
+/* Interpret an SCFS instruction.
+ Move P in ZP2 along the freedom vector until its projection is
+ equal to C.
+
+ If ZP2 is the twilight zone, ``create'' P by setting its original
+ position to the projection. */
+
+static void
+sfnt_interpret_scfs (struct sfnt_interpreter *interpreter,
+ uint32_t p, sfnt_f26dot6 c)
+{
+ sfnt_f26dot6 x, y, distance;
+
+ sfnt_address_zp2 (interpreter, p, &x, &y, NULL, NULL);
+ distance = PROJECT (x, y);
+ sfnt_move_zp2 (interpreter, p, 1, sfnt_sub (c, distance));
+
+ if (!interpreter->state.zp2)
+ {
+ interpreter->twilight_original_x[p] = interpreter->twilight_x[p];
+ interpreter->twilight_original_y[p] = interpreter->twilight_y[p];
+ }
+}
+
+/* Symmetrically round the 26.6 fixed point value X using the rounding
+ mode in INTERPRETER. Return the result. */
+
+static sfnt_f26dot6
+sfnt_round_symmetric (struct sfnt_interpreter *interpreter, sfnt_f26dot6 x)
+{
+ int sign;
+
+ sign = 1;
+
+ if (x < 0)
+ {
+ sign = -1;
+ x = -x;
+ }
+
+ return interpreter->state.round (x, interpreter) * sign;
+}
+
+/* Interpret an MIAP (``Move Indirect Absolute Point'') instruction
+ using INTERPRETER.
+
+ Move P in ZP0 along the freedom vector until its projection on the
+ projection vector is equal to CVT units in the projection vector.
+
+ Finally, set RP0 and RP1 to P.
+
+ If ZP0 is the twilight zone, then first create that point in the
+ twilight zone by setting its ``original position'' to the
+ projection of the value.
+
+ If OPCODE is 0x3f, then in addition check the CVT value against the
+ control value cut-in, and round the magnitudes of the movement. */
+
+static void
+sfnt_interpret_miap (struct sfnt_interpreter *interpreter,
+ uint32_t cvt, uint32_t p, unsigned char opcode)
+{
+ sfnt_f26dot6 x, y, distance, value, delta;
+
+ /* Read the cvt value. */
+
+ if (cvt >= interpreter->cvt_size)
+ TRAP ("out of bounds read to cvt");
+
+ value = interpreter->cvt[cvt];
+
+ /* Now load the point. */
+ sfnt_address_zp0 (interpreter, p, &x, &y, NULL, NULL);
+
+ /* Create the twilight zone point if necessary.
+ Note that the value used is not rounded. */
+
+ if (!interpreter->state.zp0)
+ {
+ x = interpreter->twilight_x[p]
+ = interpreter->twilight_original_x[p]
+ = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x,
+ value);
+
+ y = interpreter->twilight_y[p]
+ = interpreter->twilight_original_y[p]
+ = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y,
+ value);
+ }
+
+ /* Obtain the original distance. */
+ distance = sfnt_project_vector (interpreter, x, y);
+
+ /* Round the distance and apply the cvt cut in if necessary. */
+
+ if (opcode == 0x3f)
+ {
+ delta = sfnt_sub (value, distance);
+
+ if (delta < 0)
+ delta = -delta;
+
+ /* If delta is more than the cvt cut in (more aptly named ``cut
+ out''), use the original distance. */
+
+ if (delta > interpreter->state.cvt_cut_in)
+ value = distance;
+
+ /* Round value. */
+ value = sfnt_round_symmetric (interpreter, value);
+ }
+
+ /* Move the point by the distance. */
+ sfnt_move_zp0 (interpreter, p, 1, sfnt_sub (value, distance));
+
+ /* Set reference points. */
+ interpreter->state.rp0 = p;
+ interpreter->state.rp1 = p;
+}
+
+/* Perform a single iteration of sfnt_interpret_alignrp. RP0X and
+ RP0Y should be the position of the reference point RP0 in ZP0. */
+
+static void
+sfnt_interpret_alignrp_1 (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 rp0x, sfnt_f26dot6 rp0y)
+{
+ sfnt_f26dot6 distance, x, y;
+ uint32_t point;
+
+ point = POP ();
+
+ /* Load this point. */
+ sfnt_address_zp1 (interpreter, point, &x, &y, NULL, NULL);
+
+ /* Measure the distance from here to rp0. */
+ distance = sfnt_project_vector (interpreter, sfnt_sub (x, rp0x),
+ sfnt_sub (y, rp0y));
+
+ /* Move by the opposite. */
+ sfnt_move_zp1 (interpreter, point, 1, -distance);
+}
+
+/* For loop times, pop a point in ZP1 and align it to RP0 in ZP0 by
+ moving it along the freedom vector until its projected distance
+ from RP0 becomes 0. */
+
+static void
+sfnt_interpret_alignrp (struct sfnt_interpreter *interpreter)
+{
+ sfnt_f26dot6 rp0x, rp0y;
+
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rp0x, &rp0y, NULL, NULL);
+
+ while (interpreter->state.loop--)
+ {
+ sfnt_interpret_alignrp_1 (interpreter, rp0x, rp0y);
+
+ /* Reload RP0 if it is in the same zone as ZP1. */
+ if (interpreter->state.zp0 == interpreter->state.zp1)
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rp0x, &rp0y, NULL, NULL);
+ }
+
+ interpreter->state.loop = 1;
+}
+
+/* Align the two points P1 and P2 relative to the projection vector.
+ P1 is addressed relative to ZP0, and P2 is addressed relative to
+ ZP1.
+
+ Move both points along the freedom vector by half the magnitude of
+ the the projection of a vector formed by P1.x - P2.x, P1.y - P2.y,
+ upon the projection vector. */
+
+static void
+sfnt_interpret_alignpts (struct sfnt_interpreter *interpreter,
+ uint32_t p1, uint32_t p2)
+{
+ sfnt_f26dot6 p1x, p1y, p2x, p2y;
+ sfnt_f26dot6 magnitude;
+
+ sfnt_address_zp0 (interpreter, p1, &p1x, &p1y, NULL, NULL);
+ sfnt_address_zp1 (interpreter, p2, &p2x, &p2y, NULL, NULL);
+
+ magnitude = sfnt_project_vector (interpreter,
+ sfnt_sub (p1x, p2x),
+ sfnt_sub (p1y, p2y));
+ magnitude = magnitude / 2;
+
+ /* Now move both points along the freedom vector. */
+ sfnt_move_zp0 (interpreter, p1, 1, magnitude);
+ sfnt_move_zp1 (interpreter, p2, 1, -magnitude);
+}
+
+/* Set the point P in the zone referenced in INTERPRETER's ZP2
+ register to the intersection between the line formed by the points
+ POINT_A0 to POINT_A1 in ZP0 and another line formed by POINT_B0 to
+ POINT_B1 in ZP1.
+
+ Touch the point P. */
+
+static void
+sfnt_interpret_isect (struct sfnt_interpreter *interpreter,
+ uint32_t point_a0, uint32_t point_a1,
+ uint32_t point_b0, uint32_t point_b1,
+ uint32_t p)
+{
+ sfnt_f26dot6 a0x, a0y, a1x, a1y;
+ sfnt_f26dot6 b0x, b0y, b1x, b1y;
+#if 0
+ sfnt_f26dot6 determinant, dx, dy;
+ sfnt_f26dot6 a0, b0, a1, b1;
+ sfnt_f26dot6 c0, c1, px, py;
+#else
+ sfnt_f26dot6 dx, dy, dax, day, dbx, dby;
+ sfnt_f26dot6 discriminant, val, dot_product;
+ sfnt_f26dot6 px, py;
+#endif
+
+ /* Load points. */
+ sfnt_address_zp0 (interpreter, point_a0, &a0x, &a0y, NULL, NULL);
+ sfnt_address_zp0 (interpreter, point_a1, &a1x, &a1y, NULL, NULL);
+ sfnt_address_zp1 (interpreter, point_b0, &b0x, &b0y, NULL, NULL);
+ sfnt_address_zp1 (interpreter, point_b1, &b1x, &b1y, NULL, NULL);
+
+#if 0
+ /* The system is determined from the standard form (look this up) of
+ both lines.
+
+ (the variables below have no relation to C identifiers
+ unless otherwise specified.)
+
+ a0*x + b0*y = c0
+ a1*x + b1*y = c1
+
+ The coefficient matrix is thus
+
+ [ a0 b0
+ a1 b1 ]
+
+ the vector of constants (also just dubbed the ``column vector''
+ by some people)
+
+ [ c0
+ c1 ]
+
+ and the solution vector becomes
+
+ [ x
+ y ]
+
+ Since there are exactly two equations and two unknowns, Cramer's
+ rule applies, and there is no need for any Gaussian elimination.
+
+ The determinant for the coefficient matrix is:
+
+ D = a0*b1 - b0*a1
+
+ the first and second determinants are:
+
+ Dx = c0*b1 - a0*c1
+ Dy = a1*c1 - c0*b1
+
+ and x = Dx / D, y = Dy / D.
+
+ If the system is indeterminate, D will be 0. */
+
+ sfnt_line_to_standard_form (a0x, a0y, a1x, a1y,
+ &a0, &b0, &c0);
+ sfnt_line_to_standard_form (b0x, b0y, b1x, b1y,
+ &a1, &b1, &c1);
+
+
+ /* Compute determinants. */
+ determinant = sfnt_sub (sfnt_mul_fixed (a0, b1),
+ sfnt_mul_fixed (b0, a1));
+ dx = sfnt_sub (sfnt_mul_fixed (c0, b1),
+ sfnt_mul_fixed (a1, c1));
+ dy = sfnt_sub (sfnt_mul_fixed (a0, c1),
+ sfnt_mul_fixed (c0, b0));
+
+ /* Detect degenerate cases. */
+
+ if (determinant == 0)
+ goto degenerate_case;
+#else
+ /* The algorithm above would work with floating point, but overflows
+ too easily with fixed point numbers.
+
+ Instead, use the modified vector projection algorithm found in
+ FreeType. */
+
+ dbx = sfnt_sub (b1x, b0x);
+ dby = sfnt_sub (b1y, b0y);
+ dax = sfnt_sub (a1x, a0x);
+ day = sfnt_sub (a1y, a0y);
+
+ /* Compute vector cross product. */
+ discriminant = sfnt_add (sfnt_mul_f26dot6 (dax, -dby),
+ sfnt_mul_f26dot6 (day, dbx));
+ dot_product = sfnt_add (sfnt_mul_f26dot6 (dax, dbx),
+ sfnt_mul_f26dot6 (day, dby));
+
+ /* Reject any non-intersections and grazing intersections. */
+ if (!(sfnt_mul (19, abs (discriminant)) > abs (dot_product)))
+ return;
+
+ /* Reject any non-intersections. */
+ if (!discriminant)
+ goto degenerate_case;
+
+ dx = sfnt_sub (b0x, a0x);
+ dy = sfnt_sub (b0y, a0y);
+ val = sfnt_add (sfnt_mul_f26dot6 (dx, -dby),
+ sfnt_mul_f26dot6 (dy, dbx));
+
+ /* Project according to these values. */
+ dx = sfnt_add (a0x, sfnt_multiply_divide_signed (val, dax,
+ discriminant));
+ dy = sfnt_add (a0y, sfnt_multiply_divide_signed (val, day,
+ discriminant));
+#endif
+
+ sfnt_store_zp2 (interpreter, p,
+#if 0
+ sfnt_div_fixed (dx, determinant),
+ sfnt_div_fixed (dy, determinant),
+#else
+ dx, dy,
+#endif
+ SFNT_POINT_TOUCHED_BOTH);
+ return;
+
+ degenerate_case:
+
+ /* Apple says that in this case:
+
+ Px = (a0x + a1x) / 2 + (b0x + b1x) / 2
+ ---------------------------------
+ 2
+ Py = (a0y + a1y) / 2 + (b0y + b1y) / 2
+ ---------------------------------
+ 2 */
+
+ px = (sfnt_add (a0x, a1x) / 2 + sfnt_add (b0x, b1x) / 2) / 2;
+ py = (sfnt_add (a0y, a1y) / 2 + sfnt_add (b0y, b1y) / 2) / 2;
+ sfnt_store_zp2 (interpreter, p, px, py,
+ SFNT_POINT_TOUCHED_BOTH);
+}
+
+/* Compute the square root of the 16.16 fixed point number N. */
+
+static sfnt_fixed
+sfnt_sqrt_fixed (sfnt_fixed n)
+{
+ int count;
+ unsigned int root, rem_hi, rem_lo, possible;
+
+ root = 0;
+
+ if (n > 0)
+ {
+ rem_hi = 0;
+ rem_lo = n;
+ count = 24;
+
+ do
+ {
+ rem_hi = (rem_hi << 2) | (rem_lo >> 30);
+ rem_lo <<= 2;
+ root <<= 1;
+ possible = (root << 1) + 1;
+
+ if (rem_hi >= possible)
+ {
+ rem_hi -= possible;
+ root += 1;
+ }
+ }
+ while (--count);
+ }
+
+ return root;
+}
+
+/* Compute a unit vector describing a vector VX, VY. Return the value
+ in *VECTOR. */
+
+static void
+sfnt_normalize_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_unit_vector *vector)
+{
+ sfnt_f26dot6 x_squared, y_squared;
+ sfnt_fixed n, magnitude;
+
+ if (!vx && !vy)
+ {
+ /* If vx and vy are both zero, then just project
+ horizontally. */
+
+ fail:
+ vector->x = 04000;
+ vector->y = 0;
+ return;
+ }
+
+ /* Scale vx and vy up if they won't at least make 1. */
+
+ while (!(vx < -32 || vx > 32) && !(vy < -32 || vy > 32))
+ {
+ vx = vx * 2;
+ vy = vy * 2;
+ }
+
+ /* Compute the magnitude of this vector. */
+ x_squared = sfnt_mul_f26dot6 (vx, vx);
+ y_squared = sfnt_mul_f26dot6 (vy, vy);
+
+ /* x_squared and y_squared can end up too large to fit in a 16.16
+ fixed. Scale both values down until they fit. */
+
+ while (x_squared > 0x200000 || y_squared > 0x200000
+ || x_squared < -0x200000 || y_squared < -0x200000)
+ {
+ x_squared /= 2;
+ y_squared /= 2;
+ }
+
+ /* Convert to 16.16 for greater precision. */
+ n = sfnt_add (x_squared, y_squared) * 1024;
+
+ /* Get hypotenuse of the triangle from vx, 0, to 0, vy. */
+ magnitude = sfnt_sqrt_fixed (n);
+
+ /* Avoid division by zero. */
+ if (!magnitude)
+ goto fail;
+
+ /* Long division.. eek! */
+ vector->x = (sfnt_div_fixed (vx * 1024, magnitude) / 4);
+ vector->y = (sfnt_div_fixed (vy * 1024, magnitude) / 4);
+}
+
+/* Compute a unit vector describing the direction of a line from the
+ point P2 to the point P1. Save the result in *VECTOR.
+
+ P2 is the address of a point in the zone specified in the ZP2
+ register. P1 is the address of a point in the zone specified in
+ the ZP1 register. Take the values of both registers from the
+ specified INTERPRETER's graphics state.
+
+ If PERPENDICULAR, then *VECTOR will be rotated 90 degrees
+ counter-clockwise. Else, *VECTOR will be parallel to the line.
+
+ If ORIGINAL, then the coordinates used to calculate the line will
+ be those prior to instructing. Otherwise, the current coordinates
+ will be used. */
+
+static void
+sfnt_line_to_vector (struct sfnt_interpreter *interpreter,
+ uint32_t p2, uint32_t p1,
+ struct sfnt_unit_vector *vector,
+ bool perpendicular, bool original)
+{
+ sfnt_f26dot6 x2, y2, original_x2, original_y2;
+ sfnt_f26dot6 x1, y1, original_x1, original_y1;
+ sfnt_f26dot6 a, b, temp;
+
+ sfnt_address_zp2 (interpreter, p2, &x2, &y2, &original_x2,
+ &original_y2);
+ sfnt_address_zp1 (interpreter, p1, &x1, &y1, &original_x1,
+ &original_y1);
+
+ /* Use original coordinates if specified. */
+
+ if (original)
+ {
+ x2 = original_x2;
+ y2 = original_y2;
+ x1 = original_x1;
+ y1 = original_y1;
+ }
+
+ /* Calculate the vector between X2, Y2, and X1, Y1. */
+ a = sfnt_sub (x1, x2);
+ b = sfnt_sub (y1, y2);
+
+ /* Rotate counterclockwise if necessary. */
+
+ if (perpendicular)
+ {
+ temp = b;
+ b = a;
+ a = -temp;
+ }
+
+ /* Normalize this vector, turning it into a unit vector. */
+ sfnt_normalize_vector (a, b, vector);
+}
+
+/* Measure the distance between P1 in ZP0 and P2 in ZP1,
+ relative to the projection or dual projection vector.
+
+ Return the distance of P1 and P2 relative to their original
+ un-instructed positions should OPCODE be 0x4A, and to their
+ instructed positions should OPCODE be 0x49. */
+
+static sfnt_f26dot6
+sfnt_measure_distance (struct sfnt_interpreter *interpreter,
+ uint32_t p1, uint32_t p2,
+ unsigned char opcode)
+{
+ sfnt_f26dot6 p1x, p1y, p1_original_x, p1_original_y;
+ sfnt_f26dot6 p2x, p2y, p2_original_x, p2_original_y;
+
+ /* P1 is relative to ZP0 and P2 is relative to ZP1.
+ Apple's manual says this, Microsoft's does not. */
+
+ sfnt_address_zp0 (interpreter, p1, &p1x, &p1y,
+ &p1_original_x, &p1_original_y);
+ sfnt_address_zp1 (interpreter, p2, &p2x, &p2y,
+ &p2_original_x, &p2_original_y);
+
+ if (opcode == 0x4A)
+ return DUAL_PROJECT (sfnt_sub (p1_original_x, p2_original_x),
+ sfnt_sub (p1_original_y, p2_original_y));
+
+ return PROJECT (sfnt_sub (p1x, p2x),
+ sfnt_sub (p1y, p2y));
+}
+
+/* Interpret an MSIRP instruction in INTERPRETER.
+ Take a point P, and make the distance between P in ZP1 and the
+ current position of RP0 in ZP0 equal to D.
+
+ If ZP1 is the twilight zone, then create the point P by setting its
+ position and relative positions.
+
+ Then, if OPCODE is equal to 0x3b, make P RP0. */
+
+static void
+sfnt_interpret_msirp (struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 d, uint32_t p, unsigned char opcode)
+{
+ sfnt_f26dot6 rp0x, rp0y, rp0_original_x, rp0_original_y;
+ sfnt_f26dot6 x, y;
+ sfnt_f26dot6 old_distance, temp;
+
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rp0x, &rp0y, &rp0_original_x,
+ &rp0_original_y);
+ sfnt_address_zp1 (interpreter, p, &x, &y, NULL, NULL);
+
+ if (!interpreter->state.zp1)
+ {
+ /* Create this point in the twilight zone at RP0. */
+
+ x = interpreter->twilight_x[p] = rp0x;
+ y = interpreter->twilight_y[p] = rp0y;
+
+ /* Now set the original positions to the projected difference
+ from rp0. This makes sense once you think about it. */
+ temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x, d);
+ temp = sfnt_add (temp, rp0_original_x);
+ interpreter->twilight_original_x[p] = temp;
+
+ temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y, d);
+ temp = sfnt_add (temp, rp0_original_y);
+ interpreter->twilight_original_y[p] = temp;
+ }
+
+ /* Compute the original distance. */
+ old_distance = sfnt_project_vector (interpreter,
+ sfnt_sub (x, rp0x),
+ sfnt_sub (y, rp0y));
+
+ /* Move the point. */
+ sfnt_move_zp1 (interpreter, p, 1, sfnt_sub (d, old_distance));
+
+ /* Nothing in the TrueType reference manual says directly that this
+ instruction should change rp1 and rp2. However, it says this
+ instruction is ``very similar to the MIRP[] instruction
+ except...'', and FreeType seems to do this, so do it as well. */
+
+ interpreter->state.rp1 = interpreter->state.rp0;
+ interpreter->state.rp2 = p;
+
+ if (opcode == 0x3b)
+ interpreter->state.rp0 = p;
+}
+
+/* Interpret an IP instruction in INTERPRETER. For loop times, pop a
+ single point in ZP2, and interpolate it so that its original
+ relationship to the points RP1 in ZP0 and RP2 in ZP1 as measured
+ along the dual projection vector continues to hold true. */
+
+static void
+sfnt_interpret_ip (struct sfnt_interpreter *interpreter)
+{
+ sfnt_f26dot6 rp1x, rp1y, rp1_original_x, rp1_original_y;
+ sfnt_f26dot6 rp2x, rp2y, rp2_original_x, rp2_original_y;
+ sfnt_f26dot6 range, new_range, org_distance, cur_distance;
+ sfnt_f26dot6 new_distance;
+ uint32_t p;
+ sfnt_f26dot6 x, y, original_x, original_y;
+
+ /* First load both reference points. */
+ sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+ &rp1x, &rp1y, &rp1_original_x,
+ &rp1_original_y);
+ sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+ &rp2x, &rp2y, &rp2_original_x,
+ &rp2_original_y);
+
+ /* Get the original distance between of RP1 and RP2 measured
+ relative to the dual projection vector. */
+ range = sfnt_dual_project_vector (interpreter,
+ sfnt_sub (rp2_original_x,
+ rp1_original_x),
+ sfnt_sub (rp2_original_y,
+ rp1_original_y));
+
+ /* Get the new distance. */
+ new_range = sfnt_dual_project_vector (interpreter,
+ sfnt_sub (rp2x, rp1x),
+ sfnt_sub (rp2y, rp1y));
+
+ while (interpreter->state.loop--)
+ {
+ p = POP ();
+
+ /* Load this point relative to zp2. */
+ sfnt_address_zp2 (interpreter, p, &x, &y, &original_x,
+ &original_y);
+
+ /* Now compute the old distance from this point to rp1. */
+ org_distance
+ = sfnt_dual_project_vector (interpreter,
+ sfnt_sub (original_x,
+ rp1_original_x),
+ sfnt_sub (original_y,
+ rp1_original_y));
+
+ /* And the current distance from this point to rp1, so
+ how much to move can be determined. */
+ cur_distance
+ = sfnt_project_vector (interpreter,
+ sfnt_sub (x, rp1x),
+ sfnt_sub (y, rp1y));
+
+ /* Finally, apply the ratio of the new distance between RP1 and
+ RP2 to that of the old distance between the two reference
+ points to org_distance, making new_distance.
+
+ If both reference points occupy the same position on the dual
+ projection vector, then simply use the old distance. */
+
+ if (org_distance)
+ {
+ if (range)
+ new_distance
+ = sfnt_multiply_divide_signed (org_distance,
+ new_range, range);
+ else
+ new_distance = org_distance;
+ }
+ else
+ new_distance = 0;
+
+ /* And move the point along the freedom vector to reflect the
+ change in distance. */
+ sfnt_move_zp2 (interpreter, p, 1,
+ sfnt_sub (new_distance, cur_distance));
+ }
+
+ interpreter->state.loop = 1;
+}
+
+/* Apply the delta specified by OPERAND to the control value table
+ entry at INDEX currently loaded inside INTERPRETER.
+
+ Trap if INDEX is out of bounds.
+
+ NUMBER is the number of the specific DELTAC instruction this delta
+ is being applied on behalf of. It must be between 1 and 3. */
+
+static void
+sfnt_deltac (int number, struct sfnt_interpreter *interpreter,
+ unsigned int index, unsigned char operand)
+{
+ int ppem, delta;
+
+ /* Make sure INDEX is a valid cvt entry. */
+
+ if (index >= interpreter->cvt_size)
+ TRAP ("DELTACn instruction out of bounds");
+
+ /* operand is an 8 bit number. The most significant 4 bits
+ represent a specific PPEM size at which to apply the delta
+ specified in the low 4 bits, summed with an instruction specific
+ delta, and the current delta base. */
+
+ ppem = (operand >> 4) + interpreter->state.delta_base;
+
+ switch (number)
+ {
+ case 1:
+ break;
+
+ case 2:
+ ppem += 16;
+ break;
+
+ case 3:
+ ppem += 32;
+ break;
+ }
+
+ /* Don't apply the delta if the ppem size doesn't match. */
+
+ if (interpreter->ppem != ppem)
+ return;
+
+ /* Now, determine the delta using the low 4 bits. The low 4 bits
+ actually specify a ``magnitude'' to apply to the delta, and do
+ not have an encoding for the delta 0. */
+
+ switch (operand & 0xf)
+ {
+ case 0:
+ delta = -8;
+ break;
+
+ case 1:
+ delta = -7;
+ break;
+
+ case 2:
+ delta = -6;
+ break;
+
+ case 3:
+ delta = -5;
+ break;
+
+ case 4:
+ delta = -4;
+ break;
+
+ case 5:
+ delta = -3;
+ break;
+
+ case 6:
+ delta = -2;
+ break;
+
+ case 7:
+ delta = -1;
+ break;
+
+ case 8:
+ delta = 1;
+ break;
+
+ case 9:
+ delta = 2;
+ break;
+
+ case 10:
+ delta = 3;
+ break;
+
+ case 11:
+ delta = 4;
+ break;
+
+ case 12:
+ delta = 5;
+ break;
+
+ case 13:
+ delta = 6;
+ break;
+
+ case 14:
+ delta = 7;
+ break;
+
+ case 15:
+ delta = 8;
+ break;
+
+ /* To pacify -fanalyzer. */
+ default:
+ abort ();
+ }
+
+ /* Now, scale up the delta by the step size, which is determined by
+ the delta shift. */
+ delta *= 1l << (6 - interpreter->state.delta_shift);
+
+ /* Finally, apply the delta to the CVT entry. */
+ interpreter->cvt[index] = sfnt_add (interpreter->cvt[index],
+ delta);
+}
+
+/* Interpret an MDAP (Move Direct Absolute Point) instruction with the
+ opcode OPCODE and the operand P in INTERPRETER.
+
+ Touch the point P (within the zone specified in zp0) in the
+ directions specified in the freedom vector. Then, if OPCODE is
+ 0x7f, round the point and move it the rounded distance along the
+ freedom vector.
+
+ Finally, set the RP0 and RP1 registers to P. */
+
+static void
+sfnt_interpret_mdap (struct sfnt_interpreter *interpreter,
+ uint32_t p, uint32_t opcode)
+{
+ sfnt_f26dot6 here, distance, px, py;
+
+ sfnt_address_zp0 (interpreter, p, &px, &py, NULL, NULL);
+
+ /* Measure the current distance. */
+ here = sfnt_project_vector (interpreter, px, py);
+
+ if (opcode == 0x7f)
+ {
+ /* Measure distance, round, then move to the distance. */
+ distance = sfnt_project_vector (interpreter, px, py);
+ distance = sfnt_round_symmetric (interpreter, distance);
+ distance = sfnt_sub (distance, here);
+ }
+ else
+ /* Don't move. Just touch the point. */
+ distance = 0;
+
+ sfnt_move_zp0 (interpreter, p, 1, distance);
+
+ interpreter->state.rp0 = p;
+ interpreter->state.rp1 = p;
+}
+
+/* Apply the delta specified by OPERAND to the point P in ZP0
+ currently loaded inside INTERPRETER.
+
+ Trap if P is out of bounds.
+
+ NUMBER is the number of the specific DELTAP instruction this delta
+ is being applied on behalf of. It must be between 1 and 3. */
+
+static void
+sfnt_deltap (int number, struct sfnt_interpreter *interpreter,
+ unsigned char operand, unsigned int index)
+{
+ int ppem, delta;
+
+ return;
+
+ /* Extract the ppem from OPERAND. The format is the same as in
+ sfnt_deltac. */
+
+ ppem = (operand >> 4) + interpreter->state.delta_base;
+
+ switch (number)
+ {
+ case 1:
+ break;
+
+ case 2:
+ ppem += 16;
+ break;
+
+ case 3:
+ ppem += 32;
+ break;
+ }
+
+ /* Don't apply the delta if the ppem size doesn't match. */
+
+ if (interpreter->ppem != ppem)
+ return;
+
+ /* Now, determine the magnitude of the movement and find the
+ delta. */
+
+ switch (operand & 0xf)
+ {
+ case 0:
+ delta = -8;
+ break;
+
+ case 1:
+ delta = -7;
+ break;
+
+ case 2:
+ delta = -6;
+ break;
+
+ case 3:
+ delta = -5;
+ break;
+
+ case 4:
+ delta = -4;
+ break;
+
+ case 5:
+ delta = -3;
+ break;
+
+ case 6:
+ delta = -2;
+ break;
+
+ case 7:
+ delta = -1;
+ break;
+
+ case 8:
+ delta = 1;
+ break;
+
+ case 9:
+ delta = 2;
+ break;
+
+ case 10:
+ delta = 3;
+ break;
+
+ case 11:
+ delta = 4;
+ break;
+
+ case 12:
+ delta = 5;
+ break;
+
+ case 13:
+ delta = 6;
+ break;
+
+ case 14:
+ delta = 7;
+ break;
+
+ case 15:
+ delta = 8;
+ break;
+
+ /* To pacify -fanalyzer. */
+ default:
+ abort ();
+ }
+
+ /* Now, scale up the delta by the step size, which is determined by
+ the delta shift. */
+ delta *= 1l << (6 - interpreter->state.delta_shift);
+
+ /* Move the point. */
+ sfnt_check_zp0 (interpreter, index);
+ sfnt_move_zp0 (interpreter, index, 1, delta);
+}
+
+/* Needed by sfnt_interpret_call. */
+static void sfnt_interpret_run (struct sfnt_interpreter *,
+ enum sfnt_interpreter_run_context);
+
+/* Call DEFINITION inside INTERPRETER.
+
+ Save INTERPRETER->IP, INTERPRETER->instructions, and
+ INTERPRETER->num_instructions onto the C stack.
+
+ Then, load the instructions in DEFINITION, and run the interpreter
+ again with the context CONTEXT.
+
+ Finally, restore all values. */
+
+static void
+sfnt_interpret_call (struct sfnt_interpreter_definition *definition,
+ struct sfnt_interpreter *interpreter,
+ enum sfnt_interpreter_run_context context)
+{
+ uint16_t num_instructions;
+ int IP;
+ unsigned char *instructions;
+
+ /* Check that no recursion is going on. */
+ if (interpreter->call_depth++ >= 128)
+ TRAP ("CALL called CALL more than 127 times");
+
+ /* Save the old IP, instructions and number of instructions. */
+ num_instructions = interpreter->num_instructions;
+ IP = interpreter->IP;
+ instructions = interpreter->instructions;
+
+ /* Load and run the definition. */
+ interpreter->num_instructions = definition->instruction_count;
+ interpreter->instructions = definition->instructions;
+ interpreter->IP = 0;
+ sfnt_interpret_run (interpreter, context);
+
+ /* Restore the old values. */
+ interpreter->num_instructions = num_instructions;
+ interpreter->IP = IP;
+ interpreter->instructions = instructions;
+ interpreter->call_depth--;
+}
+
+/* Set the detailed rounding state in interpreter, on behalf of either
+ an SROUND or S45ROUND instruction that has been given the operand
+ OPERAND.
+
+ Use the specified GRID_PERIOD to determine the period. It is is a
+ 18.14 fixed point number, but the rounding state set will be a 26.6
+ fixed point number. */
+
+static void
+sfnt_set_srounding_state (struct sfnt_interpreter *interpreter,
+ uint32_t operand, sfnt_f18dot14 grid_period)
+{
+ sfnt_f18dot14 period, phase, threshold;
+
+ /* The most significant 2 bits in the 8 bit OPERAND determine the
+ period. */
+
+ switch ((operand & 0xc0) >> 6)
+ {
+ case 0:
+ period = grid_period / 2;
+ break;
+
+ case 1:
+ period = grid_period;
+ break;
+
+ case 2:
+ period = grid_period * 2;
+ break;
+
+ case 3:
+ default:
+ TRAP ("reserved period given to SROUND");
+ }
+
+ /* The next two bits determine the phase. */
+
+ switch ((operand & 0x30) >> 4)
+ {
+ case 0:
+ phase = 0;
+ break;
+
+ case 1:
+ phase = period / 4;
+ break;
+
+ case 2:
+ phase = period / 2;
+ break;
+
+ case 3:
+ default:
+ phase = period * 3 / 2;
+ break;
+ }
+
+ /* And the least significant 4 bits determine the threshold. */
+
+ if (operand & 0x0f)
+ threshold = (((int) (operand & 0x0f) - 4)
+ * period / 8);
+ else
+ threshold = period - 1;
+
+ /* Now extend these values to 26.6 format and set them. */
+ interpreter->period = period >> 8;
+ interpreter->phase = phase >> 8;
+ interpreter->threshold = threshold >> 8;
+}
+
+/* Move to the next opcode in INTERPRETER's instruction stream.
+ Value is the opcode originally at INTERPRETER->IP. */
+
+static unsigned char
+sfnt_skip_code (struct sfnt_interpreter *interpreter)
+{
+ unsigned char opcode;
+ int nbytes;
+
+ if (interpreter->IP == interpreter->num_instructions)
+ TRAP ("IP at end of instruction stream");
+
+ /* Load opcode at IP. */
+ opcode = interpreter->instructions[interpreter->IP];
+
+ if (opcode == 0x40 || opcode == 0x41)
+ {
+ if (interpreter->IP + 1 >= interpreter->num_instructions)
+ TRAP ("Missing arg to NPUSHB or NPUSHW");
+
+ /* Figure out how many bytes or words to push. */
+
+ nbytes = interpreter->instructions[interpreter->IP + 1];
+
+ if (opcode == 0x41)
+ nbytes *= 2;
+
+ if (interpreter->IP + 2 + nbytes > interpreter->num_instructions)
+ TRAP ("args to NPUSH instruction lie outside IS");
+
+ /* Increment IP by so much. */
+ interpreter->IP += 2 + nbytes;
+ }
+ else if (opcode >= 0xb0 && opcode <= 0xb7)
+ {
+ nbytes = opcode - 0xb0 + 1;
+
+ if (interpreter->IP + 1 + nbytes > interpreter->num_instructions)
+ TRAP ("args to PUSHB instruction lie outide IS");
+
+ interpreter->IP += 1 + nbytes;
+ }
+ else if (opcode >= 0xb8 && opcode <= 0xbf)
+ {
+ nbytes = (opcode - 0xb8 + 1) * 2;
+
+ if (interpreter->IP + 1 + nbytes > interpreter->num_instructions)
+ TRAP ("args to PUSHW instruction lie outide IS");
+
+ interpreter->IP += 1 + nbytes;
+ }
+ else
+ interpreter->IP++;
+
+ return opcode;
+}
+
+/* Interpret the unimplemented operation OPCODE using INTERPRETER, and
+ the context WHY. If there is no instruction definition named
+ OPCODE, trap. */
+
+static void
+sfnt_interpret_unimplemented (struct sfnt_interpreter *interpreter,
+ unsigned char opcode,
+ enum sfnt_interpreter_run_context why)
+{
+ uint32_t i;
+ struct sfnt_interpreter_definition *def;
+
+ for (i = 0; i < interpreter->instruction_defs_size; ++i)
+ {
+ def = &interpreter->instruction_defs[i];
+
+ if (def->opcode == opcode)
+ {
+ if (!def->instructions)
+ TRAP ("** ERROR ** malformed internal instruction"
+ " definition");
+
+ sfnt_interpret_call (def, interpreter, why);
+ return;
+ }
+ }
+
+ TRAP ("invalid instruction");
+}
+
+/* Start a function definition in INTERPRETER, with the function
+ opcode OPCODE. */
+
+static void
+sfnt_interpret_fdef (struct sfnt_interpreter *interpreter,
+ uint32_t opcode)
+{
+ size_t i, num_fdefs;
+ int IP;
+ unsigned char instruction;
+
+ IP = interpreter->IP + 1;
+ num_fdefs = 0;
+
+ /* Now find an ENDF. */
+
+ while ((instruction = sfnt_skip_code (interpreter)) != 0x2d)
+ {
+ if (interpreter->IP >= interpreter->num_instructions)
+ TRAP ("missing ENDF");
+
+ /* If this is an FDEF or IDEF instruction, increment num_fdefs.
+ Prohibit nested FDEFs or IDEFS. */
+ if (instruction == 0x2c || instruction == 0x89)
+ ++num_fdefs;
+
+ if (num_fdefs > 1)
+ TRAP ("IDEF or FDEF before ENDF");
+ }
+
+ /* ENDF has been found. Now save the function definition. Try to
+ find an existing function definition with this opcode. If that
+ fails, make i the first available function definition. */
+
+ for (i = 0; i < interpreter->function_defs_size; ++i)
+ {
+ if (interpreter->function_defs[i].opcode == opcode
+ || !interpreter->function_defs[i].instructions)
+ break;
+ }
+
+ if (i == interpreter->function_defs_size)
+ TRAP ("number of fdefs exceeded maxp->max_function_defs");
+
+ /* Save the opcode of this function definition. */
+ interpreter->function_defs[i].opcode = opcode;
+
+ /* Make sure to ignore the trailing ENDF instruction. */
+ interpreter->function_defs[i].instruction_count
+ = interpreter->IP - IP - 1;
+
+ /* Now save a pointer to the instructions. */
+ interpreter->function_defs[i].instructions = interpreter->instructions + IP;
+}
+
+/* Start an instruction definition in INTERPRETER, with the
+ instruction opcode OPCODE. */
+
+static void
+sfnt_interpret_idef (struct sfnt_interpreter *interpreter,
+ uint32_t opcode)
+{
+ size_t i, num_fdefs;
+ int IP;
+ unsigned char instruction;
+
+ IP = interpreter->IP + 1;
+ num_fdefs = 0;
+
+ /* Now find an ENDF. */
+
+ while ((instruction = sfnt_skip_code (interpreter)) != 0x2d)
+ {
+ if (interpreter->IP >= interpreter->num_instructions)
+ TRAP ("missing ENDF");
+
+ /* If this is an FDEF or IDEF instruction, increment num_fdefs.
+ Prohibit nested FDEFs or IDEFS. */
+ if (instruction == 0x2c || instruction == 0x89)
+ ++num_fdefs;
+
+ if (num_fdefs > 1)
+ TRAP ("IDEF or FDEF before ENDF");
+ }
+
+ /* ENDF has been found. Now save the instruction definition. Try to
+ find an existing instruction definition with this opcode. If that
+ fails, make i the first available instruction definition. */
+
+ for (i = 0; i < interpreter->instruction_defs_size; ++i)
+ {
+ if (interpreter->instruction_defs[i].opcode == opcode
+ || !interpreter->instruction_defs[i].instructions)
+ break;
+ }
+
+ if (i == interpreter->instruction_defs_size)
+ TRAP ("number of defs exceeded maxp->max_instruction_defs");
+
+ /* Save the opcode of this instruction definition. */
+ interpreter->instruction_defs[i].opcode = opcode;
+
+ /* Make sure to ignore the trailing ENDF instruction. */
+ interpreter->instruction_defs[i].instruction_count
+ = interpreter->IP - IP - 1;
+
+ /* Now save a pointer to the instructions. */
+ interpreter->instruction_defs[i].instructions
+ = interpreter->instructions + IP;
+}
+
+/* Interpret the specified conditional at INTERPRETER->IP.
+ If CONDITION, evaluate this branch up until the next ELSE or ENDIF.
+ Else, evaluate the branch from a matching ELSE condition, if
+ one exists. */
+
+static void
+sfnt_interpret_if (struct sfnt_interpreter *interpreter,
+ bool condition)
+{
+ int nifs;
+ bool need_break;
+ unsigned char opcode;
+
+ if (condition)
+ {
+ interpreter->IP++;
+ return;
+ }
+
+ /* Number of ifs. */
+ nifs = 0;
+ need_break = false;
+
+ /* Break past the matching else condition. */
+ do
+ {
+ /* Load the current opcode, then increase IP. */
+ opcode = sfnt_skip_code (interpreter);
+
+ if (interpreter->IP >= interpreter->num_instructions)
+ break;
+
+ switch (opcode)
+ {
+ case 0x58: /* IF */
+ nifs++;
+ break;
+
+ case 0x1B: /* ELSE */
+ if (nifs == 1)
+ need_break = true;
+
+ break;
+
+ case 0x59: /* EIF */
+ nifs--;
+ if (nifs == 0)
+ need_break = true;
+
+ break;
+ }
+ }
+ while (!need_break);
+}
+
+/* Interpret the specified ELSE branch at INTERPRETER->IP.
+ Evaluate starting from a matching ENDIF instruction.
+
+ If IF has set INTERPRETER->IP to a code within an ELSE branch, this
+ will not be called. */
+
+static void
+sfnt_interpret_else (struct sfnt_interpreter *interpreter)
+{
+ int nifs;
+ unsigned char opcode;
+
+ /* Number of ifs. */
+ nifs = 1;
+
+ /* Break past the matching ENDIF condition. */
+ do
+ {
+ /* Load the current opcode, then increase IP. */
+ opcode = sfnt_skip_code (interpreter);
+
+ if (interpreter->IP >= interpreter->num_instructions)
+ break;
+
+ switch (opcode)
+ {
+ case 0x58: /* IF */
+ nifs++;
+ break;
+
+ case 0x59: /* EIF */
+ nifs--;
+
+ break;
+ }
+ }
+ while (nifs > 0);
+}
+
+/* ``Add engine compensation to X''. Since engine compensation is not
+ implemented here, this simply returns X. INTERPRETER is
+ unused. */
+
+static sfnt_f26dot6
+sfnt_round_none (sfnt_f26dot6 x, struct sfnt_interpreter *interpreter)
+{
+ return x;
+}
+
+/* Round X to the grid after adding engine compensation. Return the
+ result. INTERPRETER is unused. */
+
+static sfnt_f26dot6
+sfnt_round_to_grid (sfnt_f26dot6 x, struct sfnt_interpreter *interpreter)
+{
+ return sfnt_round_f26dot6 (x);
+}
+
+/* Round X to the nearest half integer or integer and return the
+ result. INTERPRETER is unused. */
+
+static sfnt_f26dot6
+sfnt_round_to_double_grid (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ return (x + 020) & ~037;
+}
+
+/* Take the floor of X and return the result. INTERPRETER is
+ unused. */
+
+static sfnt_f26dot6
+sfnt_round_down_to_grid (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_floor_f26dot6 (x);
+}
+
+/* Take the ceiling of X and return the result. INTERPRETER is
+ unused. */
+
+static sfnt_f26dot6
+sfnt_round_up_to_grid (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_ceil_f26dot6 (x);
+}
+
+/* Round X to only the nearest half integer and return the result.
+ INTERPRETER is unused. */
+
+static sfnt_f26dot6
+sfnt_round_to_half_grid (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_floor_f26dot6 (x) + 32;
+}
+
+/* Round X using the detailed rounding information ``super rounding
+ state'' in INTERPRETER. Value is the result. */
+
+static sfnt_f26dot6
+sfnt_round_super (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ sfnt_f26dot6 value;
+
+ /* Compute the rounded value. */
+ value = sfnt_add ((interpreter->threshold
+ - interpreter->phase), x);
+ value = sfnt_add (value & -interpreter->period,
+ interpreter->phase);
+
+ /* Remember that since the phase is specified by font instructions,
+ it is possible for the sign to be changed. In that case, return
+ the phase itself. */
+
+ return value < 0 ? interpreter->phase : value;
+}
+
+/* Round X using the detailed rounding information ``super rounding
+ state'' in INTERPRETER, but suitably for values that are multiples
+ of the sqrt of 2. Value is the result. */
+
+static sfnt_f26dot6
+sfnt_round_super45 (sfnt_f26dot6 x,
+ struct sfnt_interpreter *interpreter)
+{
+ sfnt_f26dot6 value;
+
+ /* Compute the rounded value. */
+
+ value = ((sfnt_add (x, (interpreter->threshold
+ - interpreter->phase))
+ / interpreter->period)
+ * interpreter->period);
+ value = sfnt_add (value, interpreter->phase);
+
+ /* Remember that since the phase is specified by font instructions,
+ it is possible for the sign to be changed. In that case, return
+ the phase itself. */
+
+ return value < 0 ? interpreter->phase : value;
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+ INTERPRETER's projection vector, assuming that INTERPRETER's
+ projection vector is on the X axis.
+
+ Value is the magnitude of the projected vector. */
+
+static sfnt_f26dot6
+sfnt_project_onto_x_axis_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_interpreter *interpreter)
+{
+ return vx;
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+ INTERPRETER's projection vector, assuming that INTERPRETER's
+ projection vector is on the Y axis.
+
+ Value is the magnitude of the projected vector. */
+
+static sfnt_f26dot6
+sfnt_project_onto_y_axis_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_interpreter *interpreter)
+{
+ return vy;
+}
+
+/* Calculate AX * BX + AY * BY divided by 16384. */
+
+static int32_t
+sfnt_dot_fix_14 (int32_t ax, int32_t ay, int bx, int by)
+{
+#ifndef INT64_MAX
+ int32_t m, s, hi1, hi2, hi;
+ uint32_t l, lo1, lo2, lo;
+
+
+ /* Compute ax*bx as 64-bit value. */
+ l = (uint32_t) ((ax & 0xffffu) * bx);
+ m = (ax >> 16) * bx;
+
+ lo1 = l + ((uint32_t) m << 16);
+ hi1 = (m >> 16) + ((int32_t) l >> 31) + (lo1 < l);
+
+ /* Compute ay*by as 64-bit value. */
+ l = (uint32_t) ((ay & 0xffffu) * by);
+ m = (ay >> 16) * by;
+
+ lo2 = l + ((uint32_t) m << 16);
+ hi2 = (m >> 16) + ((int32_t) l >> 31) + (lo2 < l);
+
+ /* Add them. */
+ lo = lo1 + lo2;
+ hi = hi1 + hi2 + (lo < lo1);
+
+ /* Divide the result by 2^14 with rounding. */
+ s = hi >> 31;
+ l = lo + (uint32_t) s;
+ hi += s + (l < lo);
+ lo = l;
+
+ l = lo + 0x2000u;
+ hi += (l < lo);
+
+ return (int32_t) (((uint32_t) hi << 18) | (l >> 14));
+#else
+ int64_t xx, yy;
+
+ xx = (int64_t) ax * bx;
+ yy = (int64_t) ay * by;
+
+ xx += yy;
+ yy = xx >> 63;
+ xx += 0x2000 + yy;
+
+ return (int32_t) (xx / (2 << 14));
+#endif
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+ INTERPRETER's projection vector, making only the assumption that the
+ projection vector is a valid unit vector.
+
+ Value is the magnitude of the projected vector. */
+
+static sfnt_f26dot6
+sfnt_project_onto_any_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_dot_fix_14 (vx, vy,
+ interpreter->state.projection_vector.x,
+ interpreter->state.projection_vector.y);
+}
+
+/* Project the specified vector VX and VY onto the unit vector that is
+ INTERPRETER's dual projection vector, making only the assumption
+ that the dual projection vector is a valid unit vector.
+
+ The dual projection vector is a vector that is normally the
+ projection vector, but can be set using the original unscaled
+ coordinates of two points as well.
+
+ Value is the magnitude of the projected vector. */
+
+static sfnt_f26dot6
+sfnt_dual_project_onto_any_vector (sfnt_f26dot6 vx, sfnt_f26dot6 vy,
+ struct sfnt_interpreter *interpreter)
+{
+ return sfnt_dot_fix_14 (vx, vy,
+ interpreter->state.dual_projection_vector.x,
+ interpreter->state.dual_projection_vector.y);
+}
+
+/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
+ vector. Set N flags in *FLAGS where appropriate and when non-NULL.
+
+ Assume both vectors are aligned to the X axis. */
+
+static void
+sfnt_move_x (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+ size_t n, struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 distance, unsigned char *flags)
+{
+ while (n--)
+ {
+ *x = sfnt_add (*x, distance);
+ x++;
+
+ if (flags)
+ *flags++ |= SFNT_POINT_TOUCHED_X;
+ }
+}
+
+/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
+ vector. Set N flags in *FLAGS where appropriate and when non-NULL.
+
+ Assume both vectors are aligned to the Y axis. */
+
+static void
+sfnt_move_y (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+ size_t n, struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 distance, unsigned char *flags)
+{
+ while (n--)
+ {
+ *y = sfnt_add (*y, distance);
+ y++;
+
+ if (flags)
+ *flags++ |= SFNT_POINT_TOUCHED_Y;
+ }
+}
+
+/* Move N points at *X, *Y by DISTANCE along INTERPRETER's freedom
+ vector. Set N flags in *FLAGS where appropriate and when
+ non-NULL. */
+
+static void
+sfnt_move (sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+ size_t n, struct sfnt_interpreter *interpreter,
+ sfnt_f26dot6 distance, unsigned char *flags)
+{
+ sfnt_f26dot6 versor, k;
+ sfnt_f2dot14 dot_product;
+ size_t num;
+
+ dot_product = interpreter->state.vector_dot_product;
+
+ /* If the vectors are orthogonal, it is impossible to move anywhere,
+ so simply return. */
+ if (!dot_product)
+ return;
+
+ /* Not actually 26.6, but the multiply-divisions below cancel each
+ other out, so the result is 26.6. */
+ versor = interpreter->state.freedom_vector.x;
+
+ if (versor)
+ {
+ /* Move along X axis, converting the distance to the freedom
+ vector. */
+ num = n;
+ k = sfnt_multiply_divide_signed (distance,
+ versor,
+ dot_product);
+
+ while (num--)
+ {
+ *x = sfnt_add (*x, k);
+ x++;
+
+ if (flags)
+ *flags++ |= SFNT_POINT_TOUCHED_X;
+ }
+ }
+
+ versor = interpreter->state.freedom_vector.y;
+
+ if (versor)
+ {
+ /* Move along X axis, converting the distance to the freedom
+ vector. */
+ num = n;
+ k = sfnt_multiply_divide_signed (distance,
+ versor,
+ dot_product);
+
+ while (num--)
+ {
+ *y = sfnt_add (*y, k);
+ y++;
+
+ if (flags)
+ *flags++ |= SFNT_POINT_TOUCHED_Y;
+ }
+ }
+}
+
+/* Validate the graphics state GS.
+ Establish function pointers for rounding and projection.
+ Establish dot product used to convert vector distances between
+ each other. */
+
+static void
+sfnt_validate_gs (struct sfnt_graphics_state *gs)
+{
+ /* Establish the function used for rounding based on the round
+ state. */
+
+ switch (gs->round_state)
+ {
+ case 5: /* Rounding off. */
+ gs->round = sfnt_round_none;
+ break;
+
+ case 0: /* Round to half grid. */
+ gs->round = sfnt_round_to_half_grid;
+ break;
+
+ case 1: /* Round to grid. */
+ gs->round = sfnt_round_to_grid;
+ break;
+
+ case 2: /* Round to double grid. */
+ gs->round = sfnt_round_to_double_grid;
+ break;
+
+ case 4: /* Round up to grid. */
+ gs->round = sfnt_round_up_to_grid;
+ break;
+
+ case 3: /* Round down to grid. */
+ gs->round = sfnt_round_down_to_grid;
+ break;
+
+ case 6: /* Fine grained rounding. */
+ gs->round = sfnt_round_super;
+ break;
+
+ case 7: /* Fine grained rounding 45 degree variant. */
+ gs->round = sfnt_round_super45;
+ break;
+ }
+
+ /* Establish the function used for vector projection.
+ When the projection vector is an axis vector, a fast
+ version can be used. */
+
+ if (gs->projection_vector.x == 040000)
+ gs->project = sfnt_project_onto_x_axis_vector;
+ else if (gs->projection_vector.y == 040000)
+ gs->project = sfnt_project_onto_y_axis_vector;
+ else
+ gs->project = sfnt_project_onto_any_vector;
+
+ /* Do the same for the dual projection vector. */
+
+ if (gs->dual_projection_vector.x == 040000)
+ gs->dual_project = sfnt_project_onto_x_axis_vector;
+ else if (gs->dual_projection_vector.y == 040000)
+ gs->dual_project = sfnt_project_onto_y_axis_vector;
+ else
+ gs->dual_project = sfnt_dual_project_onto_any_vector;
+
+ /* Compute dot product of the freedom and projection vectors.
+ Handle the common case where the freedom vector is aligned
+ to an axis. */
+
+ if (gs->freedom_vector.x == 040000)
+ gs->vector_dot_product = gs->projection_vector.x;
+ else if (gs->freedom_vector.y == 040000)
+ gs->vector_dot_product = gs->projection_vector.y;
+ else
+ /* Actually calculate the dot product. */
+ gs->vector_dot_product = ((((long) gs->projection_vector.x
+ * gs->freedom_vector.x)
+ + ((long) gs->projection_vector.y
+ * gs->freedom_vector.y))
+ / 16384);
+
+ /* Now figure out which function to use to move distances. Handle
+ the common case where both the freedom and projection vectors are
+ aligned to an axis. */
+
+ if (gs->freedom_vector.x == 040000
+ && gs->projection_vector.x == 040000)
+ gs->move = sfnt_move_x;
+ else if (gs->freedom_vector.y == 040000
+ && gs->projection_vector.y == 040000)
+ gs->move = sfnt_move_y;
+ else
+ gs->move = sfnt_move;
+}
+
+/* Set the X and Y versors of the freedom vector of INTERPRETER's
+ graphics state to the specified X and Y, in 2.14 fixed point
+ format. */
+
+static void
+sfnt_set_freedom_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f2dot14 x, sfnt_f2dot14 y)
+{
+ interpreter->state.freedom_vector.x = x;
+ interpreter->state.freedom_vector.y = y;
+
+ sfnt_validate_gs (&interpreter->state);
+}
+
+/* Set the X and Y versors of the projection vector of INTERPRETER's
+ graphics state to the specified X and Y, in 2.14 fixed point
+ format. */
+
+static void
+sfnt_set_projection_vector (struct sfnt_interpreter *interpreter,
+ sfnt_f2dot14 x, sfnt_f2dot14 y)
+{
+ interpreter->state.projection_vector.x = x;
+ interpreter->state.projection_vector.y = y;
+ interpreter->state.dual_projection_vector.x = x;
+ interpreter->state.dual_projection_vector.y = y;
+
+ sfnt_validate_gs (&interpreter->state);
+}
+
+/* Interpret an SHZ instruction with the specified OPCODE. Like
+ sfnt_interpret_shc, but do the move for each point in the entire
+ specified ZONE. */
+
+static void
+sfnt_interpret_shz (struct sfnt_interpreter *interpreter,
+ uint32_t zone, unsigned int opcode)
+{
+ sfnt_f26dot6 x, y, original_x, original_y;
+ sfnt_f26dot6 magnitude;
+
+ if (zone != 0 && !interpreter->glyph_zone)
+ /* There are no points in the glyph zone. */
+ return;
+
+ if (opcode == 0x37)
+ sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+ &x, &y, &original_x, &original_y);
+ else
+ sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+ &x, &y, &original_x, &original_y);
+
+ magnitude = sfnt_project_vector (interpreter,
+ sfnt_sub (x, original_x),
+ sfnt_sub (y, original_y));
+
+ if (zone == 0)
+ sfnt_move_twilight_zone (interpreter, 0,
+ interpreter->twilight_zone_size,
+ magnitude);
+ else
+ sfnt_move_glyph_zone (interpreter, 0,
+ interpreter->glyph_zone->num_points,
+ magnitude);
+}
+
+/* Interpret an SHC instruction with the specified OPCODE and CONTOUR.
+ Like sfnt_interpret_shp, but do the move for each point in the
+ specified contour. */
+
+static void
+sfnt_interpret_shc (struct sfnt_interpreter *interpreter,
+ uint32_t contour, unsigned int opcode)
+{
+ sfnt_f26dot6 x, y, original_x, original_y;
+ sfnt_f26dot6 magnitude;
+ size_t start, end, n;
+
+ if (!interpreter->glyph_zone)
+ TRAP ("SHC without glyph zone");
+
+ /* Check that the contour is within bounds. */
+ if (contour >= interpreter->glyph_zone->num_contours)
+ TRAP ("contour out of bounds");
+
+ /* Figure out the magnitude of the change, measured from the
+ projection vector. */
+
+ if (opcode == 0x35)
+ sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+ &x, &y, &original_x, &original_y);
+ else
+ sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+ &x, &y, &original_x, &original_y);
+
+ magnitude = sfnt_project_vector (interpreter,
+ sfnt_sub (x, original_x),
+ sfnt_sub (y, original_y));
+
+ /* Now obtain the start and end of the contour.
+ Verify that both are valid. */
+
+ if (contour)
+ start = interpreter->glyph_zone->contour_end_points[contour - 1];
+ else
+ start = 0;
+
+ end = interpreter->glyph_zone->contour_end_points[contour];
+
+ if (start > end || end >= interpreter->glyph_zone->num_points)
+ TRAP ("invalid contour data in glyph");
+
+ /* Compute the number of points to move. */
+ n = end - start + 1;
+
+ /* Move that many points. */
+ sfnt_move_glyph_zone (interpreter, start, n, magnitude);
+}
+
+/* Interpret an SHP instruction with the specified OPCODE. Move a
+ popped point in ZP2 along the freedom vector by the distance
+ between a specified point from its original position, which is RP1
+ in ZP0 if OPCODE is 0x33, and RP2 in ZP1 if OPCODE is 0x32.
+
+ Repeat for the number of iterations specified by a prior SLOOP
+ instruction. */
+
+static void
+sfnt_interpret_shp (struct sfnt_interpreter *interpreter,
+ unsigned int opcode)
+{
+ sfnt_f26dot6 x, y, original_x, original_y;
+ sfnt_f26dot6 magnitude;
+ uint32_t point;
+
+ /* Figure out the magnitude of the change, measured from the
+ projection vector. */
+
+ if (opcode == 0x33)
+ sfnt_address_zp0 (interpreter, interpreter->state.rp1,
+ &x, &y, &original_x, &original_y);
+ else
+ sfnt_address_zp1 (interpreter, interpreter->state.rp2,
+ &x, &y, &original_x, &original_y);
+
+ magnitude = sfnt_project_vector (interpreter,
+ sfnt_sub (x, original_x),
+ sfnt_sub (y, original_y));
+
+ /* Now project it onto the freedom vector and move the point that
+ much for loop variable times. */
+
+ while (interpreter->state.loop--)
+ {
+ point = POP ();
+
+ sfnt_check_zp2 (interpreter, point);
+ sfnt_move_zp2 (interpreter, point, 1, magnitude);
+ }
+
+ /* Restore interpreter->state.loop to 1. */
+ interpreter->state.loop = 1;
+}
+
+#define load_point(p) \
+ (opcode == 0x31 \
+ ? interpreter->glyph_zone->x_current[p] \
+ : interpreter->glyph_zone->y_current[p])
+
+#define store_point(p, val) \
+ (opcode == 0x31 \
+ ? (interpreter->glyph_zone->x_current[p] = (val)) \
+ : (interpreter->glyph_zone->y_current[p] = (val)))
+
+#define load_original(p) \
+ (opcode == 0x31 \
+ ? interpreter->glyph_zone->x_points[p] \
+ : interpreter->glyph_zone->y_points[p])
+
+#define IUP_SINGLE_PAIR() \
+ /* Now make touch_start the first point before, i.e. the first \
+ touched point in this pair. */ \
+ \
+ if (touch_start == start) \
+ touch_start = end; \
+ else \
+ touch_start = touch_start - 1; \
+ \
+ /* Set point_min and point_max based on which glyph is at a \
+ lower value. */ \
+ \
+ if (load_original (touch_start) < load_original (touch_end)) \
+ { \
+ point_min = touch_start; \
+ point_max = touch_end; \
+ } \
+ else \
+ { \
+ point_max = touch_start; \
+ point_min = touch_end; \
+ } \
+ \
+ min_pos = load_point (point_min); \
+ max_pos = load_point (point_max); \
+ \
+ /* This is needed for interpolation. */ \
+ original_max_pos = load_original (point_max); \
+ original_min_pos = load_original (point_min); \
+ \
+ /* Now process points between touch_start and touch_end. */ \
+ \
+ i = touch_start + 1; \
+ \
+ /* touch_start might be the last point in the contour. */ \
+ \
+ if (i > end) \
+ i = start; \
+ \
+ while (i != touch_end) \
+ { \
+ /* Movement is always relative to the original position of \
+ the point. */ \
+ position = load_original (i); \
+ \
+ /* If i is in between touch_start and touch_end... */ \
+ if (position >= original_min_pos \
+ && position <= original_max_pos) \
+ { \
+ /* Handle the degenerate case where original_min_pos and \
+ original_max_pos have not changed by placing the point in \
+ the middle. */ \
+ if (original_min_pos == original_max_pos) \
+ ratio = 077777; \
+ else \
+ /* ... preserve the ratio of i between min_pos and \
+ max_pos... */ \
+ ratio = sfnt_div_fixed ((sfnt_sub (position, \
+ original_min_pos) \
+ * 1024), \
+ (sfnt_sub (original_max_pos, \
+ original_min_pos) \
+ * 1024)); \
+ \
+ delta = sfnt_sub (max_pos, min_pos); \
+ delta = sfnt_mul_fixed (ratio, delta); \
+ store_point (i, sfnt_add (min_pos, delta)); \
+ } \
+ else \
+ { \
+ /* ... otherwise, move i by how much the nearest touched \
+ point moved. */ \
+ \
+ if (position >= original_max_pos) \
+ delta = sfnt_sub (max_pos, original_max_pos); \
+ else \
+ delta = sfnt_sub (min_pos, original_min_pos); \
+ \
+ store_point (i, sfnt_add (position, delta)); \
+ } \
+ \
+ if (++i > end) \
+ i = start; \
+ } \
+
+/* Interpolate untouched points in the contour between and including
+ START and END inside INTERPRETER's glyph zone according to the
+ rules specified for an IUP instruction. Perform interpolation on
+ the axis specified by OPCODE and MASK. */
+
+static void
+sfnt_interpret_iup_1 (struct sfnt_interpreter *interpreter,
+ size_t start, size_t end,
+ unsigned char opcode, int mask)
+{
+ size_t point;
+ size_t touch_start, touch_end;
+ size_t first_point;
+ size_t point_min, point_max, i;
+ sfnt_f26dot6 position, min_pos, max_pos, delta, ratio;
+ sfnt_f26dot6 original_max_pos;
+ sfnt_f26dot6 original_min_pos;
+
+ /* Find the first touched point. If none is found, simply
+ return. */
+
+ for (point = start; point <= end; ++point)
+ {
+ if (interpreter->glyph_zone->flags[point] & mask)
+ goto touched;
+ }
+
+ goto untouched;
+
+ touched:
+
+ point = start;
+
+ /* Find the first touched point. */
+ while (!(interpreter->glyph_zone->flags[point] & mask))
+ {
+ point++;
+
+ /* There are no touched points. */
+ if (point > end)
+ goto untouched;
+ }
+
+ first_point = point;
+
+ while (point <= end)
+ {
+ /* Find the next untouched point. */
+ while (interpreter->glyph_zone->flags[point] & mask)
+ {
+ point++;
+
+ if (point > end)
+ goto wraparound;
+ }
+
+ /* touch_start is now the first untouched point. */
+ touch_start = point;
+
+ /* Find the next touched point. */
+ while (!(interpreter->glyph_zone->flags[point] & mask))
+ {
+ point++;
+
+ /* Move back to start if point has gone past the end of the
+ contour. */
+ if (point > end)
+ goto wraparound_1;
+ }
+
+ /* touch_end is now the next touched point. */
+ touch_end = point;
+
+ /* Do the interpolation. */
+ IUP_SINGLE_PAIR ();
+ }
+
+ goto untouched;
+
+ wraparound:
+ /* This is like wraparound_1, except that no untouched points have
+ yet to be found.
+
+ This means the first untouched point is start. */
+ touch_start = start;
+
+ wraparound_1:
+ /* If point > end, wrap around. Here, touch_start is set
+ properly, so touch_end must be first_point. */
+
+ touch_end = first_point;
+ IUP_SINGLE_PAIR ();
+
+ untouched:
+ /* No points were touched or all points have been considered, so
+ return immediately. */
+ return;
+}
+
+#undef load_point
+#undef store_point
+#undef load_original
+
+/* Interpret an IUP (``interpolate untouched points'') instruction.
+ INTERPRETER is the interpreter, and OPCODE is the instruction
+ number. See the TrueType Reference Manual for more details. */
+
+static void
+sfnt_interpret_iup (struct sfnt_interpreter *interpreter,
+ unsigned char opcode)
+{
+ int mask;
+ size_t i, point, end, first_point;
+
+ /* Check that the zone is the glyph zone. */
+
+ if (!interpreter->state.zp2)
+ TRAP ("trying to iup in twilight zone");
+
+ if (!interpreter->glyph_zone)
+ TRAP ("iup without loaded glyph!");
+
+ /* Figure out what axis to interpolate in based on the opcode. */
+ if (opcode == 0x30)
+ mask = SFNT_POINT_TOUCHED_Y;
+ else
+ mask = SFNT_POINT_TOUCHED_X;
+
+ /* Now, for each contour, interpolate untouched points. */
+ point = 0;
+ for (i = 0; i < interpreter->glyph_zone->num_contours; ++i)
+ {
+ first_point = point;
+ end = interpreter->glyph_zone->contour_end_points[i];
+
+ if (point >= interpreter->glyph_zone->num_points
+ || end >= interpreter->glyph_zone->num_points)
+ TRAP ("glyph contains out of bounds contour end point"
+ " data!");
+
+ sfnt_interpret_iup_1 (interpreter, first_point, end,
+ opcode, mask);
+ point = end + 1;
+
+ /* Skip the subsequent phantom points, which may end up
+ intermixed with contours inside a compound glyph. */
+
+ while (point < interpreter->glyph_zone->num_points
+ && interpreter->glyph_zone->flags[point] & SFNT_POINT_PHANTOM)
+ point++;
+ }
+}
+
+/* Interpret an MIRP instruction with the specified OPCODE in
+ INTERPRETER. Pop a point in ZP1 and CVT index, and move the point
+ until its distance from RP0 in ZP0 is the same as in the control
+ value. If the point lies in the twilight zone, then ``create'' it
+ as well.
+
+ OPCODE contains a great many flags.
+ They are all described in the TrueType reference manual. */
+
+static void
+sfnt_interpret_mirp (struct sfnt_interpreter *interpreter,
+ uint32_t opcode)
+{
+ uint32_t n;
+ uint32_t p;
+ sfnt_f26dot6 distance, delta, temp;
+ sfnt_f26dot6 current_projection, original_projection;
+ sfnt_f26dot6 x, y, org_x, org_y;
+ sfnt_f26dot6 rx, ry, org_rx, org_ry;
+
+ /* CVT index. */
+ n = POP ();
+
+ /* Point number. */
+ p = POP ();
+
+ /* Now get the distance from the CVT. */
+ if (n >= interpreter->cvt_size)
+ TRAP ("cvt index out of bounds");
+
+ distance = interpreter->cvt[n];
+
+ /* Test against the single width value. */
+
+ delta = sfnt_sub (distance,
+ interpreter->state.single_width_value);
+
+ if (delta < 0)
+ delta = -delta;
+
+ if (delta < interpreter->state.sw_cut_in)
+ {
+ /* Use the single width instead, as the CVT entry is too
+ small. */
+
+ if (distance >= 0)
+ distance = interpreter->state.single_width_value;
+ else
+ distance = -interpreter->state.single_width_value;
+ }
+
+ /* Load the reference point. */
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rx, &ry, &org_rx, &org_ry);
+
+ /* Create the point in the twilight zone, should that be ZP1. */
+
+ if (!interpreter->state.zp1)
+ {
+ /* Since P hasn't been loaded yet, whether or not it is valid is
+ not known. */
+ sfnt_check_zp1 (interpreter, p);
+
+ interpreter->twilight_x[p] = rx;
+ interpreter->twilight_y[p] = ry;
+
+ temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.x,
+ distance);
+ temp = sfnt_add (temp, org_rx);
+ interpreter->twilight_original_x[p] = temp;
+
+ temp = sfnt_mul_f2dot14 (interpreter->state.projection_vector.y,
+ distance);
+ temp = sfnt_add (temp, org_ry);
+ interpreter->twilight_original_y[p] = temp;
+ }
+
+ /* Load P. */
+ sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y);
+
+ /* If distance would be negative and auto_flip is on, flip it. */
+
+ original_projection = DUAL_PROJECT (org_x - org_rx,
+ org_y - org_ry);
+ current_projection = PROJECT (x - rx, y - ry);
+
+ if (interpreter->state.auto_flip)
+ {
+ if ((original_projection ^ distance) < 0)
+ distance = -distance;
+ }
+
+ /* Flag B means look at the cvt cut in and round the
+ distance. */
+
+ if (opcode & 4)
+ {
+ delta = sfnt_sub (distance, original_projection);
+
+ if (delta < 0)
+ delta = -delta;
+
+ if (delta > interpreter->state.cvt_cut_in)
+ distance = original_projection;
+
+ /* Now, round the distance. */
+ distance = sfnt_round_symmetric (interpreter, distance);
+ }
+
+ /* Flag C means look at the minimum distance. */
+
+ if (opcode & 8)
+ {
+ if (original_projection >= 0
+ && distance < interpreter->state.minimum_distance)
+ distance = interpreter->state.minimum_distance;
+ else if (original_projection < 0
+ && distance > -interpreter->state.minimum_distance)
+ distance = -interpreter->state.minimum_distance;
+ }
+
+ /* Finally, move the point. */
+ sfnt_move_zp1 (interpreter, p, 1,
+ sfnt_sub (distance, current_projection));
+
+ /* Set RP1 to RP0 and RP2 to the point. If flag 3 is set, also make
+ it RP0. */
+ interpreter->state.rp1 = interpreter->state.rp0;
+ interpreter->state.rp2 = p;
+
+ if (opcode & 16)
+ interpreter->state.rp0 = p;
+}
+
+/* Interpret an MDRP instruction with the specified OPCODE in
+ INTERPRETER. Pop a point in ZP1, and move the point until its
+ distance from RP0 in ZP0 is the same as in the original outline.
+
+ This is almost like MIRP[abcde].
+
+ OPCODE contains a great many flags.
+ They are all described in the TrueType reference manual. */
+
+static void
+sfnt_interpret_mdrp (struct sfnt_interpreter *interpreter,
+ uint32_t opcode)
+{
+ uint32_t p;
+ sfnt_f26dot6 distance, delta;
+ sfnt_f26dot6 current_projection, original_projection;
+ sfnt_f26dot6 x, y, org_x, org_y;
+ sfnt_f26dot6 rx, ry, org_rx, org_ry;
+
+ /* Point number. */
+ p = POP ();
+
+ /* Load the points. */
+ sfnt_address_zp1 (interpreter, p, &x, &y, &org_x, &org_y);
+ sfnt_address_zp0 (interpreter, interpreter->state.rp0,
+ &rx, &ry, &org_rx, &org_ry);
+
+ distance = DUAL_PROJECT (org_x - org_rx,
+ org_y - org_ry);
+ original_projection = distance;
+ current_projection = PROJECT (x - rx, y - ry);
+
+ /* Test against the single width value. */
+
+ delta = sfnt_sub (distance,
+ interpreter->state.single_width_value);
+
+ if (delta < 0)
+ delta = -delta;
+
+ if (delta < interpreter->state.sw_cut_in)
+ {
+ /* Use the single width instead, as the CVT entry is too
+ small. */
+
+ if (distance >= 0)
+ distance = interpreter->state.single_width_value;
+ else
+ distance = -interpreter->state.single_width_value;
+ }
+
+ /* Flag B means look at the cvt cut in and round the
+ distance. */
+
+ if (opcode & 4)
+ {
+ delta = sfnt_sub (distance, original_projection);
+
+ if (delta < 0)
+ delta = -delta;
+
+ if (delta > interpreter->state.cvt_cut_in)
+ distance = original_projection;
+
+ /* Now, round the distance. */
+ distance = sfnt_round_symmetric (interpreter, distance);
+ }
+
+ /* Flag C means look at the minimum distance. */
+
+ if (opcode & 8)
+ {
+ if (original_projection >= 0
+ && distance < interpreter->state.minimum_distance)
+ distance = interpreter->state.minimum_distance;
+ else if (original_projection < 0
+ && distance > -interpreter->state.minimum_distance)
+ distance = -interpreter->state.minimum_distance;
+ }
+
+ /* Finally, move the point. */
+ sfnt_move_zp1 (interpreter, p, 1,
+ sfnt_sub (distance, current_projection));
+
+ /* Set RP1 to RP0 and RP2 to the point. If flag 3 is set, also make
+ it RP0. */
+ interpreter->state.rp1 = interpreter->state.rp0;
+ interpreter->state.rp2 = p;
+
+ if (opcode & 16)
+ interpreter->state.rp0 = p;
+}
+
+/* Execute the program now loaded into INTERPRETER.
+ WHY specifies why the interpreter is being run, and is used to
+ control the behavior of instructions such IDEF[] and FDEF[].
+
+ Transfer control to INTERPRETER->trap if interpretation is aborted
+ due to an error, and set INTERPRETER->trap_reason to a string
+ describing the error.
+
+ INTERPRETER->glyph_zone should be cleared before calling this
+ function. */
+
+static void
+sfnt_interpret_run (struct sfnt_interpreter *interpreter,
+ enum sfnt_interpreter_run_context why)
+{
+ unsigned char opcode;
+ bool is_prep;
+
+ /* Determine whether or not this is the control value program. */
+ is_prep = (why == SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM);
+
+#ifdef TEST
+ /* Allow testing control value program instructions as well. */
+ if (why == SFNT_RUN_CONTEXT_TEST)
+ is_prep = true;
+#endif
+
+ while (interpreter->IP < interpreter->num_instructions)
+ {
+ opcode = interpreter->instructions[interpreter->IP];
+
+#ifdef TEST
+ if (interpreter->run_hook)
+ interpreter->run_hook (interpreter);
+#endif
+
+ switch (opcode)
+ {
+ case 0x00: /* SVTCA y */
+ SVTCAy ();
+ break;
+
+ case 0x01: /* SVTCA x */
+ SVTCAx ();
+ break;
+
+ case 0x02: /* SPvTCA y */
+ SPvTCAy ();
+ break;
+
+ case 0x03: /* SPvTCA x */
+ SPvTCAx ();
+ break;
+
+ case 0x04: /* SFvTCA y */
+ SFvTCAy ();
+ break;
+
+ case 0x05: /* SFvTCA x */
+ SFvTCAx ();
+ break;
+
+ case 0x06: /* SPvTL // */
+ case 0x07: /* SPvTL + */
+ SPVTL ();
+ break;
+
+ case 0x08: /* SFvTL // */
+ case 0x09: /* SFvTL + */
+ SFVTL ();
+ break;
+
+ case 0x0A: /* SPvFS */
+ SPVFS ();
+ break;
+
+ case 0x0B: /* SFvFS */
+ SFVFS ();
+ break;
+
+ case 0x0C: /* GPv */
+ GPV ();
+ break;
+
+ case 0x0D: /* GFv */
+ GFV ();
+ break;
+
+ case 0x0E: /* SFvTPv */
+ SFVTPV ();
+ break;
+
+ case 0x0F: /* ISECT */
+ ISECT ();
+ break;
+
+ case 0x10: /* SRP0 */
+ SRP0 ();
+ break;
+
+ case 0x11: /* SRP1 */
+ SRP1 ();
+ break;
+
+ case 0x12: /* SRP2 */
+ SRP2 ();
+ break;
+
+ case 0x13: /* SZP0 */
+ SZP0 ();
+ break;
+
+ case 0x14: /* SZP1 */
+ SZP1 ();
+ break;
+
+ case 0x15: /* SZP2 */
+ SZP2 ();
+ break;
+
+ case 0x16: /* SZPS */
+ SZPS ();
+ break;
+
+ case 0x17: /* SLOOP */
+ SLOOP ();
+ break;
+
+ case 0x18: /* RTG */
+ RTG ();
+ break;
+
+ case 0x19: /* RTHG */
+ RTHG ();
+ break;
+
+ case 0x1A: /* SMD */
+ SMD ();
+ break;
+
+ case 0x1B: /* ELSE */
+ ELSE ();
+ break;
+
+ case 0x1C: /* JMPR */
+ JMPR ();
+ break;
+
+ case 0x1D: /* SCVTCI */
+ SCVTCI ();
+ break;
+
+ case 0x1E: /* SSWCI */
+ SSWCI ();
+ break;
+
+ case 0x1F: /* SSW */
+ SSW ();
+ break;
+
+ case 0x20: /* DUP */
+ DUP ();
+ break;
+
+ case 0x21: /* POP */
+ POP ();
+ break;
+
+ case 0x22: /* CLEAR */
+ CLEAR ();
+ break;
+
+ case 0x23: /* SWAP */
+ SWAP ();
+ break;
+
+ case 0x24: /* DEPTH */
+ DEPTH ();
+ break;
+
+ case 0x25: /* CINDEX */
+ CINDEX ();
+ break;
+
+ case 0x26: /* MINDEX */
+ MINDEX ();
+ break;
+
+ case 0x27: /* ALIGNPTS */
+ ALIGNPTS ();
+ break;
+
+ case 0x28: /* RAW */
+ RAW ();
+ break;
+
+ case 0x29: /* UTP */
+ UTP ();
+ break;
+
+ case 0x2A: /* LOOPCALL */
+ LOOPCALL ();
+ break;
+
+ case 0x2B: /* CALL */
+ CALL ();
+ break;
+
+ case 0x2C: /* FDEF */
+ FDEF ();
+ break;
+
+ case 0x2D: /* ENDF */
+ ENDF ();
+ break;
+
+ case 0x2E: /* MDAP */
+ case 0x2F: /* MDAP */
+ MDAP ();
+ break;
+
+ case 0x30: /* IUP */
+ case 0x31: /* IUP */
+ IUP ();
+ break;
+
+ case 0x32: /* SHP */
+ case 0x33: /* SHP */
+ SHP ();
+ break;
+
+ case 0x34: /* SHC */
+ case 0x35: /* SHC */
+ SHC ();
+ break;
+
+ case 0x36: /* SHZ */
+ case 0x37: /* SHZ */
+ SHZ ();
+ break;
+
+ case 0x38: /* SHPIX */
+ SHPIX ();
+ break;
+
+ case 0x39: /* IP */
+ IP ();
+ break;
+
+ case 0x3A: /* MSIRP */
+ case 0x3B: /* MSIRP */
+ MSIRP ();
+ break;
+
+ case 0x3C: /* ALIGNRP */
+ ALIGNRP ();
+ break;
+
+ case 0x3D: /* RTDG */
+ RTDG ();
+ break;
+
+ case 0x3E: /* MIAP */
+ case 0x3F: /* MIAP */
+ MIAP ();
+ break;
+
+ case 0x40: /* NPUSHB */
+ NPUSHB ();
+ break;
+
+ case 0x41: /* NPUSHW */
+ NPUSHW ();
+ break;
+
+ case 0x42: /* WS */
+ WS ();
+ break;
+
+ case 0x43: /* RS */
+ RS ();
+ break;
+
+ case 0x44: /* WCVTP */
+ WCVTP ();
+ break;
+
+ case 0x45: /* RCVT */
+ RCVT ();
+ break;
+
+ case 0x46: /* GC */
+ case 0x47: /* GC */
+ GC ();
+ break;
+
+ case 0x48: /* SCFS */
+ SCFS ();
+ break;
+
+ case 0x49: /* MD */
+ case 0x4A: /* MD */
+ MD ();
+ break;
+
+ case 0x4B: /* MPPEM */
+ MPPEM ();
+ break;
+
+ case 0x4C: /* MPS */
+ MPS ();
+ break;
+
+ case 0x4D: /* FLIPON */
+ FLIPON ();
+ break;
+
+ case 0x4E: /* FLIPOFF */
+ FLIPOFF ();
+ break;
+
+ case 0x4F: /* DEBUG */
+ DEBUG ();
+ break;
+
+ case 0x50: /* LT */
+ LT ();
+ break;
+
+ case 0x51: /* LTEQ */
+ LTEQ ();
+ break;
+
+ case 0x52: /* GT */
+ GT ();
+ break;
+
+ case 0x53: /* GTEQ */
+ GTEQ ();
+ break;
+
+ case 0x54: /* EQ */
+ EQ ();
+ break;
+
+ case 0x55: /* NEQ */
+ NEQ ();
+ break;
+
+ case 0x56: /* ODD */
+ ODD ();
+ break;
+
+ case 0x57: /* EVEN */
+ EVEN ();
+ break;
+
+ case 0x58: /* IF */
+ IF ();
+ break;
+
+ case 0x59: /* EIF */
+ EIF ();
+ break;
+
+ case 0x5A: /* AND */
+ AND ();
+ break;
+
+ case 0x5B: /* OR */
+ OR ();
+ break;
+
+ case 0x5C: /* NOT */
+ NOT ();
+ break;
+
+ case 0x5D: /* DELTAP1 */
+ DELTAP1 ();
+ break;
+
+ case 0x5E: /* SDB */
+ SDB ();
+ break;
+
+ case 0x5F: /* SDS */
+ SDS ();
+ break;
+
+ case 0x60: /* ADD */
+ ADD ();
+ break;
+
+ case 0x61: /* SUB */
+ SUB ();
+ break;
+
+ case 0x62: /* DIV */
+ DIV ();
+ break;
+
+ case 0x63: /* MUL */
+ MUL ();
+ break;
+
+ case 0x64: /* ABS */
+ ABS ();
+ break;
+
+ case 0x65: /* NEG */
+ NEG ();
+ break;
+
+ case 0x66: /* FLOOR */
+ FLOOR ();
+ break;
+
+ case 0x67: /* CEILING */
+ CEILING ();
+ break;
+
+ case 0x68: /* ROUND */
+ case 0x69: /* ROUND */
+ case 0x6A: /* ROUND */
+ case 0x6B: /* ROUND */
+ ROUND ();
+ break;
+
+ case 0x6C: /* NROUND */
+ case 0x6D: /* NROUND */
+ case 0x6E: /* NRRUND */
+ case 0x6F: /* NROUND */
+ NROUND ();
+ break;
+
+ case 0x70: /* WCVTF */
+ WCVTF ();
+ break;
+
+ case 0x71: /* DELTAP2 */
+ DELTAP2 ();
+ break;
+
+ case 0x72: /* DELTAP3 */
+ DELTAP3 ();
+ break;
+
+ case 0x73: /* DELTAC1 */
+ DELTAC1 ();
+ break;
+
+ case 0x74: /* DELTAC2 */
+ DELTAC2 ();
+ break;
+
+ case 0x75: /* DELTAC3 */
+ DELTAC3 ();
+ break;
+
+ case 0x76: /* SROUND */
+ SROUND ();
+ break;
+
+ case 0x77: /* S45Round */
+ S45ROUND ();
+ break;
+
+ case 0x78: /* JROT */
+ JROT ();
+ break;
+
+ case 0x79: /* JROF */
+ JROF ();
+ break;
+
+ case 0x7A: /* ROFF */
+ ROFF ();
+ break;
+
+ case 0x7B: /* ILLEGAL_INSTRUCTION */
+ ILLEGAL_INSTRUCTION ();
+ break;
+
+ case 0x7C: /* RUTG */
+ RUTG ();
+ break;
+
+ case 0x7D: /* RDTG */
+ RDTG ();
+ break;
+
+ case 0x7E: /* SANGW */
+ SANGW ();
+ break;
+
+ case 0x7F: /* AA */
+ AA ();
+ break;
+
+ case 0x80: /* FLIPPT */
+ FLIPPT ();
+ break;
+
+ case 0x81: /* FLIPRGON */
+ FLIPRGON ();
+ break;
+
+ case 0x82: /* FLIPRGOFF */
+ FLIPRGOFF ();
+ break;
+
+ case 0x83: /* RMVT */
+ case 0x84: /* WMVT */
+ NOT_IMPLEMENTED ();
+ break;
+
+ case 0x85: /* SCANCTRL */
+ SCANCTRL ();
+ break;
+
+ case 0x86: /* SDPVTL */
+ case 0x87: /* SDPVTL */
+ SDPVTL ();
+ break;
+
+ case 0x88: /* GETINFO */
+ GETINFO ();
+ break;
+
+ case 0x89: /* IDEF */
+ IDEF ();
+ break;
+
+ case 0x8A: /* ROLL */
+ ROLL ();
+ break;
+
+ case 0x8B: /* MAX */
+ _MAX ();
+ break;
+
+ case 0x8C: /* MIN */
+ _MIN ();
+ break;
+
+ /* Scan or dropout control is not implemented. Instead, 256
+ grays are used to display pixels which are partially
+ turned on. */
+ case 0x8D: /* SCANTYPE */
+ SCANTYPE ();
+ break;
+
+ case 0x8E: /* INSTCTRL */
+ INSTCTRL ();
+ break;
+
+ case 0x8F: /* ADJUST */
+ case 0x90: /* ADJUST */
+ NOT_IMPLEMENTED ();
+ break;
+
+ case 0x91: /* GXAXIS */
+ GXAXIS ();
+ break;
+
+ default:
+ if (opcode >= 0xE0) /* MIRP */
+ {
+ MIRP ();
+ }
+ else if (opcode >= 0xC0) /* MDRP */
+ {
+ MDRP ();
+ }
+ else if (opcode >= 0xB8) /* PUSHW */
+ {
+ PUSHW ();
+ }
+ else if (opcode >= 0xB0) /* PUSHB */
+ {
+ PUSHB ();
+ }
+ else
+ NOT_IMPLEMENTED ();
+ }
+
+ /* In the case of an NPUSHB or NPUSHW instruction,
+ interpreter->IP has only been increased to skip over the
+ extra bytes, and not the byte containing the instruction
+ itself. */
+ interpreter->IP++;
+
+ /* This label is used by instructions to continue without
+ incrementing IP. It is used by instructions which set IP
+ themselves, such as ELSE, IF, FDEF, IDEF and JMPR. */
+ skip_step:
+ continue;
+ }
+}
+
+/* Execute the font program FPGM using INTERPRETER.
+ This must only be called once per interpreter, else behavior is
+ undefined.
+
+ Value is NULL upon success, else it is a string describing the
+ reason for failure.
+
+ The caller must save the graphics state after interpreting the font
+ program and restore it prior to instructing each glyph. */
+
+TEST_STATIC const char *
+sfnt_interpret_font_program (struct sfnt_interpreter *interpreter,
+ struct sfnt_fpgm_table *fpgm)
+{
+ if (setjmp (interpreter->trap))
+ return interpreter->trap_reason;
+
+ /* Set up the interpreter to evaluate the font program. */
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = fpgm->instructions;
+ interpreter->num_instructions = fpgm->num_instructions;
+ interpreter->glyph_zone = NULL;
+
+ sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_FONT_PROGRAM);
+ return NULL;
+}
+
+/* Execute the control value program PREP using INTERPRETER.
+
+ Return NULL and the graphics state after the execution of the
+ program in *STATE, or a string describing the reason for a failure
+ to interpret the program.
+
+ The caller must save the graphics state after interpreting the
+ control value program and restore it prior to instructing each
+ glyph. */
+
+TEST_STATIC const char *
+sfnt_interpret_control_value_program (struct sfnt_interpreter *interpreter,
+ struct sfnt_prep_table *prep,
+ struct sfnt_graphics_state *state)
+{
+ if (setjmp (interpreter->trap))
+ return interpreter->trap_reason;
+
+ /* Set up the interpreter to evaluate the control value program. */
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = prep->instructions;
+ interpreter->num_instructions = prep->num_instructions;
+ interpreter->glyph_zone = NULL;
+
+ sfnt_interpret_run (interpreter,
+ SFNT_RUN_CONTEXT_CONTROL_VALUE_PROGRAM);
+
+ /* If instruct_control & 4, then changes to the graphics state made
+ in this program should be reverted. */
+
+ if (interpreter->state.instruct_control & 4)
+ sfnt_init_graphics_state (&interpreter->state);
+
+ /* Save the graphics state upon success. */
+ memcpy (state, &interpreter->state, sizeof *state);
+ return NULL;
+}
+
+
+
+/* Glyph hinting. The routines here perform hinting on simple and
+ compound glyphs.
+
+ In order to keep the hinting mechanism separate from the rest of
+ the code, the routines here perform outline decomposition and
+ scaling separately. It might be nice to fix that in the
+ future. */
+
+/* Instructed glyph outline decomposition. This is separate from
+ sfnt_decompose_glyph because this file should be able to be built
+ with instructions disabled. */
+
+/* Decompose OUTLINE, an instructed outline, into its individual
+ components.
+
+ Call MOVE_TO to move to a specific location. For each line
+ encountered, call LINE_TO to draw a line to that location. For
+ each spline encountered, call CURVE_TO to draw the curves
+ comprising the spline. Call each of those functions with 16.16
+ fixed point coordinates.
+
+ Call all functions with DCONTEXT as an argument.
+
+ The winding rule used to fill the resulting lines is described in
+ chapter 2 of the TrueType reference manual, under the heading
+ "distinguishing the inside from the outside of a glyph."
+
+ Value is 0 upon success, or some non-zero value upon failure, which
+ can happen if the glyph is invalid. */
+
+static int
+sfnt_decompose_instructed_outline (struct sfnt_instructed_outline *outline,
+ sfnt_move_to_proc move_to,
+ sfnt_line_to_proc line_to,
+ sfnt_curve_to_proc curve_to,
+ void *dcontext)
+{
+ size_t here, last, n;
+
+ if (!outline->num_contours)
+ return 0;
+
+ here = 0;
+
+ for (n = 0; n < outline->num_contours; ++n)
+ {
+ /* here is the first index into the glyph's point arrays
+ belonging to the contour in question. last is the index
+ of the last point in the contour. */
+ last = outline->contour_end_points[n];
+
+ /* Make sure here and last make sense. */
+
+ if (here > last || last >= outline->num_points)
+ goto fail;
+
+ if (sfnt_decompose_glyph_2 (here, last, move_to,
+ line_to, curve_to, dcontext,
+ outline->x_points,
+ outline->y_points,
+ outline->flags, 1024))
+ goto fail;
+
+ /* Move forward to the start of the next contour. */
+ here = last + 1;
+
+ /* here may be a phantom point when outlining a compound glyph,
+ as they can have phantom points mixed in with contours.
+
+ When that happens, skip past all the phantom points. */
+
+ while (here < outline->num_points
+ && outline->flags[here] & SFNT_POINT_PHANTOM)
+ here++;
+ }
+
+ return 0;
+
+ fail:
+ return 1;
+}
+
+/* Decompose and build an outline for the specified instructed outline
+ INSTRUCTED. Return the outline data with a refcount of 0 upon
+ success, or NULL upon failure.
+
+ This function is not reentrant. */
+
+TEST_STATIC struct sfnt_glyph_outline *
+sfnt_build_instructed_outline (struct sfnt_instructed_outline *instructed)
+{
+ struct sfnt_glyph_outline *outline;
+ int rc;
+
+ memset (&build_outline_context, 0, sizeof build_outline_context);
+
+ /* Allocate the outline now with enough for 44 words at the end. */
+ outline = xmalloc (sizeof *outline + 40 * sizeof (*outline->outline));
+ outline->outline_size = 40;
+ outline->outline_used = 0;
+ outline->refcount = 0;
+ outline->outline
+ = (struct sfnt_glyph_outline_command *) (outline + 1);
+
+ /* Clear outline bounding box. */
+ outline->xmin = 0;
+ outline->ymin = 0;
+ outline->xmax = 0;
+ outline->ymax = 0;
+
+ /* Set up the context. */
+ build_outline_context.outline = outline;
+ build_outline_context.factor = 0177777;
+
+ /* Start decomposing. */
+ rc = sfnt_decompose_instructed_outline (instructed,
+ sfnt_move_to_and_build,
+ sfnt_line_to_and_build,
+ sfnt_curve_to_and_build,
+ NULL);
+
+ /* Synchronize the outline object with what might have changed
+ inside sfnt_decompose_glyph. */
+ outline = build_outline_context.outline;
+
+ /* Finally, obtain the origin point of the glyph after it has been
+ instructed. */
+
+ if (instructed->num_points > 1)
+ outline->origin
+ = instructed->x_points[instructed->num_points - 2];
+ else
+ outline->origin = 0;
+
+ if (rc)
+ {
+ xfree (outline);
+ return NULL;
+ }
+
+ return outline;
+}
+
+
+
+/* Compute phantom points for the specified glyph GLYPH. Use the
+ unscaled metrics specified in METRICS, and the 16.16 fixed point
+ scale SCALE.
+
+ Place the X and Y coordinates of the first phantom point in *X1 and
+ *Y1, and those of the second phantom point in *X2 and *Y2. */
+
+static void
+sfnt_compute_phantom_points (struct sfnt_glyph *glyph,
+ struct sfnt_glyph_metrics *metrics,
+ sfnt_fixed scale,
+ sfnt_f26dot6 *x1, sfnt_f26dot6 *y1,
+ sfnt_f26dot6 *x2, sfnt_f26dot6 *y2)
+{
+ sfnt_fword f1, f2;
+
+ /* Two ``phantom points'' are appended to each outline by the scaler
+ prior to instruction interpretation. One of these points
+ represents the left-side bearing distance from xmin, while the
+ other represents the advance width. Both are then used after the
+ hinting process to ensure that the reported glyph metrics are
+ consistent with the instructed outline. */
+
+ /* First compute both values in fwords. */
+ f1 = glyph->xmin - metrics->lbearing;
+ f2 = f1 + metrics->advance;
+
+ /* Apply the metrics distortion. */
+ f1 += glyph->origin_distortion;
+ f2 += glyph->advance_distortion;
+
+ /* Next, scale both up. */
+ *x1 = sfnt_mul_f26dot6_fixed (f1 * 64, scale);
+ *x2 = sfnt_mul_f26dot6_fixed (f2 * 64, scale);
+
+ /* Clear y1 and y2. */
+ *y1 = 0;
+ *y2 = 0;
+}
+
+/* Load the simple glyph GLYPH into the specified INTERPRETER, scaling
+ it up by INTERPRETER's scale, and run its glyph program if
+ present. Use the unscaled metrics specified in METRICS.
+
+ Upon success, return NULL and the resulting points and contours in
+ *VALUE. Else, value is the reason interpretation failed. */
+
+TEST_STATIC const char *
+sfnt_interpret_simple_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_glyph_metrics *metrics,
+ struct sfnt_instructed_outline **value)
+{
+ size_t zone_size, temp, outline_size, i;
+ struct sfnt_interpreter_zone *zone;
+ struct sfnt_interpreter_zone *volatile preserved_zone;
+ sfnt_f26dot6 phantom_point_1_x;
+ sfnt_f26dot6 phantom_point_1_y;
+ sfnt_f26dot6 phantom_point_2_x;
+ sfnt_f26dot6 phantom_point_2_y;
+ sfnt_f26dot6 tem;
+ volatile bool zone_was_allocated;
+ struct sfnt_instructed_outline *outline;
+
+ zone_size = 0;
+ zone_was_allocated = false;
+
+ /* Calculate the size of the zone structure. */
+
+ if (INT_MULTIPLY_WRAPV (glyph->simple->number_of_points + 2,
+ sizeof *zone->x_points * 4,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_MULTIPLY_WRAPV (glyph->number_of_contours,
+ sizeof *zone->contour_end_points,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_MULTIPLY_WRAPV (glyph->simple->number_of_points + 2,
+ sizeof *zone->flags,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_ADD_WRAPV (sizeof *zone, zone_size, &zone_size))
+ return "Glyph exceeded maximum permissible size";
+
+ /* Don't use malloc if possible. */
+
+ if (zone_size <= 1024 * 16)
+ zone = alloca (zone_size);
+ else
+ {
+ zone = xmalloc (zone_size);
+ zone_was_allocated = true;
+ }
+
+ /* Now load the zone with data. */
+ zone->num_points = glyph->simple->number_of_points + 2;
+ zone->num_contours = glyph->number_of_contours;
+ zone->contour_end_points = (size_t *) (zone + 1);
+ zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points
+ + zone->num_contours);
+ zone->x_current = zone->x_points + zone->num_points;
+ zone->y_points = zone->x_current + zone->num_points;
+ zone->y_current = zone->y_points + zone->num_points;
+ zone->flags = (unsigned char *) (zone->y_current
+ + zone->num_points);
+
+ /* Load x_points and x_current. */
+ for (i = 0; i < glyph->simple->number_of_points; ++i)
+ {
+ /* Load the fword. */
+ tem = glyph->simple->x_coordinates[i];
+
+ /* Scale that fword. */
+ tem = sfnt_mul_f26dot6_fixed (tem * 64, interpreter->scale);
+
+ /* Set x_points and x_current. */
+ zone->x_points[i] = tem;
+ zone->x_current[i] = tem;
+ }
+
+ /* Compute phantom points. */
+ sfnt_compute_phantom_points (glyph, metrics, interpreter->scale,
+ &phantom_point_1_x, &phantom_point_1_y,
+ &phantom_point_2_x, &phantom_point_2_y);
+
+ /* Load phantom points. */
+ zone->x_points[i] = phantom_point_1_x;
+ zone->x_points[i + 1] = phantom_point_2_x;
+ zone->x_current[i] = phantom_point_1_x;
+ zone->x_current[i + 1] = phantom_point_2_x;
+
+ /* Load y_points and y_current, along with flags. */
+ for (i = 0; i < glyph->simple->number_of_points; ++i)
+ {
+ /* Load the fword. */
+ tem = glyph->simple->y_coordinates[i];
+
+ /* Scale that fword. Make sure not to round Y, as this could
+ lead to Y spilling over to the next line. */
+ tem = sfnt_mul_fixed (tem * 64, interpreter->scale);
+
+ /* Set y_points and y_current. */
+ zone->y_points[i] = tem;
+ zone->y_current[i] = tem;
+
+ /* Set flags. */
+ zone->flags[i] = (glyph->simple->flags[i]
+ & ~SFNT_POINT_TOUCHED_BOTH);
+
+ /* Make sure to clear the phantom points flag. */
+ zone->flags[i] &= ~SFNT_POINT_PHANTOM;
+ }
+
+ /* Load phantom points. */
+ zone->y_points[i] = phantom_point_1_y;
+ zone->y_points[i + 1] = phantom_point_2_y;
+ zone->y_current[i] = phantom_point_1_x;
+ zone->y_current[i + 1] = phantom_point_2_x;
+
+ /* Load phantom point flags. */
+ zone->flags[i] = SFNT_POINT_PHANTOM;
+ zone->flags[i + 1] = SFNT_POINT_PHANTOM;
+
+ /* Load contour end points. */
+ for (i = 0; i < zone->num_contours; ++i)
+ zone->contour_end_points[i]
+ = glyph->simple->end_pts_of_contours[i];
+
+ /* Load the glyph program. */
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = glyph->simple->instructions;
+ interpreter->num_instructions = glyph->simple->instruction_length;
+ interpreter->glyph_zone = zone;
+
+ /* Copy zone over to this volatile variable. */
+ preserved_zone = zone;
+
+ if (setjmp (interpreter->trap))
+ {
+ if (zone_was_allocated)
+ xfree (preserved_zone);
+
+ interpreter->glyph_zone = NULL;
+ return interpreter->trap_reason;
+ }
+
+ sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM);
+ interpreter->glyph_zone = NULL;
+
+ /* Move preserved_zone back to zone. */
+ zone = preserved_zone;
+
+ /* Now that the program has been run, build the scaled outline. */
+
+ outline_size = sizeof (*outline);
+ outline_size += (zone->num_contours
+ * sizeof *outline->contour_end_points);
+ outline_size += (zone->num_points
+ * sizeof *outline->x_points * 2);
+ outline_size += zone->num_points;
+
+ /* Allocate the outline. */
+ outline = xmalloc (outline_size);
+ outline->num_points = zone->num_points;
+ outline->num_contours = zone->num_contours;
+ outline->contour_end_points = (size_t *) (outline + 1);
+ outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points
+ + outline->num_contours);
+ outline->y_points = outline->x_points + outline->num_points;
+ outline->flags = (unsigned char *) (outline->y_points
+ + outline->num_points);
+
+ /* Copy over the contour endpoints, points, and flags. */
+ memcpy (outline->contour_end_points, zone->contour_end_points,
+ zone->num_contours * sizeof *outline->contour_end_points);
+ memcpy (outline->x_points, zone->x_current,
+ zone->num_points * sizeof *outline->x_points);
+ memcpy (outline->y_points, zone->y_current,
+ zone->num_points * sizeof *outline->y_points);
+ memcpy (outline->flags, zone->flags, zone->num_points);
+
+ /* Free the zone if necessary. */
+ if (zone_was_allocated)
+ xfree (zone);
+
+ /* Return the outline and NULL. */
+ *value = outline;
+ return NULL;
+}
+
+/* Apply the transform in the compound glyph component COMPONENT to
+ the array of points of length NUM_COORDINATES given as X and Y.
+
+ Treat X and Y as arrays of 26.6 fixed point values.
+
+ Also, apply the 26.6 fixed point offsets X_OFF and Y_OFF to each X
+ and Y coordinate.
+
+ See sfnt_decompose_compound_glyph for an explanation of why offsets
+ might be applied here, and not while reading the subglyph
+ itself. */
+
+static void
+sfnt_transform_f26dot6 (struct sfnt_compound_glyph_component *component,
+ sfnt_f26dot6 *restrict x, sfnt_f26dot6 *restrict y,
+ size_t num_coordinates,
+ sfnt_f26dot6 x_off, sfnt_f26dot6 y_off)
+{
+ double m1, m2, m3;
+ double m4, m5, m6;
+ size_t i;
+
+ if (component->flags & 010) /* WE_HAVE_A_SCALE */
+ {
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] *= component->u.scale / 16384.0;
+ y[i] *= component->u.scale / 16384.0;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+ else if (component->flags & 0100) /* WE_HAVE_AN_X_AND_Y_SCALE */
+ {
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] *= component->u.a.xscale / 16384.0;
+ y[i] *= component->u.a.yscale / 16384.0;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+ else if (component->flags & 0200) /* WE_HAVE_A_TWO_BY_TWO */
+ {
+ /* Apply the specified affine transformation.
+ A transform looks like:
+
+ M1 M2 M3 X
+ M4 M5 M6 * Y
+
+ =
+
+ M1*X + M2*Y + M3*1 = X1
+ M4*X + M5*Y + M6*1 = Y1
+
+ (In most transforms, there is another row at the bottom for
+ mathematical reasons. Since Z1 is always 1.0, the row is
+ simply implied to be 0 0 1, because 0 * x + 0 * y + 1 * 1 =
+ 1.0. See the definition of matrix3x3 in image.c for some
+ more explanations about this.) */
+ m1 = component->u.b.xscale / 16384.0;
+ m2 = component->u.b.scale01 / 16384.0;
+ m3 = 0;
+ m4 = component->u.b.scale10 / 16384.0;
+ m5 = component->u.b.yscale / 16384.0;
+ m6 = 0;
+
+ for (i = 0; i < num_coordinates; ++i)
+ {
+ x[i] = m1 * x[i] + m2 * y[i] + m3 * 1;
+ y[i] = m4 * x[i] + m5 * y[i] + m6 * 1;
+ x[i] += x_off;
+ y[i] += y_off;
+ }
+ }
+}
+
+/* Internal helper for sfnt_interpret_compound_glyph_3.
+
+ Instruct the compound glyph GLYPH using INTERPRETER after all of
+ its components have been instructed.
+
+ Use the unscaled METRICS to compute the phantom points of this
+ glyph.
+
+ CONTEXT contains the points and contours of this compound glyph,
+ numbered starting from BASE_INDEX and BASE_CONTOUR respectively.
+
+ Value is NULL upon success, or a description of the error upon
+ failure. */
+
+static const char *
+sfnt_interpret_compound_glyph_2 (struct sfnt_glyph *glyph,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_compound_glyph_context *context,
+ size_t base_index, size_t base_contour,
+ struct sfnt_glyph_metrics *metrics)
+{
+ size_t num_points, num_contours, i;
+ size_t zone_size, temp;
+ struct sfnt_interpreter_zone *zone;
+ struct sfnt_interpreter_zone *volatile preserved_zone;
+ sfnt_f26dot6 phantom_point_1_x;
+ sfnt_f26dot6 phantom_point_1_y;
+ sfnt_f26dot6 phantom_point_2_x;
+ sfnt_f26dot6 phantom_point_2_y;
+ volatile bool zone_was_allocated;
+ int rc;
+ sfnt_f26dot6 *x_base, *y_base;
+ size_t *contour_base;
+ unsigned char *flags_base;
+
+ /* Figure out how many points and contours there are to
+ instruct. */
+ num_points = context->num_points - base_index;
+ num_contours = context->num_end_points - base_contour;
+
+ /* Nothing to instruct! */
+ if (!num_points && !num_contours)
+ return NULL;
+
+ /* Build the zone. First, calculate the size of the zone
+ structure. */
+
+ zone_size = 0;
+ zone_was_allocated = false;
+
+ if (INT_MULTIPLY_WRAPV (num_points + 2,
+ sizeof *zone->x_points * 4,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_MULTIPLY_WRAPV (num_contours,
+ sizeof *zone->contour_end_points,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_MULTIPLY_WRAPV (num_points + 2,
+ sizeof *zone->flags,
+ &temp)
+ || INT_ADD_WRAPV (temp, zone_size, &zone_size)
+ || INT_ADD_WRAPV (sizeof *zone, zone_size, &zone_size))
+ return "Glyph exceeded maximum permissible size";
+
+ /* Don't use malloc if possible. */
+
+ if (zone_size <= 1024 * 16)
+ zone = alloca (zone_size);
+ else
+ {
+ zone = xmalloc (zone_size);
+ zone_was_allocated = true;
+ }
+
+ /* Now load the zone with data. */
+ zone->num_points = num_points + 2;
+ zone->num_contours = num_contours;
+ zone->contour_end_points = (size_t *) (zone + 1);
+ zone->x_points = (sfnt_f26dot6 *) (zone->contour_end_points
+ + zone->num_contours);
+ zone->x_current = zone->x_points + zone->num_points;
+ zone->y_points = zone->x_current + zone->num_points;
+ zone->y_current = zone->y_points + zone->num_points;
+ zone->flags = (unsigned char *) (zone->y_current
+ + zone->num_points);
+
+ /* Copy and renumber all contour end points to start from
+ base_index. */
+
+ for (i = 0; i < zone->num_contours; ++i)
+ zone->contour_end_points[i]
+ = (context->contour_end_points[base_contour + i]
+ - base_index);
+
+ /* Now copy over x_points, x_current, y_points and y_current. */
+
+ for (i = 0; i < num_points; ++i)
+ {
+ zone->x_current[i] = context->x_coordinates[i + base_index];
+ zone->x_points[i] = context->x_coordinates[i + base_index];
+ }
+
+ /* Compute phantom points. */
+ sfnt_compute_phantom_points (glyph, metrics, interpreter->scale,
+ &phantom_point_1_x, &phantom_point_1_y,
+ &phantom_point_2_x, &phantom_point_2_y);
+
+ /* Load phantom points. */
+ zone->x_points[i] = phantom_point_1_x;
+ zone->x_points[i + 1] = phantom_point_2_x;
+ zone->x_current[i] = phantom_point_1_x;
+ zone->x_current[i + 1] = phantom_point_2_x;
+
+ for (i = 0; i < num_points; ++i)
+ {
+ zone->y_current[i] = context->y_coordinates[i + base_index];
+ zone->y_points[i] = context->y_coordinates[i + base_index];
+
+ /* Set flags. */
+ zone->flags[i] = (context->flags[i + base_index]
+ & ~SFNT_POINT_TOUCHED_BOTH);
+ }
+
+ /* Load phantom points. */
+ zone->y_points[i] = phantom_point_1_y;
+ zone->y_points[i + 1] = phantom_point_2_y;
+ zone->y_current[i] = phantom_point_1_x;
+ zone->y_current[i + 1] = phantom_point_2_x;
+
+ /* Load phantom point flags. */
+ zone->flags[i] = SFNT_POINT_PHANTOM;
+ zone->flags[i + 1] = SFNT_POINT_PHANTOM;
+
+ /* Load the compound glyph program. */
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = glyph->compound->instructions;
+ interpreter->num_instructions = glyph->compound->instruction_length;
+ interpreter->glyph_zone = zone;
+
+ /* Copy zone over to this volatile variable. */
+ preserved_zone = zone;
+
+ if (setjmp (interpreter->trap))
+ {
+ if (zone_was_allocated)
+ xfree (preserved_zone);
+
+ interpreter->glyph_zone = NULL;
+ return interpreter->trap_reason;
+ }
+
+ sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_GLYPH_PROGRAM);
+ interpreter->glyph_zone = NULL;
+
+ /* Move preserved_zone back to zone. */
+ zone = preserved_zone;
+
+ /* Now copy the instructed points back, and add the two phantom
+ points to the end. */
+
+ for (i = 0; i < num_points; ++i)
+ {
+ context->x_coordinates[base_index + i] = zone->x_current[i];
+ context->y_coordinates[base_index + i] = zone->y_current[i];
+ }
+
+ /* Grow various arrays to fit the phantom points. */
+ rc = sfnt_expand_compound_glyph_context (context, 0, 2,
+ &x_base, &y_base,
+ &flags_base,
+ &contour_base);
+
+ if (rc)
+ {
+ if (zone_was_allocated)
+ xfree (zone);
+
+ return "Failed to expand arrays for phantom points";
+ }
+
+ /* Copy over the phantom points. */
+ x_base[0] = zone->x_current[num_points - 2];
+ x_base[1] = zone->x_current[num_points - 1];
+ y_base[0] = zone->y_current[num_points - 2];
+ y_base[1] = zone->y_current[num_points - 1];
+ flags_base[0] = zone->flags[num_points - 2];
+ flags_base[1] = zone->flags[num_points - 1];
+
+ /* Free the zone if needed. */
+ if (zone_was_allocated)
+ xfree (zone);
+
+ return NULL;
+}
+
+/* Internal helper for sfnt_interpret_compound_glyph.
+ RECURSION_COUNT is the number of times this function has called itself.
+ OFF_X and OFF_Y are the offsets to apply to the glyph outline.
+
+ METRICS are the unscaled metrics of this compound glyph.
+
+ Other arguments mean the same as they do in
+ `sfnt_interpret_compound_glyph'. */
+
+static const char *
+sfnt_interpret_compound_glyph_1 (struct sfnt_glyph *glyph,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_graphics_state *state,
+ struct sfnt_compound_glyph_context *context,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ struct sfnt_hmtx_table *hmtx,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_maxp_table *maxp,
+ struct sfnt_glyph_metrics *metrics,
+ sfnt_fixed off_x, sfnt_fixed off_y,
+ int recursion_count,
+ void *dcontext)
+{
+ struct sfnt_glyph *subglyph;
+ int i, j, rc;
+ const char *error;
+ bool need_free;
+ struct sfnt_compound_glyph_component *component;
+ sfnt_f26dot6 x, y, xtemp, ytemp;
+ size_t point, point2;
+ size_t last_point, number_of_contours;
+ sfnt_f26dot6 *x_base, *y_base;
+ size_t *contour_base;
+ unsigned char *flags_base;
+ size_t base_index, contour_start, base_contour;
+ bool defer_offsets;
+ struct sfnt_instructed_outline *value;
+ struct sfnt_glyph_metrics sub_metrics;
+
+ error = NULL;
+
+ /* Set up the base index. This is the index from where on point
+ renumbering starts.
+
+ In other words, point 0 in this glyph will be 0 + base_index,
+ point 1 will be 1 + base_index, and so on. */
+ base_index = context->num_points;
+
+ /* And this is the index of the first contour in this glyph. */
+ base_contour = context->num_end_points;
+
+ /* Prevent infinite loops. */
+ if (recursion_count > 12)
+ return "Overly deep recursion in compound glyph data";
+
+ /* Don't defer offsets. */
+ defer_offsets = false;
+
+ /* Pacify -Wmaybe-uninitialized. */
+ point = point2 = 0;
+
+ for (j = 0; j < glyph->compound->num_components; ++j)
+ {
+ /* Look up the associated subglyph. */
+ component = &glyph->compound->components[j];
+ subglyph = get_glyph (component->glyph_index,
+ dcontext, &need_free);
+
+ if (!subglyph)
+ return "Failed to obtain component glyph";
+
+ /* Record the size of the point array before expansion. This
+ will be the base to apply to all points coming from this
+ subglyph. */
+ contour_start = context->num_points;
+
+ /* Compute the offset for the component. */
+ if (component->flags & 02) /* ARGS_ARE_XY_VALUES */
+ {
+ /* Component offsets are X/Y values as opposed to points
+ GLYPH. */
+
+ if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+ {
+ /* X and Y are signed bytes. */
+ x = component->argument1.b * 64;
+ y = component->argument2.b * 64;
+ }
+ else
+ {
+ /* X and Y are signed words. */
+ x = component->argument1.d * 64;
+ y = component->argument2.d * 64;
+ }
+
+ /* Now convert X and Y into device coordinates. */
+ x = sfnt_mul_f26dot6_fixed (x, interpreter->scale);
+ y = sfnt_mul_f26dot6_fixed (y, interpreter->scale);
+
+ /* If there is some kind of scale and component offsets are
+ scaled, then apply the transform to the offset. */
+ if (component->flags & 04000) /* SCALED_COMPONENT_OFFSET */
+ sfnt_transform_f26dot6 (component, &x, &y, 1,
+ 0, 0);
+
+ if (component->flags & 04) /* ROUND_XY_TO_GRID */
+ {
+ x = sfnt_round_f26dot6 (x);
+ y = sfnt_round_f26dot6 (y);
+ }
+ }
+ else
+ {
+ /* The offset is determined by matching a point location in
+ a preceeding component with a point location in the
+ current component. The index of the point in the
+ previous component can be determined by adding
+ component->argument1.a or component->argument1.c to
+ point. argument2 contains the index of the point in the
+ current component. */
+
+ if (!(component->flags & 01)) /* ARG_1_AND_2_ARE_WORDS */
+ {
+ point = base_index + component->argument1.a;
+ point2 = component->argument2.a;
+ }
+ else
+ {
+ point = base_index + component->argument1.c;
+ point2 = component->argument2.c;
+ }
+
+ /* Now, check that the anchor point specified lies inside
+ the glyph. */
+
+ if (point >= contour_start)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Invalid anchor point";
+ }
+
+ if (!subglyph->compound)
+ {
+ if (point2 >= subglyph->simple->number_of_points)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Invalid anchored point";
+ }
+
+ /* Get the points and use them to compute the offsets. */
+ xtemp = context->x_coordinates[point];
+ ytemp = context->y_coordinates[point];
+ x = (xtemp - subglyph->simple->x_coordinates[point2] * 64);
+ y = (ytemp - subglyph->simple->y_coordinates[point2] * 64);
+ }
+ else
+ {
+ /* First, set offsets to 0, because it is not yet
+ possible to determine the position of the anchor
+ point in the child. */
+ x = 0;
+ y = 0;
+
+ /* Set a flag which indicates that offsets must be
+ resolved from the child glyph after it is loaded, but
+ before it is incorporated into the parent glyph. */
+ defer_offsets = true;
+ }
+ }
+
+ /* Obtain the glyph metrics. If doing so fails, then cancel
+ decomposition. */
+
+ if (sfnt_lookup_glyph_metrics (component->glyph_index,
+ -1, &sub_metrics,
+ hmtx, hhea, NULL, maxp))
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Failed to obtain component metrics";
+ }
+
+ if (subglyph->simple)
+ {
+ /* Simple subglyph. Copy over the points and contours,
+ then transform and instruct them.
+
+ Skip this step for glyphs without contours. */
+
+ if (subglyph->number_of_contours)
+ {
+ /* Now instruct the simple glyph, and copy it over,
+ including the two phantom points at the end. */
+ interpreter->state = *state;
+ error = sfnt_interpret_simple_glyph (subglyph, interpreter,
+ &sub_metrics, &value);
+
+ /* Cancel instructing if an error occurs. */
+
+ if (error)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return error;
+ }
+
+ /* Figure out how many more points and contours are
+ needed. Here, last_point is not the end of the
+ glyph's contours, as two phantom points are
+ included. */
+ last_point = value->num_points;
+ number_of_contours = value->num_contours;
+
+ /* Grow various arrays. */
+ rc = sfnt_expand_compound_glyph_context (context,
+ /* Number of
+ new contours
+ required. */
+ number_of_contours,
+ /* Number of new
+ points
+ required. */
+ last_point,
+ &x_base,
+ &y_base,
+ &flags_base,
+ &contour_base);
+ if (rc)
+ {
+ xfree (value);
+
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Failed to grow arrays";
+ }
+
+ /* Copy the values in VALUE into the context and free
+ VALUE, including phantom points. */
+
+ for (i = 0; i < last_point; ++i)
+ {
+ x_base[i] = value->x_points[i] + off_x + x;
+ y_base[i] = value->y_points[i] + off_y + y;
+ flags_base[i] = value->flags[i];
+ }
+
+ /* Copy over the contours. */
+ for (i = 0; i < number_of_contours; ++i)
+ contour_base[i] = (contour_start
+ + value->contour_end_points[i]);
+
+ xfree (value);
+
+ /* Apply the transform to the points. */
+ sfnt_transform_f26dot6 (component, x_base, y_base,
+ last_point, 0, 0);
+ }
+ }
+ else
+ {
+ /* Compound subglyph. Decompose and instruct the glyph
+ recursively, and then apply the transform. */
+
+ error = sfnt_interpret_compound_glyph_1 (subglyph, interpreter,
+ state,
+ context, get_glyph,
+ free_glyph, hmtx, hhea,
+ maxp, &sub_metrics,
+ off_x + x, off_y + y,
+ recursion_count + 1,
+ dcontext);
+
+ if (error)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return error;
+ }
+
+ /* When an anchor point is being used to translate the
+ glyph, and the subglyph in question is actually a
+ compound glyph, it is impossible to know which offset to
+ use until the compound subglyph has actually been
+ loaded.
+
+ As a result, the offset is calculated here, using the
+ points in the loaded child compound glyph. But first, X
+ and Y must be reset to 0, as otherwise the translation
+ might be applied twice if defer_offsets is not set. */
+
+ x = 0;
+ y = 0;
+
+ if (defer_offsets)
+ {
+ /* Renumber the non renumbered point2 to point into the
+ decomposed component. */
+ point2 += contour_start;
+
+ /* Next, check that the non-renumbered point being
+ anchored lies inside the glyph data that was
+ decomposed. */
+
+ if (point2 >= context->num_points)
+ {
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+
+ return "Invalid point2";
+ }
+
+ /* Get the points and use them to compute the
+ offsets. */
+
+ xtemp = context->x_coordinates[point];
+ ytemp = context->y_coordinates[point];
+ x = (xtemp - context->x_coordinates[point2]);
+ y = (ytemp - context->y_coordinates[point2]);
+ }
+
+ sfnt_transform_f26dot6 (component,
+ context->x_coordinates + contour_start,
+ context->y_coordinates + contour_start,
+ contour_start - context->num_points,
+ x, y);
+ }
+
+ /* Finally, free the subglyph. */
+ if (need_free)
+ free_glyph (subglyph, dcontext);
+ }
+
+ /* Run the program for the entire compound glyph, if any. */
+
+ if (glyph->compound->instruction_length)
+ {
+ interpreter->state = *state;
+ error = sfnt_interpret_compound_glyph_2 (glyph, interpreter,
+ context, base_index,
+ base_contour,
+ metrics);
+ }
+
+ return error;
+}
+
+/* Interpret the compound glyph GLYPH using the specified INTERPRETER.
+ Load each component of the compound glyph GLYPH. CONTEXT should be
+ a reference to a `struct sfnt_compound_glyph_context' that is on
+ the stack.
+
+ Use glyph metrics specified in the HMTX, HHEA and MAXP tables, and
+ the unscaled metrics for GLYPH specified in METRICS.
+
+ If the component is a simple glyph, scale and instruct it
+ immediately.
+
+ If the component is a compound glyph, then load it recursively
+ until a predetermined recursion level is exceeded.
+
+ Set INTERPRETER's state to STATE prior to instructing each
+ component.
+
+ Load component glyphs using GET_GLYPH, which should return whether
+ or not FREE_GLYPH should be called with the glyph that was loaded.
+ Call both functions with DCONTEXT as an argument.
+
+ Finally, append the resulting contours, run any compound glyph
+ program, and return the instructed outline in *VALUE.
+
+ Value is NULL upon success, and the type of error upon failure. */
+
+TEST_STATIC const char *
+sfnt_interpret_compound_glyph (struct sfnt_glyph *glyph,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_graphics_state *state,
+ sfnt_get_glyph_proc get_glyph,
+ sfnt_free_glyph_proc free_glyph,
+ struct sfnt_hmtx_table *hmtx,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_maxp_table *maxp,
+ struct sfnt_glyph_metrics *metrics,
+ void *dcontext,
+ struct sfnt_instructed_outline **value)
+{
+ struct sfnt_compound_glyph_context context;
+ const char *error;
+ struct sfnt_instructed_outline *outline;
+ size_t outline_size, temp;
+
+ /* Set up the glyph decomposition context. */
+ memset (&context, 0, sizeof context);
+
+ /* Now start decomposing the glyph. */
+ error = sfnt_interpret_compound_glyph_1 (glyph, interpreter,
+ state, &context,
+ get_glyph, free_glyph,
+ hmtx, hhea, maxp,
+ metrics, 0, 0, 0,
+ dcontext);
+
+ /* If an error occurs, free the data in the context and return. */
+
+ if (error)
+ {
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+ return error;
+ }
+
+ /* Copy the compound glyph data into an instructed outline. */
+ outline_size = sizeof (*outline);
+
+ if (INT_MULTIPLY_WRAPV (context.num_end_points,
+ sizeof *outline->contour_end_points,
+ &temp)
+ || INT_ADD_WRAPV (outline_size, temp, &outline_size)
+ || INT_MULTIPLY_WRAPV (context.num_points,
+ sizeof *outline->x_points * 2,
+ &temp)
+ || INT_ADD_WRAPV (outline_size, temp, &outline_size)
+ || INT_ADD_WRAPV (context.num_points, outline_size,
+ &outline_size))
+ {
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+ return "Glyph exceeds maximum permissible size";
+ }
+
+ /* Allocate the outline. */
+ outline = xmalloc (outline_size);
+ outline->num_points = context.num_points;
+ outline->num_contours = context.num_end_points;
+ outline->contour_end_points = (size_t *) (outline + 1);
+ outline->x_points = (sfnt_f26dot6 *) (outline->contour_end_points
+ + outline->num_contours);
+ outline->y_points = outline->x_points + outline->num_points;
+ outline->flags = (unsigned char *) (outline->y_points
+ + outline->num_points);
+
+ /* Copy over the contour endpoints, points, and flags. */
+ memcpy (outline->contour_end_points, context.contour_end_points,
+ outline->num_contours * sizeof *outline->contour_end_points);
+ memcpy (outline->x_points, context.x_coordinates,
+ outline->num_points * sizeof *outline->x_points);
+ memcpy (outline->y_points, context.y_coordinates,
+ outline->num_points * sizeof *outline->y_points);
+ memcpy (outline->flags, context.flags, context.num_points);
+
+ /* Free the context data. */
+ xfree (context.x_coordinates);
+ xfree (context.y_coordinates);
+ xfree (context.flags);
+ xfree (context.contour_end_points);
+
+ *value = outline;
+ return NULL;
+}
+
+
+
+
+/* Unicode Variation Sequence (UVS) support.
+
+ Unicode defines a mechanism by which a two-codepoint sequence
+ consisting of a ``base character'' and ``variation selector'' is
+ able to produce a glyph that is a variant of the glyph that would
+ conventionally have been mapped to the ``base character''.
+
+ TrueType describes variation selector sequences through a type of
+ character mapping table that is given the format 14. The character
+ mapping table consists of an array of variation selectors, each of
+ which have a corresponding ``default UVS table'', which describes
+ ranges of ``base characters'' having no special variant glyphs, and
+ a ``non-default UVS table'', which is a map of ``base characters''
+ to their corresponding variant glyphs. */
+
+/* Read a default UVS table from the font file FD, at the specified
+ OFFSET. Value is the default UVS table upon success, else
+ NULL. */
+
+static struct sfnt_default_uvs_table *
+sfnt_read_default_uvs_table (int fd, off_t offset)
+{
+ struct sfnt_default_uvs_table *uvs;
+ uint32_t num_ranges, i, j;
+ size_t size, temp;
+ char data[512];
+
+ /* First, seek to the given offset. */
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ return NULL;
+
+ /* Next, read the number of ranges present. */
+
+ if (read (fd, &num_ranges, sizeof num_ranges) != sizeof num_ranges)
+ return NULL;
+
+ /* Swap the number of ranges present. */
+ sfnt_swap32 (&num_ranges);
+
+ /* Now, allocate enough to hold the UVS table. */
+
+ size = sizeof *uvs;
+ if (INT_MULTIPLY_WRAPV (sizeof *uvs->ranges, num_ranges,
+ &temp)
+ || INT_ADD_WRAPV (temp, size, &size))
+ return NULL;
+
+ uvs = xmalloc (size);
+
+ /* Fill in the data which was already read. */
+ uvs->num_unicode_value_ranges = num_ranges;
+
+ /* Fill in the pointer to the ranges. */
+ uvs->ranges = (struct sfnt_unicode_value_range *) (uvs + 1);
+ i = 0;
+
+ /* Read each default UVS range in multiples of 512 bytes. Then,
+ fill in uvs->ranges. */
+
+ while (num_ranges)
+ {
+ size = (num_ranges > 128 ? 512 : num_ranges * 4);
+
+ if (read (fd, data, size) != size)
+ {
+ xfree (uvs);
+ return NULL;
+ }
+
+ for (j = 0; j < size / 4; ++j)
+ {
+ uvs->ranges[i + j].start_unicode_value
+ = sfnt_read_24 ((unsigned char *) data + j * 4);
+ uvs->ranges[i + j].additional_count = data[j * 4 + 1];
+ }
+
+ i += j;
+ num_ranges -= size / 4;
+ }
+
+ /* Return the resulting default UVS table. */
+ return uvs;
+}
+
+/* Read a non-default UVS table from the font file FD, at the
+ specified OFFSET. Value is the non-default UVS table upon success,
+ else NULL. */
+
+static struct sfnt_nondefault_uvs_table *
+sfnt_read_nondefault_uvs_table (int fd, off_t offset)
+{
+ struct sfnt_nondefault_uvs_table *uvs;
+ uint32_t num_mappings, i, j;
+ size_t size, temp;
+ char data[500];
+
+ /* First, seek to the given offset. */
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ return NULL;
+
+ /* Next, read the number of mappings present. */
+
+ if (read (fd, &num_mappings, sizeof num_mappings)
+ != sizeof num_mappings)
+ return NULL;
+
+ /* Swap the number of mappings present. */
+ sfnt_swap32 (&num_mappings);
+
+ /* Now, allocate enough to hold the UVS table. */
+
+ size = sizeof *uvs;
+ if (INT_MULTIPLY_WRAPV (sizeof *uvs->mappings, num_mappings,
+ &temp)
+ || INT_ADD_WRAPV (temp, size, &size))
+ return NULL;
+
+ uvs = xmalloc (size);
+
+ /* Fill in the data which was already read. */
+ uvs->num_uvs_mappings = num_mappings;
+
+ /* Fill in the pointer to the mappings. */
+ uvs->mappings = (struct sfnt_uvs_mapping *) (uvs + 1);
+
+ i = 0;
+
+ /* Read each nondefault UVS mapping in multiples of 500 bytes.
+ Then, fill in uvs->ranges. */
+
+ while (num_mappings)
+ {
+ size = (num_mappings > 100 ? 500 : num_mappings * 5);
+
+ if (read (fd, data, size) != size)
+ {
+ xfree (uvs);
+ return NULL;
+ }
+
+ for (j = 0; j < size / 5; ++j)
+ {
+ uvs->mappings[i + j].unicode_value
+ = sfnt_read_24 ((unsigned char *) data + j * 5);
+ memcpy (&uvs->mappings[i + j].base_character_value,
+ data + j * 5 + 3,
+ sizeof uvs->mappings[i + j].base_character_value);
+ sfnt_swap16 (&uvs->mappings[i + j].base_character_value);
+ }
+
+ i += j;
+ num_mappings -= size / 5;
+ }
+
+ /* Return the nondefault UVS table. */
+ return uvs;
+}
+
+/* Perform comparison of A and B, two table offsets. */
+
+static int
+sfnt_compare_table_offsets (const void *a, const void *b)
+{
+ const struct sfnt_table_offset_rec *rec_a, *rec_b;
+
+ rec_a = a;
+ rec_b = b;
+
+ if (rec_a->offset < rec_b->offset)
+ return -1;
+ else if (rec_a->offset > rec_b->offset)
+ return 1;
+
+ return 0;
+}
+
+/* Create a variation selection context based on the format 14 cmap
+ subtable CMAP.
+
+ FD is the font file to which the table belongs.
+
+ Value is the variation selection context upon success, else NULL.
+ The context contains each variation selector record and their
+ associated default and nondefault UVS tables. Free the context
+ with `sfnt_free_uvs_context'. */
+
+TEST_STATIC struct sfnt_uvs_context *
+sfnt_create_uvs_context (struct sfnt_cmap_format_14 *cmap, int fd)
+{
+ struct sfnt_table_offset_rec *table_offsets, *rec, template;
+ size_t size, i, nmemb, j;
+ off_t offset;
+ struct sfnt_uvs_context *context;
+
+ if (INT_MULTIPLY_WRAPV (cmap->num_var_selector_records,
+ sizeof *table_offsets, &size)
+ || INT_MULTIPLY_WRAPV (size, 2, &size))
+ return NULL;
+
+ context = NULL;
+
+ /* First, record and sort the UVS and nondefault UVS table offsets
+ in ascending order. */
+
+ table_offsets = xmalloc (size);
+ memset (table_offsets, 0, size);
+ nmemb = cmap->num_var_selector_records * 2;
+ j = 0;
+
+ for (i = 0; i < cmap->num_var_selector_records; ++i)
+ {
+ /* Note that either offset may be 0, meaning there is no such
+ table. */
+
+ if (cmap->records[i].default_uvs_offset)
+ {
+ if (INT_ADD_WRAPV (cmap->offset,
+ cmap->records[i].default_uvs_offset,
+ &table_offsets[j].offset))
+ goto bail;
+
+ table_offsets[j++].is_nondefault_table = false;
+ }
+
+ if (cmap->records[i].nondefault_uvs_offset)
+ {
+ if (INT_ADD_WRAPV (cmap->offset,
+ cmap->records[i].nondefault_uvs_offset,
+ &table_offsets[j].offset))
+ goto bail;
+
+ table_offsets[j++].is_nondefault_table = true;
+ }
+ }
+
+ /* Make nmemb the number of offsets actually looked up. */
+ nmemb = j;
+
+ qsort (table_offsets, nmemb, sizeof *table_offsets,
+ sfnt_compare_table_offsets);
+
+ /* Now go through table_offsets, and read everything. nmemb is the
+ number of elements in table_offsets[i]; it is kept up to date
+ when duplicate members are removed. */
+ offset = -1;
+
+ for (i = 0; i < nmemb; ++i)
+ {
+ /* Skip past duplicate tables. */
+
+ while (table_offsets[i].offset == offset && i < nmemb)
+ {
+ nmemb--;
+ table_offsets[i] = table_offsets[i + 1];
+ }
+
+ /* If the last element of the array is a duplicate, break out of
+ the loop. */
+
+ if (i == nmemb)
+ break;
+
+ /* Read the correct type of table depending on
+ table_offsets[i].is_nondefault_table. Then skip past
+ duplicate tables. Don't handle the case where two different
+ kind of tables share the same offset, because that is not
+ possible in a valid variation selector record. */
+
+ offset = table_offsets[i].offset;
+
+ if (table_offsets[i].is_nondefault_table)
+ table_offsets[i].table
+ = sfnt_read_nondefault_uvs_table (fd, offset);
+ else
+ table_offsets[i].table
+ = sfnt_read_default_uvs_table (fd, offset);
+ }
+
+ /* Now make the context. */
+ context = xmalloc (sizeof *context);
+ context->num_records = cmap->num_var_selector_records;
+ context->nmemb = nmemb;
+ context->records = xmalloc (sizeof *context->records
+ * cmap->num_var_selector_records);
+
+ for (i = 0; i < cmap->num_var_selector_records; ++i)
+ {
+ context->records[i].selector = cmap->records[i].var_selector;
+
+ /* Either offset may be 0, meaning no such table exists. Also,
+ the code below will lose if more than one kind of table
+ shares the same offset, because that is impossible. */
+
+ if (cmap->records[i].default_uvs_offset)
+ {
+ /* Resolve the default table. */
+ template.offset = (cmap->records[i].default_uvs_offset
+ + cmap->offset);
+ rec = bsearch (&template, table_offsets,
+ nmemb, sizeof *table_offsets,
+ sfnt_compare_table_offsets);
+
+ /* Make sure this record is the right type. */
+ if (!rec || rec->is_nondefault_table || !rec->table)
+ goto bail;
+
+ context->records[i].default_uvs = rec->table;
+ }
+ else
+ context->records[i].default_uvs = NULL;
+
+ if (cmap->records[i].nondefault_uvs_offset)
+ {
+ /* Resolve the nondefault table. */
+ template.offset = (cmap->records[i].nondefault_uvs_offset
+ + cmap->offset);
+ rec = bsearch (&template, table_offsets,
+ nmemb, sizeof *table_offsets,
+ sfnt_compare_table_offsets);
+
+ if (!rec)
+ goto bail;
+
+ /* Make sure this record is the right type. */
+ if (!rec || !rec->is_nondefault_table || !rec->table)
+ goto bail;
+
+ context->records[i].nondefault_uvs = rec->table;
+ }
+ else
+ context->records[i].nondefault_uvs = NULL;
+ }
+
+ context->tables = table_offsets;
+ return context;
+
+ bail:
+
+ if (context)
+ {
+ xfree (context->records);
+ xfree (context);
+ }
+
+ /* Loop through and free any tables that might have been read
+ already. */
+
+ for (i = 0; i < nmemb; ++i)
+ xfree (table_offsets[i].table);
+
+ xfree (table_offsets);
+ return NULL;
+}
+
+/* Free the specified variation selection context C. */
+
+TEST_STATIC void
+sfnt_free_uvs_context (struct sfnt_uvs_context *c)
+{
+ size_t i;
+
+ xfree (c->records);
+
+ for (i = 0; i < c->nmemb; ++i)
+ xfree (c->tables[i].table);
+
+ xfree (c->tables);
+ xfree (c);
+}
+
+/* Compare *(sfnt_char *) K to ((struct sfnt_uvs_mapping *)
+ V)->unicode_value appropriately for bsearch. */
+
+static int
+sfnt_compare_uvs_mapping (const void *k, const void *v)
+{
+ const sfnt_char *key;
+ const struct sfnt_uvs_mapping *value;
+
+ key = k;
+ value = v;
+
+ if (*key < value->unicode_value)
+ return -1;
+ else if (*key == value->unicode_value)
+ return 0;
+
+ return 1;
+}
+
+/* Return the ID of a variation glyph for the character C in the
+ nondefault UVS mapping table UVS.
+
+ Value is the glyph ID upon success, or 0 if there is no variation
+ glyph for the base character C. */
+
+TEST_STATIC sfnt_glyph
+sfnt_variation_glyph_for_char (struct sfnt_nondefault_uvs_table *uvs,
+ sfnt_char c)
+{
+ struct sfnt_uvs_mapping *mapping;
+
+ mapping = bsearch (&c, uvs->mappings, uvs->num_uvs_mappings,
+ sizeof *uvs->mappings,
+ sfnt_compare_uvs_mapping);
+
+ return mapping ? mapping->base_character_value : 0;
+}
+
+
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Memory mapping support.
+ It useful to map OpenType layout tables prior to using them in
+ an external shaping engine such as HarfBuzz. */
+
+/* Map a table identified by TAG into the structure *TABLE.
+ TAG is swapped into host byte order.
+
+ Use the table directory SUBTABLE, which corresponds to the font
+ file FD.
+
+ Return 0 upon success, and set TABLE->data to the table data,
+ TABLE->mapping to the start of the mapped area, TABLE->length to
+ the length of the table contents, and TABLE->size to the size of
+ the mapping.
+
+ Return 1 upon failure. */
+
+int
+sfnt_map_table (int fd, struct sfnt_offset_subtable *subtable,
+ uint32_t tag, struct sfnt_mapped_table *table)
+{
+ struct sfnt_table_directory *directory;
+ size_t offset, page, map_offset;
+ void *data;
+ int i;
+
+ /* Find the table in the directory. */
+
+ for (i = 0; i < subtable->num_tables; ++i)
+ {
+ if (subtable->subtables[i].tag == tag)
+ {
+ directory = &subtable->subtables[i];
+ break;
+ }
+ }
+
+ if (i == subtable->num_tables)
+ return 1;
+
+ /* Now try to map the glyph data. Make sure offset is a multiple of
+ the page size. */
+
+ page = getpagesize ();
+ offset = directory->offset & ~(page - 1);
+
+ /* Figure out how much larger the mapping should be. */
+ map_offset = directory->offset - offset;
+
+ /* Do the mmap. */
+ data = mmap (NULL, directory->length + map_offset,
+ PROT_READ, MAP_PRIVATE, fd, offset);
+
+ if (data == MAP_FAILED)
+ return 1;
+
+ /* Fill in *TABLE. */
+ table->data = (unsigned char *) data + map_offset;
+ table->mapping = data;
+ table->length = directory->length;
+ table->size = directory->length + map_offset;
+ return 0;
+}
+
+/* Unmap the table inside *TABLE.
+ Value is 0 upon success, 1 otherwise. */
+
+int
+sfnt_unmap_table (struct sfnt_mapped_table *table)
+{
+ return munmap (table->mapping, table->size) != 0;
+}
+
+#endif /* HAVE_MMAP && !TEST */
+
+
+
+#ifndef TEST
+
+/* Reading table contents. */
+
+/* Read the table with the specified TAG from the font file FD.
+ Return its length in *LENGTH, and its data upon success, else
+ NULL. */
+
+void *
+sfnt_read_table (int fd, struct sfnt_offset_subtable *subtable,
+ uint32_t tag, size_t *length)
+{
+ struct sfnt_table_directory *directory;
+ void *data;
+ int i;
+
+ /* Find the table in the directory. */
+
+ for (i = 0; i < subtable->num_tables; ++i)
+ {
+ if (subtable->subtables[i].tag == tag)
+ {
+ directory = &subtable->subtables[i];
+ break;
+ }
+ }
+
+ if (i == subtable->num_tables)
+ return NULL;
+
+ /* Seek to the table. */
+
+ if (lseek (fd, directory->offset, SEEK_SET) != directory->offset)
+ return NULL;
+
+ /* Now allocate enough to hold the data and read into it. */
+
+ data = xmalloc (directory->length);
+ if (read (fd, data, directory->length) != directory->length)
+ {
+ xfree (data);
+ return NULL;
+ }
+
+ /* Return the length and table data. */
+ *length = directory->length;
+ return data;
+}
+
+#endif /* !TEST */
+
+
+
+/* Glyph variations. Instead of defining separate fonts for each
+ combination of weight, width and slant (bold, condensed, italic,
+ etc), some fonts specify a list of ``variation axes'', each of
+ which determines one delta to apply to each point in every
+ glyph.
+
+ This optional information is specified in the `fvar' (font
+ variation), `gvar' (glyph variation) and `cvar' (CVT variation)
+ tables in a font file. */
+
+/* Read an fvar table from the given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the fvar table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_fvar_table *
+sfnt_read_fvar_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_fvar_table *fvar;
+ ssize_t rc;
+ size_t min_bytes, ps_size, non_ps_size, temp, pad;
+ off_t offset;
+ int i, j;
+ char *buffer;
+ sfnt_fixed *coords;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_FVAR);
+
+ if (!directory)
+ return NULL;
+
+ min_bytes = SFNT_ENDOF (struct sfnt_fvar_table,
+ instance_size, uint16_t);
+
+ /* Check that the length is at least min_bytes. */
+ if (directory->length < min_bytes)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold the fvar table header. */
+ fvar = xmalloc (sizeof *fvar);
+
+ /* Read the fvar table header. */
+ buffer = NULL;
+ rc = read (fd, fvar, min_bytes);
+ if (rc != min_bytes)
+ goto bail;
+
+ /* Swap what was read. */
+ sfnt_swap16 (&fvar->major_version);
+ sfnt_swap16 (&fvar->minor_version);
+ sfnt_swap16 (&fvar->offset_to_data);
+ sfnt_swap16 (&fvar->count_size_pairs);
+ sfnt_swap16 (&fvar->axis_count);
+ sfnt_swap16 (&fvar->axis_size);
+ sfnt_swap16 (&fvar->instance_count);
+ sfnt_swap16 (&fvar->instance_size);
+
+ /* major_version should be 1, and minor_version 0. */
+
+ if (fvar->major_version != 1 || fvar->minor_version)
+ goto bail;
+
+ /* count_size_pairs should be more than 2. */
+
+ if (fvar->count_size_pairs < 2)
+ goto bail;
+
+ /* Don't try to read tables where the axis format differs. */
+
+ if (fvar->axis_size != 20)
+ goto bail;
+
+ /* The instance size must either be 2 * sizeof (uint16_t) +
+ axisCount * sizeof (sfnt_fixed), meaning there is no PostScript
+ name identifier, or 3 * sizeof (uint16_t) + axisCount * sizeof
+ (sfnt_fixed), meaning there is. */
+
+ if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed),
+ &temp)
+ || INT_ADD_WRAPV (2 * sizeof (uint16_t), temp, &non_ps_size))
+ goto bail;
+
+ if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof (sfnt_fixed),
+ &temp)
+ || INT_ADD_WRAPV (3 * sizeof (uint16_t), temp, &ps_size))
+ goto bail;
+
+ if (fvar->instance_size != non_ps_size
+ && fvar->instance_size != ps_size)
+ goto bail;
+
+ /* Now compute the offset of the axis data from the start of the
+ font file. */
+
+ if (INT_ADD_WRAPV (fvar->offset_to_data, directory->offset,
+ &offset))
+ goto bail;
+
+ /* Seek there. */
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ goto bail;
+
+ min_bytes = sizeof *fvar;
+
+ /* Now, read each axis and instance. Calculate how much extra data
+ needs to be allocated for the axes and instances: this is
+ fvar->axis_count * sizeof (struct sfnt_variation_axis), some
+ padding, and finally fvar->instance_count * sizeof (struct
+ sfnt_instance) + sizeof (sfnt_fixed) * fvar->instance_count *
+ fvar->axis_count. */
+
+ if (INT_MULTIPLY_WRAPV (fvar->axis_count, sizeof *fvar->axis,
+ &temp)
+ || INT_ADD_WRAPV (min_bytes, temp, &min_bytes))
+ goto bail;
+
+ pad = alignof (struct sfnt_variation_axis);
+ pad -= min_bytes & (pad - 1);
+
+ if (INT_ADD_WRAPV (min_bytes, pad, &min_bytes))
+ goto bail;
+
+ if (INT_MULTIPLY_WRAPV (fvar->instance_count,
+ sizeof *fvar->instance,
+ &temp)
+ || INT_ADD_WRAPV (min_bytes, temp, &min_bytes))
+ goto bail;
+
+ if (INT_MULTIPLY_WRAPV (fvar->instance_count,
+ sizeof *fvar->instance->coords,
+ &temp)
+ || INT_MULTIPLY_WRAPV (temp, fvar->axis_count, &temp)
+ || INT_ADD_WRAPV (min_bytes, temp, &min_bytes))
+ goto bail;
+
+ /* Reallocate fvar. */
+ fvar = xrealloc (fvar, min_bytes);
+
+ /* Fill in offsets. */
+ fvar->axis = (struct sfnt_variation_axis *) (fvar + 1);
+ fvar->instance
+ = (struct sfnt_instance *) (((char *) (fvar->axis
+ + fvar->axis_count))
+ + pad);
+
+ /* Read axes. */
+
+ if (directory->length - SFNT_ENDOF (struct sfnt_fvar_table,
+ instance_size, uint16_t)
+ < sizeof *fvar->axis * fvar->axis_count)
+ goto bail;
+
+ rc = read (fd, fvar->axis, sizeof *fvar->axis * fvar->axis_count);
+ if (rc != sizeof *fvar->axis * fvar->axis_count)
+ goto bail;
+
+ /* Swap each axis. */
+
+ for (i = 0; i < fvar->axis_count; ++i)
+ {
+ sfnt_swap32 (&fvar->axis[i].axis_tag);
+ sfnt_swap32 (&fvar->axis[i].min_value);
+ sfnt_swap32 (&fvar->axis[i].default_value);
+ sfnt_swap32 (&fvar->axis[i].max_value);
+ sfnt_swap16 (&fvar->axis[i].flags);
+ sfnt_swap16 (&fvar->axis[i].name_id);
+ }
+
+ /* Read each instance. */
+
+ if (fvar->instance_size < 1024 * 16)
+ buffer = alloca (fvar->instance_size);
+ else
+ buffer = xmalloc (fvar->instance_size);
+
+ coords = (sfnt_fixed *) (fvar->instance + fvar->instance_count);
+
+ for (i = 0; i < fvar->instance_count; ++i)
+ {
+ rc = read (fd, buffer, fvar->instance_size);
+ if (rc != fvar->instance_size)
+ goto bail;
+
+ /* Fill in various fields. */
+
+ fvar->instance[i].name_id = *((uint16_t *) buffer);
+ fvar->instance[i].flags = *((uint16_t *) buffer + 1);
+ fvar->instance[i].ps_name_id = 0;
+
+ sfnt_swap16 (&fvar->instance[i].name_id);
+ sfnt_swap16 (&fvar->instance[i].flags);
+
+ /* Read coordinates. */
+
+ fvar->instance[i].coords = coords;
+ coords += fvar->axis_count;
+
+ memcpy (fvar->instance[i].coords, buffer + 4,
+ sizeof *fvar->instance[i].coords * fvar->axis_count);
+
+ /* Swap coordinates. */
+
+ for (j = 0; j < fvar->axis_count; ++j)
+ sfnt_swap32 (&fvar->instance[i].coords[j]);
+
+ /* Read the PostScript name ID if necessary. If not, set it to
+ nil. */
+
+ if (fvar->instance_size == ps_size)
+ {
+ fvar->instance[i].ps_name_id
+ = *(uint16_t *) (buffer + 4 + (sizeof *fvar->instance[i].coords
+ * fvar->axis_count));
+ sfnt_swap16 (&fvar->instance[i].ps_name_id);
+ }
+ }
+
+ /* Free the temporary buffer. */
+ if (buffer && fvar->instance_size >= 1024 * 16)
+ xfree (buffer);
+
+ /* Return the fvar table. */
+ return fvar;
+
+ bail:
+ if (buffer && fvar->instance_size >= 1024 * 16)
+ xfree (buffer);
+
+ xfree (fvar);
+ return NULL;
+}
+
+
+
+/* Read a gvar table from the given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the gvar table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_gvar_table *
+sfnt_read_gvar_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_gvar_table *gvar;
+ ssize_t rc;
+ size_t min_bytes, off_size, coordinate_size, data_size;
+ int i;
+ off_t offset;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_GVAR);
+
+ if (!directory)
+ return NULL;
+
+ min_bytes = SFNT_ENDOF (struct sfnt_gvar_table,
+ offset_to_data, uint32_t);
+
+ /* Check that the length is at least min_bytes. */
+ if (directory->length < min_bytes)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold the gvar table header. */
+ gvar = xmalloc (sizeof *gvar);
+
+ /* Read the gvar table header. */
+ rc = read (fd, gvar, min_bytes);
+ if (rc != min_bytes)
+ goto bail;
+
+ /* Swap what was read. */
+ sfnt_swap16 (&gvar->version);
+ sfnt_swap16 (&gvar->reserved);
+ sfnt_swap16 (&gvar->axis_count);
+ sfnt_swap16 (&gvar->shared_coord_count);
+ sfnt_swap32 (&gvar->offset_to_coord);
+ sfnt_swap16 (&gvar->glyph_count);
+ sfnt_swap16 (&gvar->flags);
+ sfnt_swap32 (&gvar->offset_to_data);
+
+ if (gvar->version != 1)
+ goto bail;
+
+ if (gvar->offset_to_data > directory->length)
+ goto bail;
+
+ /* Figure out the size required for the offset array. Note that
+ there is one extra offset at the end of the array to mark the
+ size of the last glyph. */
+
+ if (gvar->flags & 1)
+ /* Offsets are long words. */
+ off_size = sizeof (uint32_t) * (gvar->glyph_count + 1);
+ else
+ /* Offsets are words. */
+ off_size = sizeof (uint16_t) * (gvar->glyph_count + 1);
+
+ /* Now figure out the size of the shared coordinates. */
+ coordinate_size = (gvar->shared_coord_count * gvar->axis_count
+ * sizeof (uint16_t));
+
+ /* And the size of the glyph variation data. */
+ data_size = directory->length - gvar->offset_to_data;
+
+ /* Wraparound. */
+ if (data_size > directory->length)
+ goto bail;
+
+ /* Figure out how big gvar needs to be. */
+ if (INT_ADD_WRAPV (sizeof *gvar, coordinate_size, &min_bytes)
+ || INT_ADD_WRAPV (min_bytes, off_size, &min_bytes)
+ || INT_ADD_WRAPV (min_bytes, data_size, &min_bytes))
+ goto bail;
+
+ /* Now allocate enough for all of this extra data. */
+ gvar = xrealloc (gvar, min_bytes);
+
+ /* Start reading offsets. */
+
+ if (!(gvar->flags & 1))
+ {
+ gvar->u.offset_word = (uint16_t *) (gvar + 1);
+ rc = read (fd, gvar->u.offset_word, off_size);
+ if (rc != off_size)
+ goto bail;
+
+ for (i = 0; i <= gvar->glyph_count; ++i)
+ sfnt_swap16 (&gvar->u.offset_word[i]);
+ }
+ else
+ {
+ gvar->u.offset_long = (uint32_t *) (gvar + 1);
+ rc = read (fd, gvar->u.offset_long, off_size);
+ if (rc != off_size)
+ goto bail;
+
+ for (i = 0; i <= gvar->glyph_count; ++i)
+ sfnt_swap32 (&gvar->u.offset_long[i]);
+ }
+
+ /* Start reading shared coordinates. */
+
+ gvar->global_coords = ((sfnt_f2dot14 *) ((char *) gvar + off_size));
+
+ if (gvar->shared_coord_count)
+ {
+ if (INT_ADD_WRAPV (gvar->offset_to_coord, directory->offset,
+ &offset))
+ goto bail;
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ goto bail;
+
+ if (read (fd, gvar->global_coords, coordinate_size)
+ != coordinate_size)
+ goto bail;
+
+ for (i = 0; i <= coordinate_size / sizeof *gvar->global_coords; ++i)
+ sfnt_swap16 (&gvar->global_coords[i]);
+ }
+
+ /* Finally, read the rest of the glyph variation data. */
+ gvar->data_size = data_size;
+ gvar->glyph_variation_data
+ = (unsigned char *) (gvar->global_coords
+ + (coordinate_size
+ / sizeof *gvar->global_coords));
+
+ if (gvar->data_size)
+ {
+ if (INT_ADD_WRAPV (gvar->offset_to_data, directory->offset,
+ &offset))
+ goto bail;
+
+ if (lseek (fd, offset, SEEK_SET) != offset)
+ goto bail;
+
+ if (read (fd, gvar->glyph_variation_data,
+ gvar->data_size) != gvar->data_size)
+ goto bail;
+ }
+
+ /* Return the read gvar table. */
+ return gvar;
+
+ bail:
+ xfree (gvar);
+ return NULL;
+}
+
+
+
+/* Read an avar table from the given font FD. Use the table directory
+ specified in SUBTABLE.
+
+ Return the avar table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_avar_table *
+sfnt_read_avar_table (int fd, struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_avar_table *avar;
+ ssize_t rc;
+ size_t min_size, size, i, k, j;
+ uint16_t *buffer;
+ struct sfnt_short_frac_correspondence *correspondences;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_AVAR);
+
+ if (!directory)
+ return NULL;
+
+ min_size = SFNT_ENDOF (struct sfnt_avar_table, axis_count, uint32_t);
+
+ /* Check that the length is at least min_size. */
+ if (directory->length < min_size)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold the avar table header. */
+ avar = xmalloc (sizeof *avar);
+
+ /* Read the avar table header. */
+ rc = read (fd, avar, min_size);
+ if (rc != min_size)
+ goto bail;
+
+ /* Swap what was read. */
+ sfnt_swap32 (&avar->version);
+ sfnt_swap32 (&avar->axis_count);
+
+ if (avar->version != 0x00010000)
+ goto bail;
+
+ if (avar->axis_count < 0)
+ goto bail;
+
+ /* Allocate a buffer that holds the rest of the data. */
+ size = directory->length - min_size;
+ buffer = xmalloc (size);
+ rc = read (fd, buffer, size);
+ if (rc != size)
+ goto bail1;
+
+ /* Swap each word. */
+ for (i = 0; i < size / sizeof *buffer; ++i)
+ sfnt_swap16 (&buffer[i]);
+
+ /* Now, determine how big the resulting data needs to be. Each
+ struct has a pointer field, and that should be its alignment. */
+
+ k = 0;
+ min_size = sizeof *avar;
+ for (i = 0; i < avar->axis_count; ++i)
+ {
+ /* Check that k remains within bounds. */
+ if (k >= size / sizeof *buffer)
+ goto bail1;
+
+ /* Now add one struct sfnt_short_frac_segment for each axis and
+ each of its correspondences. */
+ if (INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_segment),
+ min_size, &min_size)
+ || INT_ADD_WRAPV (sizeof (struct sfnt_short_frac_correspondence)
+ * buffer[k], min_size, &min_size))
+ goto bail1;
+
+ /* Verify that words from here to buffer[1 + buffer[k] * 2], the
+ next pairCount field, are within bounds. */
+ j = k + 1 + buffer[k] * 2;
+ if (j > size / sizeof *buffer)
+ goto bail1;
+
+ /* Move to the next pairCount field. */
+ k = j;
+ }
+
+ /* Resize avar to min_size and start filling in various
+ pointers. */
+ avar = xrealloc (avar, min_size);
+ avar->segments = (struct sfnt_short_frac_segment *) (avar + 1);
+ correspondences
+ = ((struct sfnt_short_frac_correspondence *) (avar->segments
+ + avar->axis_count));
+
+ k = 0;
+ for (i = 0; i < avar->axis_count; ++i)
+ {
+ avar->segments[i].pair_count = buffer[k++];
+ avar->segments[i].correspondence = correspondences;
+
+ for (j = 0; j < avar->segments[i].pair_count; ++j)
+ {
+ correspondences->from_coord = buffer[k++];
+ correspondences->to_coord = buffer[k++];
+ correspondences++;
+ }
+ }
+
+ /* Return the read avar table. Free buffer. */
+ xfree (buffer);
+ return avar;
+
+ bail1:
+ xfree (buffer);
+ bail:
+ xfree (avar);
+ return NULL;
+}
+
+
+
+/* Read a sequence of packed points starting from DATA. Return the
+ number of points read in *NPOINTS_RETURN and the array of unpacked
+ points, or NULL upon failure.
+
+ If non-NULL, set LOCATION to DATA plus the number of bytes read
+ upon success.
+
+ Return (uint16_t *) -1 if there are no points at all.
+ In this case, deltas will apply to all points in the glyph,
+ and *NPOINTS_RETURN will be UINT16_MAX.
+
+ END is one byte past the last byte in DATA. */
+
+static uint16_t *
+sfnt_read_packed_points (unsigned char *restrict data,
+ uint16_t *npoints_return,
+ unsigned char *restrict end,
+ unsigned char *restrict *location)
+{
+ int npoints;
+ uint16_t *points;
+ int i, first, control;
+
+ points = NULL;
+ npoints = 0;
+
+ if (data >= end)
+ return NULL;
+
+ /* Load the control byte. */
+ control = *data++;
+
+ if (!control)
+ {
+ *npoints_return = UINT16_MAX;
+ *location = data;
+ return (uint16_t *) -1;
+ }
+
+ /* Now figure out the number of points within. */
+
+ if (control & 0x80)
+ {
+ npoints = control & 0x7f;
+ npoints <<= 8;
+
+ if (data >= end)
+ return NULL;
+
+ npoints |= *data++;
+ }
+ else
+ npoints = control;
+
+ /* Start reading points. */
+ first = 0;
+ i = 0;
+ points = xmalloc (sizeof *points * npoints);
+
+ while (i < npoints)
+ {
+ if (data >= end)
+ goto bail;
+
+ control = *data++;
+
+ if (control & 0x80)
+ {
+ /* Next control & 0x7f words are points. */
+
+ control &= 0x7f;
+
+ while (control != -1 && i < npoints)
+ {
+ if (data >= end || data + 1 >= end)
+ goto bail;
+
+ first += *data++ << 8u;
+ first += *data++;
+ points[i] = first;
+ control -= 1, ++i;
+ }
+ }
+ else
+ {
+ /* Next control bytes are points. */
+
+ while (control != -1 && i < npoints)
+ {
+ if (data >= end)
+ goto bail;
+
+ first += *data++;
+ points[i] = first;
+ control -= 1, ++i;
+ }
+ }
+ }
+
+ /* Return the points read. */
+ *npoints_return = npoints;
+ *location = data;
+ return points;
+
+ bail:
+ xfree (points);
+ return NULL;
+}
+
+/* Read and return N packed deltas from DATA. Set *DATA_RETURN to
+ DATA plus the number of bytes read.
+
+ END is the end of the glyph variation data. Value is an array of N
+ deltas upon success, and NULL upon failure. */
+
+static sfnt_fword *
+sfnt_read_packed_deltas (unsigned char *restrict data,
+ unsigned char *restrict end,
+ int n,
+ unsigned char *restrict *data_return)
+{
+ sfnt_fword *deltas;
+ int i, count;
+ unsigned char control;
+ uint16_t value;
+
+ if (data >= end)
+ return NULL;
+
+ deltas = xmalloc (sizeof *deltas * n);
+ i = 0;
+
+ while (i < n)
+ {
+ if (data >= end)
+ goto fail;
+
+ control = *data++;
+ count = control & 0x3f;
+
+ while (count != -1 && i < n)
+ {
+ if (control & 0x80)
+ deltas[i++] = 0;
+ else if (control & 0x40)
+ {
+ if (data + 1 >= end)
+ goto fail;
+
+ value = *data++ << 8;
+ value |= *data++;
+ deltas[i++] = value;
+ }
+ else
+ {
+ if (data >= end)
+ goto fail;
+
+ deltas[i++] = (signed char) *data++;
+ }
+
+ --count;
+ }
+ }
+
+ *data_return = data;
+ return deltas;
+
+ fail:
+ xfree (deltas);
+ return NULL;
+}
+
+/* Read a cvar table from the given font FD. Use the table directory
+ specified in SUBTABLE, axis information provided in the fvar table
+ FVAR, and CVT information provided in the cvt table CVT.
+
+ Return the cvar table upon success, else NULL. */
+
+TEST_STATIC struct sfnt_cvar_table *
+sfnt_read_cvar_table (int fd, struct sfnt_offset_subtable *subtable,
+ struct sfnt_fvar_table *fvar,
+ struct sfnt_cvt_table *cvt)
+{
+ struct sfnt_table_directory *directory;
+ struct sfnt_cvar_table *cvar;
+ ssize_t rc;
+ size_t ntuples, size;
+ int i, j;
+ sfnt_f2dot14 *coords;
+ uint16_t *local, *points, npoints, data_size, min_size, index;
+ unsigned char *buffer, *data, *end, *tuple;
+ ptrdiff_t data_offset;
+ sfnt_fword *deltas;
+
+ /* Find the table in the directory. */
+
+ directory = sfnt_find_table (subtable, SFNT_TABLE_CVAR);
+
+ if (!directory)
+ return NULL;
+
+ min_size = SFNT_ENDOF (struct sfnt_cvar_table, data_offset,
+ uint16_t);
+
+ /* Check that the length is at least min_size. */
+ if (directory->length < min_size)
+ return NULL;
+
+ /* Seek to the location given in the directory. */
+ if (lseek (fd, directory->offset, SEEK_SET) == (off_t) -1)
+ return NULL;
+
+ /* Allocate enough to hold the cvar table header. */
+ cvar = xmalloc (sizeof *cvar);
+
+ /* Read the cvar table header. */
+ rc = read (fd, cvar, min_size);
+ if (rc != min_size)
+ goto bail;
+
+ /* Swap what was read. */
+ sfnt_swap32 (&cvar->version);
+ sfnt_swap16 (&cvar->tuple_count);
+ sfnt_swap16 (&cvar->data_offset);
+
+ /* Read the rest of the table. */
+ size = directory->length - min_size;
+ buffer = xmalloc (size);
+ rc = read (fd, buffer, size);
+ if (rc != size)
+ goto bail;
+
+ /* Now figure out how large cvar must be by reading the tuples. */
+
+ ntuples = cvar->tuple_count & 0x0fff;
+ data_offset = ((ptrdiff_t) cvar->data_offset
+ - (ptrdiff_t) min_size);
+ end = buffer + size;
+
+ if (data_offset < 0)
+ goto bail1;
+
+ /* See if there are shared points, and read them if there are. */
+
+ data = buffer + data_offset;
+ tuple = buffer;
+ points = NULL;
+
+ if (cvar->tuple_count & 0x8000)
+ {
+ points = sfnt_read_packed_points (data, &npoints, end,
+ &tuple);
+ if (!points)
+ goto bail1;
+
+ /* Add npoints words to the size. */
+ size = npoints * sizeof *points;
+ }
+ else
+ size = 0;
+
+ while (ntuples--)
+ {
+ data = buffer + data_offset;
+
+ /* Read the tuple. */
+ if (tuple + 3 >= end)
+ goto bail2;
+
+ memcpy (&data_size, tuple, sizeof data_size);
+ tuple += sizeof data_size;
+ memcpy (&index, tuple, sizeof index);
+ tuple += sizeof index;
+ sfnt_swap16 (&data_size);
+ sfnt_swap16 (&index);
+
+ /* Increment the offset to the data by the data size specified
+ here. */
+ data_offset += data_size;
+
+ if (index & 0x8000)
+ {
+ /* Embedded coordinates are present. Read each
+ coordinate and add it to the size. */
+
+ if (tuple + fvar->axis_count * sizeof *coords - 1 >= end)
+ goto bail2;
+
+ tuple += sizeof *coords * fvar->axis_count;
+ if (INT_ADD_WRAPV (size, sizeof *coords * fvar->axis_count,
+ &size))
+ goto bail2;
+ }
+ else
+ /* This table is invalid, as cvar tables don't have global
+ coordinates. */
+ goto bail2;
+
+ /* Now read indeterminate tuples if required. */
+ if (index & 0x4000)
+ {
+ tuple += fvar->axis_count * 4;
+ if (INT_ADD_WRAPV (size, fvar->axis_count * 4, &size))
+ goto bail2;
+ }
+
+ /* Add one point and one delta for each CVT element. */
+ if (INT_ADD_WRAPV (size, cvt->num_elements * 4, &size))
+ goto bail2;
+
+ /* Now add the size of the tuple. */
+ if (INT_ADD_WRAPV (size, sizeof *cvar->variation, &size))
+ goto bail2;
+ }
+
+ if (INT_ADD_WRAPV (sizeof *cvar, size, &size))
+ goto bail2;
+
+ /* Reallocate cvar. */
+ cvar = xrealloc (cvar, size);
+ ntuples = cvar->tuple_count & 0x0fff;
+ cvar->variation = (struct sfnt_tuple_variation *) (cvar + 1);
+ coords = (sfnt_f2dot14 *) (cvar->variation + ntuples);
+ tuple = buffer;
+
+ data_offset = ((ptrdiff_t) cvar->data_offset
+ - (ptrdiff_t) min_size);
+
+ /* Start reading the tuples into cvar. */
+ for (i = 0; i < ntuples; ++i)
+ {
+ data = buffer + data_offset;
+
+ /* Read the tuple. */
+ if (tuple + 3 >= end)
+ goto bail2;
+
+ memcpy (&data_size, tuple, sizeof data_size);
+ tuple += sizeof data_size;
+ memcpy (&index, tuple, sizeof index);
+ tuple += sizeof index;
+ sfnt_swap16 (&data_size);
+ sfnt_swap16 (&index);
+
+ /* Increment the offset to the data by the data size specified
+ here. */
+ data_offset += data_size;
+
+ cvar->variation[i].intermediate_start = NULL;
+ cvar->variation[i].intermediate_end = NULL;
+
+ if (index & 0x8000)
+ {
+ /* Embedded coordinates are present. Read each
+ coordinate. */
+ cvar->variation[i].coordinates = coords;
+
+ for (j = 0; j < fvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto bail2;
+
+ memcpy (coords++, tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (coords);
+ }
+ }
+ else
+ goto bail2;
+
+ /* Now read indeterminate tuples if required. */
+ if (index & 0x4000)
+ {
+ cvar->variation[i].intermediate_start = coords;
+
+ for (j = 0; j < fvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto bail2;
+
+ memcpy (coords++, tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (coords);
+ }
+
+ cvar->variation[i].intermediate_end = coords;
+
+ for (j = 0; j < fvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto bail2;
+
+ memcpy (coords++, tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (coords);
+ }
+ }
+
+ /* Finally, read private ``point'' numbers. If this flag is not
+ set, use shared point numbers previously read.
+
+ Read at most CVT->num_elements points, as that is all the
+ storage allocated. */
+
+ if (index & 0x2000)
+ {
+ local = sfnt_read_packed_points (data, &cvar->variation[i].num_points,
+ end, &data);
+ if (!local)
+ goto bail2;
+
+ /* If points apply to all CVT indices, skip this part. */
+
+ if (cvar->variation[i].num_points != UINT16_MAX)
+ {
+ if (cvar->variation[i].num_points > cvt->num_elements)
+ cvar->variation[i].num_points = cvt->num_elements;
+
+ cvar->variation[i].points = (uint16_t *) coords;
+ for (j = 0; j < cvar->variation[i].num_points; ++j)
+ *coords++ = local[j];
+ xfree (local);
+ }
+ else
+ cvar->variation[i].points = NULL;
+ }
+ else
+ {
+ /* Copy in the shared point numbers instead. */
+ cvar->variation[i].num_points = npoints;
+
+ if (npoints != UINT16_MAX)
+ {
+ if (cvar->variation[i].num_points > cvt->num_elements)
+ cvar->variation[i].num_points = cvt->num_elements;
+
+ cvar->variation[i].points = (uint16_t *) coords;
+ for (j = 0; j < cvar->variation[i].num_points; ++j)
+ *coords++ = points[j];
+ }
+ else
+ cvar->variation[i].points = NULL;
+ }
+
+ /* And read packed deltas. If cvar->variation[i].num_points is
+ UINT16_MAX, then there is one delta for each CVT entry.
+ Otherwise, there are that many deltas. */
+
+ if (cvar->variation[i].num_points == UINT16_MAX)
+ {
+ deltas = sfnt_read_packed_deltas (data, end, cvt->num_elements,
+ &data);
+
+ if (!deltas)
+ goto bail2;
+
+ cvar->variation[i].deltas = coords;
+
+ for (j = 0; j < cvt->num_elements; ++j)
+ *coords++ = deltas[j];
+ xfree (deltas);
+ }
+ else
+ {
+ deltas = sfnt_read_packed_deltas (data, end,
+ cvar->variation[i].num_points,
+ &data);
+ if (!deltas)
+ goto bail2;
+
+ cvar->variation[i].deltas = coords;
+
+ for (j = 0; j < cvar->variation[i].num_points; ++j)
+ *coords++ = deltas[j];
+ xfree (deltas);
+ }
+ }
+
+ /* Free data and return the read cvar table. */
+ if (points != (void *) -1)
+ xfree (points);
+ xfree (buffer);
+ return cvar;
+
+ bail2:
+ if (points != (void *) -1)
+ xfree (points);
+ bail1:
+ xfree (buffer);
+ bail:
+ xfree (cvar);
+ return NULL;
+}
+
+
+
+/* Initialize the specified BLEND with the given FVAR and GVAR tables.
+ If non-NULL, adjust normalized coordinates using the axis variation
+ table AVAR; similarly, adjust interpreter CVT values using CVAR, if
+ specified. */
+
+TEST_STATIC void
+sfnt_init_blend (struct sfnt_blend *blend, struct sfnt_fvar_table *fvar,
+ struct sfnt_gvar_table *gvar, struct sfnt_avar_table *avar,
+ struct sfnt_cvar_table *cvar)
+{
+ size_t size;
+
+ blend->fvar = fvar;
+ blend->gvar = gvar;
+ blend->avar = avar;
+ blend->cvar = cvar;
+
+ /* Allocate a single array to hold both coords and norm_coords. */
+ size = (fvar->axis_count * sizeof *blend->coords * 2);
+ blend->coords = xmalloc (size);
+ blend->norm_coords = blend->coords + fvar->axis_count;
+}
+
+/* Free what was initialized in the specified BLEND. */
+
+TEST_STATIC void
+sfnt_free_blend (struct sfnt_blend *blend)
+{
+ xfree (blend->coords);
+}
+
+/* Normalize BLEND->fvar->axis_count coordinates in BLEND->coords and
+ place the result in BLEND->norm_coords. */
+
+TEST_STATIC void
+sfnt_normalize_blend (struct sfnt_blend *blend)
+{
+ struct sfnt_variation_axis *axis;
+ int i, j;
+ sfnt_fixed from, coord, j0, j1, j2;
+ sfnt_fixed from_last, coord_last;
+ struct sfnt_short_frac_segment *segment;
+
+ /* For each axis... */
+ for (i = 0; i < blend->fvar->axis_count; ++i)
+ {
+ /* Normalize based on [min, default, max], into [-1, 0, 1]. */
+ axis = &blend->fvar->axis[i];
+
+ /* Load the current design coordinate. */
+ coord = blend->coords[i];
+
+ /* Keep it within bounds. */
+
+ if (coord > axis->max_value)
+ coord = axis->max_value;
+ else if (coord < axis->min_value)
+ coord = axis->min_value;
+
+ if (coord > axis->default_value)
+ {
+ /* Avoid division by 0. */
+ if (axis->max_value != axis->default_value)
+ blend->norm_coords[i]
+ = sfnt_div_fixed (sfnt_sub (coord, axis->default_value),
+ sfnt_sub (axis->max_value,
+ axis->default_value));
+ else
+ blend->norm_coords[i] = 0;
+ }
+ else if (coord < axis->default_value)
+ {
+ if (axis->default_value != axis->min_value)
+ blend->norm_coords[i]
+ = sfnt_div_fixed (sfnt_sub (coord, axis->default_value),
+ sfnt_sub (axis->default_value,
+ axis->min_value));
+ else
+ blend->norm_coords[i] = 0;
+ }
+ else
+ blend->norm_coords[i] = 0;
+ }
+
+ /* Now, apply axis variations, but only if the avar table has the
+ right number of axes. */
+
+ if (blend->avar && (blend->fvar->axis_count
+ == blend->avar->axis_count))
+ {
+ for (i = 0; i < blend->fvar->axis_count; ++i)
+ {
+ segment = &blend->avar->segments[i];
+
+ /* Search for a correspondence record above the normalized
+ coordinate of this axis. */
+
+ for (j = 1; j < segment->pair_count; ++j)
+ {
+ from = segment->correspondence[j].from_coord * 4;
+ coord = segment->correspondence[j].to_coord * 4;
+
+ if (blend->norm_coords[i] < from)
+ {
+ from_last
+ = segment->correspondence[j - 1].from_coord * 4;
+ coord_last
+ = segment->correspondence[j - 1].to_coord * 4;
+
+ j0 = blend->norm_coords[i] - from_last;
+ j1 = coord - coord_last;
+ j2 = from - from_last;
+
+ blend->norm_coords[i]
+ = (sfnt_multiply_divide_signed (j0, j1, j2) + coord_last);
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+
+struct sfnt_gvar_glyph_header
+{
+ /* A packed field. The high 4 bits are flags and the low 12 bits are
+ the number of tuples for this glyph. The number of tuples can be
+ any number between 1 and 4095. */
+ uint16_t tuple_count;
+
+ /* Offset from the start of the GlyphVariationData table to the
+ serialized data. */
+ uint16_t data_offset;
+};
+
+/* Given a BLEND containing normalized coordinates, an array of
+ BLEND->gvar->axis_count tuple coordinates, and, if INTERMEDIATE_P,
+ a range of tuple coordinates from INTERMEDIATE_START to
+ INTERMEDIATE_END, return the scaling factor to apply to deltas for
+ each corresponding point. */
+
+static sfnt_fixed
+sfnt_compute_tuple_scale (struct sfnt_blend *blend, bool intermediate_p,
+ sfnt_f2dot14 *coords,
+ sfnt_f2dot14 *intermediate_start,
+ sfnt_f2dot14 *intermediate_end)
+{
+ int i;
+ sfnt_fixed coord, start, end;
+ sfnt_fixed scale;
+
+ /* scale is initially 1.0. */
+ scale = 0200000;
+
+ for (i = 0; i < blend->gvar->axis_count; ++i)
+ {
+ /* Load values for this axis, scaled up to sfnt_fixed. */
+ coord = coords[i] * 4;
+
+ if (intermediate_p)
+ {
+ start = intermediate_start[i] * 4;
+ end = intermediate_start[i] * 4;
+ }
+
+ /* Ignore tuples that can be skipped. */
+
+ if (!coord)
+ continue;
+
+ /* If the coordinate is set to 0, then deltas should not be
+ applied. Return 0. */
+
+ if (!blend->norm_coords[i])
+ return 0;
+
+ /* If no scaling need take place, continue. */
+
+ if (blend->norm_coords[i] == coord)
+ continue;
+
+ if (!intermediate_p)
+ {
+ /* Not an intermediate tuple; if coord is less than 0 and
+ blend->norm_coords[i] < coord, or coord is more than 0
+ and blend->norm_coords[i] > coord, then it doesn't fit,
+ so return. */
+
+ if (blend->norm_coords[i] < MIN (0, coord)
+ || blend->norm_coords[i] > MAX (0, coord))
+ return 0;
+
+ scale = sfnt_multiply_divide_signed (scale,
+ blend->norm_coords[i],
+ coord);
+ }
+ else
+ {
+ /* Otherwise, renormalize between start and end. */
+
+ if (blend->norm_coords[i] < start
+ || blend->norm_coords[i] > end)
+ return 0;
+
+ if (blend->norm_coords[i] < coord)
+ scale = sfnt_multiply_divide (scale,
+ blend->norm_coords[i] - start,
+ coord - start);
+ else
+ scale = sfnt_multiply_divide (scale,
+ end - blend->norm_coords[i],
+ end - coord);
+ }
+ }
+
+ return scale;
+}
+
+/* Infer point positions for points that have been partially moved
+ within the contour in GLYPH denoted by START and END. */
+
+static void
+sfnt_infer_deltas_1 (struct sfnt_glyph *glyph, size_t start,
+ size_t end, bool *touched, sfnt_fword *x,
+ sfnt_fword *y)
+{
+ size_t i, pair_start, pair_end, pair_first, j;
+ sfnt_fword min_pos, max_pos, position;
+ sfnt_fixed ratio, delta;
+
+ pair_start = pair_first = -1;
+
+ /* Look for pairs of touched points. */
+
+ for (i = start; i <= end; ++i)
+ {
+ if (!touched[i])
+ continue;
+
+ if (pair_start == -1)
+ {
+ pair_first = i;
+ goto next;
+ }
+
+ pair_end = i;
+
+ /* pair_start to pair_end are now a pair of points, where points
+ in between should be interpolated. */
+
+ for (j = pair_start + 1; j < pair_end; ++j)
+ {
+ /* Consider the X axis. Set min_pos and max_pos to the
+ smallest and greatest values along that axis. */
+ min_pos = MIN (x[pair_start], x[pair_end]);
+ max_pos = MAX (x[pair_start], x[pair_end]);
+
+ /* Now see if the current point lies between min and
+ max... */
+ if (x[j] >= min_pos && x[j] <= max_pos)
+ {
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
+ {
+ if ((glyph->simple->x_coordinates[pair_start]
+ - x[pair_start])
+ == (glyph->simple->x_coordinates[pair_end]
+ - x[pair_end]))
+ glyph->simple->x_coordinates[j]
+ += (glyph->simple->x_coordinates[pair_start]
+ - x[pair_start]);
+
+ continue;
+ }
+
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->x_coordinates[j] = min_pos + delta;
+ }
+ else
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ if (x[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - max_pos;
+ }
+ else
+ {
+ position = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - min_pos;
+ }
+
+ glyph->simple->x_coordinates[j] = x[j] + delta;
+ }
+
+ /* Now, consider the Y axis. */
+ min_pos = MIN (y[pair_start], y[pair_end]);
+ max_pos = MAX (y[pair_start], y[pair_end]);
+
+ /* Now see if the current point lies between min and
+ max... */
+ if (y[j] >= min_pos && y[j] <= max_pos)
+ {
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
+ {
+ if ((glyph->simple->y_coordinates[pair_start]
+ - y[pair_start])
+ == (glyph->simple->y_coordinates[pair_end]
+ - y[pair_end]))
+ glyph->simple->y_coordinates[j]
+ += (glyph->simple->y_coordinates[pair_start]
+ - y[pair_start]);
+
+ continue;
+ }
+
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->y_coordinates[j] = min_pos + delta;
+ }
+ else
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ if (y[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - max_pos;
+ }
+ else
+ {
+ position = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - min_pos;
+ }
+
+ glyph->simple->y_coordinates[j] = y[j] + delta;
+ }
+ }
+
+ next:
+ pair_start = i;
+ }
+
+ /* If pair_start is set, then lerp points between it and
+ pair_first. */
+
+ if (pair_start != (size_t) -1)
+ {
+ j = pair_start + 1;
+
+ if (j > end)
+ j = start;
+
+ pair_end = pair_first;
+
+ while (j != pair_first)
+ {
+ /* Consider the X axis. Set min_pos and max_pos to the
+ smallest and greatest values along that axis. */
+ min_pos = MIN (x[pair_start], x[pair_end]);
+ max_pos = MAX (x[pair_start], x[pair_end]);
+
+ /* Now see if the current point lies between min and
+ max... */
+ if (x[j] >= min_pos && x[j] <= max_pos)
+ {
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
+ {
+ if ((glyph->simple->x_coordinates[pair_start]
+ - x[pair_start])
+ == (glyph->simple->x_coordinates[pair_end]
+ - x[pair_end]))
+ glyph->simple->x_coordinates[j]
+ += (glyph->simple->x_coordinates[pair_start]
+ - x[pair_start]);
+
+ goto next_1;
+ }
+
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (x[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->x_coordinates[j] = min_pos + delta;
+ }
+ else
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ if (x[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - max_pos;
+ }
+ else
+ {
+ position = MIN (glyph->simple->x_coordinates[pair_start],
+ glyph->simple->x_coordinates[pair_end]);
+ delta = position - min_pos;
+ }
+
+ glyph->simple->x_coordinates[j] = x[j] + delta;
+ }
+
+ /* Now, consider the Y axis. */
+ min_pos = MIN (y[pair_start], y[pair_end]);
+ max_pos = MAX (y[pair_start], y[pair_end]);
+
+ /* Now see if the current point lies between min and
+ max... */
+ if (y[j] >= min_pos && y[j] <= max_pos)
+ {
+ /* If min_pos and max_pos are the same, apply
+ pair_start's delta if it is identical to that of
+ pair_end, or apply nothing at all otherwise. */
+
+ if (min_pos == max_pos)
+ {
+ if ((glyph->simple->y_coordinates[pair_start]
+ - y[pair_start])
+ == (glyph->simple->y_coordinates[pair_end]
+ - y[pair_end]))
+ glyph->simple->y_coordinates[j]
+ += (glyph->simple->y_coordinates[pair_start]
+ - y[pair_start]);
+
+ goto next_1;
+ }
+
+ /* Interpolate between min_pos and max_pos. */
+ ratio = sfnt_div_fixed ((sfnt_sub (y[j], min_pos)
+ * 65536),
+ (sfnt_sub (max_pos, min_pos)
+ * 65536));
+
+ /* Load the current positions of pair_start and pair_end
+ along this axis. */
+ min_pos = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ max_pos = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+
+ /* Lerp in between. */
+ delta = sfnt_sub (max_pos, min_pos);
+ delta = sfnt_mul_fixed (ratio, delta);
+ glyph->simple->y_coordinates[j] = min_pos + delta;
+ }
+ else
+ {
+ /* ... otheriwse, move point j by the delta of the
+ nearest touched point. */
+
+ if (y[j] >= max_pos)
+ {
+ position = MAX (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - max_pos;
+ }
+ else
+ {
+ position = MIN (glyph->simple->y_coordinates[pair_start],
+ glyph->simple->y_coordinates[pair_end]);
+ delta = position - min_pos;
+ }
+
+ glyph->simple->y_coordinates[j] = y[j] + delta;
+ }
+
+ next_1:
+ j++;
+ if (j > end)
+ j = start;
+ }
+ }
+}
+
+/* Infer point positions for contours that have been partially moved
+ by variation. For each contour in GLYPH, find pairs of points
+ which have had deltas applied. For each such pair, interpolate
+ points between the first point in the pair and the second by
+ considering each point along every one of the two axes (X and Y)
+ like so:
+
+ - For each point that lies between the first point and the last
+ on the axis currently being considered, interpolate its
+ position in that axis so that the ratio between the first
+ point and the last in the original outline still holds.
+
+ - For each point that lies to the left or top of the first point
+ on the axis being considered, use the delta of the first point.
+
+ - And finally, for each point that lies to the right or bottom of
+ the last point on that axis, use the delta of the last
+ point.
+
+ X and Y contain the original positions positions of each point.
+ TOUCHED contains whether or not each point has been changed by
+ an explicitly specified delta.
+
+ Apply the inferred deltas back to GLYPH. */
+
+static void
+sfnt_infer_deltas (struct sfnt_glyph *glyph, bool *touched,
+ sfnt_fword *x, sfnt_fword *y)
+{
+ size_t i;
+ int point, first, end;
+
+ point = 0;
+ for (i = 0; i < glyph->number_of_contours; ++i)
+ {
+ first = point;
+ end = glyph->simple->end_pts_of_contours[i];
+
+ /* Return if the glyph is invalid. */
+
+ if (first >= glyph->simple->number_of_points
+ || end >= glyph->simple->number_of_points
+ || first > end)
+ return;
+
+ sfnt_infer_deltas_1 (glyph, first, end, touched, x, y);
+ point = end + 1;
+ }
+}
+
+/* Read the glyph variation data for the specified glyph ID from
+ BLEND's gvar table. Apply the offsets to each point in the
+ specified simple GLYPH, based on the specified BLEND.
+
+ Value is 0 upon success, else 1.
+
+ The glyph variation data consists of a number of elements, each of
+ which has its own associated point numbers and deltas, and a list
+ of one or two coordinates for each axis. Each such list is
+ referred to as a ``tuple''.
+
+ The deltas, one for each point, are multipled by the normalized
+ value of each axis and applied to those points for each tuple that
+ is found to be applicable.
+
+ Each element of the glyph variation data is applicable to an axis
+ if its list of coordinates:
+
+ - contains one element for each axis, and its axis has a value
+ between 0 and that element.
+
+ - contains two elements for each axis, and its axis has a value
+ between the first element and the second.
+
+ Return the deltas that would normally be applied to the two phantom
+ points describing horizontal bounds in *DISTORTION. Do not
+ transform the outline to reflect adjustments to the origin
+ point. */
+
+TEST_STATIC int
+sfnt_vary_simple_glyph (struct sfnt_blend *blend, sfnt_glyph id,
+ struct sfnt_glyph *glyph,
+ struct sfnt_metrics_distortion *distortion)
+{
+ uint32_t offset;
+ struct sfnt_gvar_glyph_header header;
+ uint16_t *points, npoints;
+ int i, ntuples, j, point_count;
+ unsigned char *tuple, *end, *data;
+ uint16_t data_size, index, *glyph_points;
+ sfnt_f2dot14 *restrict coords;
+ sfnt_f2dot14 *restrict intermediate_start;
+ sfnt_f2dot14 *restrict intermediate_end;
+ sfnt_fword *restrict dx, *restrict dy, fword;
+ struct sfnt_gvar_table *gvar;
+ uint16_t *local_points, n_local_points;
+ sfnt_fixed scale;
+ ptrdiff_t data_offset;
+ bool *touched;
+ sfnt_fword *restrict original_x, *restrict original_y;
+
+ gvar = blend->gvar;
+
+ if (gvar->axis_count != blend->fvar->axis_count)
+ return 1;
+
+ if (gvar->glyph_count <= id)
+ return 1;
+
+ if (gvar->flags & 1)
+ offset = gvar->u.offset_long[id];
+ else
+ offset = gvar->u.offset_word[id] * 2u;
+
+ if (offset >= gvar->data_size)
+ return 1;
+
+ end = gvar->glyph_variation_data + gvar->data_size;
+
+ /* Start reading the header. */
+
+ if (offset + sizeof header > gvar->data_size)
+ return 1;
+
+ /* Clear the distortion. */
+ distortion->origin = 0;
+ distortion->advance = 0;
+
+ memcpy (&header, gvar->glyph_variation_data + offset,
+ sizeof header);
+
+ /* Swap the header. */
+ sfnt_swap16 (&header.tuple_count);
+ sfnt_swap16 (&header.data_offset);
+
+ /* Prepare to read each tuple. */
+ ntuples = header.tuple_count & 0x0fff;
+
+ /* Initialize the data offset. This is incremented with each tuple
+ read. */
+ data_offset = header.data_offset;
+
+ /* If gvar->flags & tuples_share_point_numbers, read the shared
+ point numbers. */
+
+ npoints = 0;
+
+ if (header.tuple_count & 0x8000)
+ {
+ data = gvar->glyph_variation_data + offset + data_offset;
+ points = sfnt_read_packed_points (data, &npoints, end,
+ &tuple);
+
+ if (!points)
+ return 1;
+
+ /* Shared point numbers are part of the data after the tuple
+ array. Thus, increment data_offset by tuple - data. `tuple'
+ here holds no relation to a pointer to the current part of
+ the tuple array that is being read later on. */
+ data_offset += tuple - data;
+ }
+ else
+ points = NULL;
+
+ /* Start reading each tuple. */
+ tuple = gvar->glyph_variation_data + offset + sizeof header;
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ coords = xmalloc (gvar->axis_count * sizeof *coords * 3);
+ else
+ coords = alloca (gvar->axis_count * sizeof *coords * 3);
+
+ intermediate_start = coords + gvar->axis_count;
+ intermediate_end = coords + gvar->axis_count;
+
+ /* Allocate arrays of booleans and fwords to keep track of which
+ points have been touched. */
+ touched = NULL;
+ original_x = NULL;
+ original_y = NULL;
+
+ while (ntuples--)
+ {
+ data = gvar->glyph_variation_data + offset + data_offset;
+
+ if (tuple + 3 >= end)
+ goto fail1;
+
+ memcpy (&data_size, tuple, sizeof data_size);
+ tuple += sizeof data_size;
+ memcpy (&index, tuple, sizeof index);
+ tuple += sizeof index;
+ sfnt_swap16 (&data_size);
+ sfnt_swap16 (&index);
+
+ /* Increment the offset to the data by the data size specified
+ here. */
+ data_offset += data_size;
+
+ if (index & 0x8000)
+ {
+ /* Embedded coordinates are present. Read each
+ coordinate and add it to the tuple. */
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&coords[j], tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (&coords[j]);
+ }
+ }
+ else if ((index & 0xfff) > gvar->shared_coord_count)
+ /* index exceeds the number of shared tuples present. */
+ goto fail1;
+ else
+ /* index points into gvar->axis_count coordinates making up
+ the tuple. */
+ memcpy (coords, (gvar->global_coords
+ + ((index & 0xfff) * gvar->axis_count)),
+ gvar->axis_count * sizeof *coords);
+
+ /* Now read indeterminate tuples if required. */
+ if (index & 0x4000)
+ {
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&intermediate_start[j], tuple,
+ sizeof *intermediate_start);
+ tuple += sizeof *intermediate_start;
+ sfnt_swap16 (&intermediate_start[j]);
+ }
+
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&intermediate_end[j], tuple,
+ sizeof *intermediate_end);
+ tuple += sizeof *intermediate_end;
+ sfnt_swap16 (&intermediate_end[j]);
+ }
+ }
+
+ /* See whether or not the tuple applies to the current variation
+ configuration, and how much to scale them by. */
+
+ scale = sfnt_compute_tuple_scale (blend, index & 0x4000,
+ coords, intermediate_start,
+ intermediate_end);
+
+ if (!scale)
+ continue;
+
+ local_points = NULL;
+
+ /* Finally, read private point numbers.
+ Set local_points to those numbers; it will be freed
+ once the loop ends. */
+
+ if (index & 0x2000)
+ {
+ local_points = sfnt_read_packed_points (data, &n_local_points,
+ end, &data);
+ if (!local_points)
+ goto fail1;
+
+ point_count = n_local_points;
+ glyph_points = local_points;
+ }
+ else
+ {
+ /* If there are no private point numbers, use global
+ points. */
+ point_count = npoints;
+ glyph_points = points;
+ }
+
+ /* Now, read packed deltas. */
+
+ dx = NULL;
+ dy = NULL;
+
+ switch (point_count)
+ {
+ case UINT16_MAX:
+ /* Deltas are provided for all points in the glyph.
+ No glyph should have more than 65535 points. */
+
+ /* Add 4 phantom points to each end. */
+ dx = sfnt_read_packed_deltas (data, end,
+ glyph->simple->number_of_points + 4,
+ &data);
+ dy = sfnt_read_packed_deltas (data, end,
+ glyph->simple->number_of_points + 4,
+ &data);
+
+ if (!dx || !dy)
+ goto fail3;
+
+ /* Apply each delta to the simple glyph. */
+
+ for (i = 0; i < glyph->simple->number_of_points; ++i)
+ {
+ fword = sfnt_mul_fixed_round (dx[i], scale);
+ glyph->simple->x_coordinates[i] += fword;
+ fword = sfnt_mul_fixed_round (dy[i], scale);
+ glyph->simple->y_coordinates[i] += fword;
+ }
+
+ /* Apply the deltas for the two phantom points. */
+ distortion->origin += sfnt_mul_fixed_round (dx[i++], scale);
+ distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+ break;
+
+ default:
+ dx = sfnt_read_packed_deltas (data, end, point_count, &data);
+ dy = sfnt_read_packed_deltas (data, end, point_count, &data);
+
+ if (!dx || !dy)
+ goto fail3;
+
+ /* Deltas are only applied for each point number read. */
+
+ if (!original_x)
+ {
+ if ((glyph->simple->number_of_points
+ * sizeof *touched) >= 1024 * 16)
+ touched = xmalloc (sizeof *touched
+ * glyph->simple->number_of_points);
+ else
+ touched = alloca (sizeof *touched
+ * glyph->simple->number_of_points);
+
+ if ((sizeof *original_x * 2
+ * glyph->simple->number_of_points) >= 1024 * 16)
+ original_x = xmalloc (sizeof *original_x * 2
+ * glyph->simple->number_of_points);
+ else
+ original_x = alloca (sizeof *original_x * 2
+ * glyph->simple->number_of_points);
+
+ original_y = original_x + glyph->simple->number_of_points;
+ memcpy (original_x, glyph->simple->x_coordinates,
+ (sizeof *original_x
+ * glyph->simple->number_of_points));
+ memcpy (original_y, glyph->simple->y_coordinates,
+ (sizeof *original_y
+ * glyph->simple->number_of_points));
+ }
+
+ memset (touched, 0, (sizeof *touched
+ * glyph->simple->number_of_points));
+
+ for (i = 0; i < point_count; ++i)
+ {
+ /* Apply deltas to phantom points. */
+
+ if (glyph_points[i] == glyph->simple->number_of_points)
+ {
+ distortion->origin += sfnt_mul_fixed_round (dx[i], scale);
+ continue;
+ }
+
+ if (glyph_points[i] == glyph->simple->number_of_points + 1)
+ {
+ distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+ continue;
+ }
+
+ /* Make sure the point doesn't end up out of bounds. */
+ if (glyph_points[i] >= glyph->simple->number_of_points)
+ continue;
+
+ fword = sfnt_mul_fixed_round (dx[i], scale);
+ glyph->simple->x_coordinates[glyph_points[i]] += fword;
+ fword = sfnt_mul_fixed_round (dy[i], scale);
+ glyph->simple->y_coordinates[glyph_points[i]] += fword;
+ touched[glyph_points[i]] = true;
+ }
+
+ sfnt_infer_deltas (glyph, touched, original_x,
+ original_y);
+ break;
+ }
+
+ xfree (dx);
+ xfree (dy);
+
+ if (local_points != (uint16_t *) -1)
+ xfree (local_points);
+ }
+
+ /* Return success. */
+
+ if ((glyph->simple->number_of_points
+ * sizeof *touched) >= 1024 * 16)
+ xfree (touched);
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if ((sizeof *original_x * 2
+ * glyph->simple->number_of_points) >= 1024 * 16)
+ xfree (original_x);
+
+ if (points != (uint16_t *) -1)
+ xfree (points);
+
+ /* Set the glyph metrics distortion as well. */
+ glyph->advance_distortion = distortion->advance;
+ glyph->origin_distortion = distortion->origin;
+
+ return 0;
+
+ fail3:
+ xfree (dx);
+ xfree (dy);
+ xfree (local_points);
+ fail1:
+
+ if ((glyph->simple->number_of_points
+ * sizeof *touched) >= 1024 * 16)
+ xfree (touched);
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if ((sizeof *original_x * 2
+ * glyph->simple->number_of_points) >= 1024 * 16)
+ xfree (original_x);
+
+ if (points != (uint16_t *) -1)
+ xfree (points);
+
+ return 1;
+}
+
+/* Read the glyph variation data for the specified glyph ID from
+ BLEND's gvar table. Apply the deltas specified within to each
+ component with offsets in the specified compound GLYPH, based on
+ the specified BLEND. Return distortions to phantom points in
+ *DISTORTION.
+
+ Value is 0 upon success, 1 otherwise. */
+
+TEST_STATIC int
+sfnt_vary_compound_glyph (struct sfnt_blend *blend, sfnt_glyph id,
+ struct sfnt_glyph *glyph,
+ struct sfnt_metrics_distortion *distortion)
+{
+ uint32_t offset;
+ struct sfnt_gvar_glyph_header header;
+ uint16_t *points, npoints;
+ int i, ntuples, j, point_count;
+ unsigned char *tuple, *end, *data;
+ uint16_t data_size, index, *glyph_points;
+ sfnt_f2dot14 *restrict coords;
+ sfnt_f2dot14 *restrict intermediate_start;
+ sfnt_f2dot14 *restrict intermediate_end;
+ sfnt_fword *restrict dx, *restrict dy, fword, word;
+ struct sfnt_gvar_table *gvar;
+ uint16_t *local_points, n_local_points;
+ sfnt_fixed scale;
+ ptrdiff_t data_offset;
+ struct sfnt_compound_glyph_component *component;
+
+ gvar = blend->gvar;
+
+ if (gvar->axis_count != blend->fvar->axis_count)
+ return 1;
+
+ if (gvar->glyph_count <= id)
+ return 1;
+
+ if (gvar->flags & 1)
+ offset = gvar->u.offset_long[id];
+ else
+ offset = gvar->u.offset_word[id] * 2u;
+
+ if (offset >= gvar->data_size)
+ return 1;
+
+ end = gvar->glyph_variation_data + gvar->data_size;
+
+ /* Start reading the header. */
+
+ if (offset + sizeof header > gvar->data_size)
+ return 1;
+
+ /* Clear the distortion. */
+ distortion->origin = 0;
+ distortion->advance = 0;
+
+ memcpy (&header, gvar->glyph_variation_data + offset,
+ sizeof header);
+
+ /* Swap the header. */
+ sfnt_swap16 (&header.tuple_count);
+ sfnt_swap16 (&header.data_offset);
+
+ /* Prepare to read each tuple. */
+ ntuples = header.tuple_count & 0x0fff;
+
+ /* Initialize the data offset. This is incremented with each tuple
+ read. */
+ data_offset = header.data_offset;
+
+ /* If gvar->flags & tuples_share_point_numbers, read the shared
+ point numbers. */
+
+ npoints = 0;
+
+ if (header.tuple_count & 0x8000)
+ {
+ data = gvar->glyph_variation_data + offset + data_offset;
+ points = sfnt_read_packed_points (data, &npoints, end,
+ &tuple);
+
+ if (!points)
+ return 1;
+
+ /* Shared point numbers are part of the data after the tuple
+ array. Thus, increment data_offset by tuple - data. `tuple'
+ here holds no relation to a pointer to the current part of
+ the tuple array that is being read later on. */
+ data_offset += tuple - data;
+ }
+ else
+ points = NULL;
+
+ /* Start reading each tuple. */
+ tuple = gvar->glyph_variation_data + offset + sizeof header;
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ coords = xmalloc (gvar->axis_count * sizeof *coords * 3);
+ else
+ coords = alloca (gvar->axis_count * sizeof *coords * 3);
+
+ intermediate_start = coords + gvar->axis_count;
+ intermediate_end = coords + gvar->axis_count;
+
+ while (ntuples--)
+ {
+ data = gvar->glyph_variation_data + offset + data_offset;
+
+ if (tuple + 3 >= end)
+ goto fail1;
+
+ memcpy (&data_size, tuple, sizeof data_size);
+ tuple += sizeof data_size;
+ memcpy (&index, tuple, sizeof index);
+ tuple += sizeof index;
+ sfnt_swap16 (&data_size);
+ sfnt_swap16 (&index);
+
+ /* Increment the offset to the data by the data size specified
+ here. */
+ data_offset += data_size;
+
+ if (index & 0x8000)
+ {
+ /* Embedded coordinates are present. Read each
+ coordinate and add it to the tuple. */
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&coords[j], tuple, sizeof *coords);
+ tuple += sizeof *coords;
+ sfnt_swap16 (&coords[j]);
+ }
+ }
+ else if ((index & 0xfff) > gvar->shared_coord_count)
+ /* index exceeds the number of shared tuples present. */
+ goto fail1;
+ else
+ /* index points into gvar->axis_count coordinates making up
+ the tuple. */
+ memcpy (coords, (gvar->global_coords
+ + ((index & 0xfff) * gvar->axis_count)),
+ gvar->axis_count * sizeof *coords);
+
+ /* Now read indeterminate tuples if required. */
+ if (index & 0x4000)
+ {
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&intermediate_start[j], tuple,
+ sizeof *intermediate_start);
+ tuple += sizeof *intermediate_start;
+ sfnt_swap16 (&intermediate_start[j]);
+ }
+
+ for (j = 0; j < gvar->axis_count; ++j)
+ {
+ if (tuple + 1 >= end)
+ goto fail1;
+
+ memcpy (&intermediate_end[j], tuple,
+ sizeof *intermediate_end);
+ tuple += sizeof *intermediate_end;
+ sfnt_swap16 (&intermediate_end[j]);
+ }
+ }
+
+ /* See whether or not the tuple applies to the current variation
+ configuration, and how much to scale them by. */
+
+ scale = sfnt_compute_tuple_scale (blend, index & 0x4000,
+ coords, intermediate_start,
+ intermediate_end);
+
+ if (!scale)
+ continue;
+
+ local_points = NULL;
+
+ /* Finally, read private point numbers.
+ Set local_points to those numbers; it will be freed
+ once the loop ends. */
+
+ if (index & 0x2000)
+ {
+ local_points = sfnt_read_packed_points (data, &n_local_points,
+ end, &data);
+ if (!local_points)
+ goto fail1;
+
+ point_count = n_local_points;
+ glyph_points = local_points;
+ }
+ else
+ {
+ /* If there are no private point numbers, use global
+ points. */
+ point_count = npoints;
+ glyph_points = points;
+ }
+
+ /* Now, read packed deltas. */
+
+ dx = NULL;
+ dy = NULL;
+
+ switch (point_count)
+ {
+ case UINT16_MAX:
+ /* Deltas are provided for all components in the glyph. */
+
+ /* Add 4 phantom points to each end. */
+ dx = sfnt_read_packed_deltas (data, end,
+ glyph->compound->num_components + 4,
+ &data);
+ dy = sfnt_read_packed_deltas (data, end,
+ glyph->compound->num_components + 4,
+ &data);
+
+ if (!dx || !dy)
+ goto fail3;
+
+ /* Apply each delta to the compound glyph. */
+
+ for (i = 0; i < glyph->compound->num_components; ++i)
+ {
+ component = &glyph->compound->components[i];
+
+ /* Check if the component uses deltas at all. */
+ if (!(component->flags & 02))
+ continue;
+
+ /* Vary the X offset. */
+
+ if (!(component->flags & 01))
+ word = component->argument1.b;
+ else
+ word = component->argument1.d;
+
+ fword = sfnt_mul_fixed_round (dx[i], scale);
+ component->argument1.d = word + fword;
+
+ /* Vary the Y offset. */
+
+ if (!(component->flags & 01))
+ word = component->argument2.b;
+ else
+ word = component->argument2.d;
+
+ fword = sfnt_mul_fixed_round (dy[i], scale);
+
+ /* Set the flag that says offsets are words. */
+ component->flags |= 01;
+ component->argument2.d = word + fword;
+ }
+
+ /* Apply the deltas for the two phantom points. */
+ distortion->origin += sfnt_mul_fixed_round (dx[i++], scale);
+ distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+ break;
+
+ default:
+ dx = sfnt_read_packed_deltas (data, end, point_count, &data);
+ dy = sfnt_read_packed_deltas (data, end, point_count, &data);
+
+ if (!dx || !dy)
+ goto fail3;
+
+ /* Deltas are only applied for each point number read. */
+
+ for (i = 0; i < point_count; ++i)
+ {
+ /* Apply deltas to phantom points. */
+
+ if (glyph_points[i] == glyph->compound->num_components)
+ {
+ distortion->origin += sfnt_mul_fixed_round (dx[i], scale);
+ continue;
+ }
+
+ if (glyph_points[i] == glyph->compound->num_components + 1)
+ {
+ distortion->advance += sfnt_mul_fixed_round (dx[i], scale);
+ continue;
+ }
+
+ /* Make sure the point doesn't end up out of bounds. */
+ if (glyph_points[i] >= glyph->compound->num_components)
+ continue;
+
+ component = &glyph->compound->components[glyph_points[i]];
+
+ /* Check if the component uses deltas at all. */
+ if (!(component->flags & 02))
+ continue;
+
+ /* Vary the X offset. */
+
+ if (!(component->flags & 01))
+ word = component->argument1.b;
+ else
+ word = component->argument1.d;
+
+ fword = sfnt_mul_fixed_round (dx[i], scale);
+ component->argument1.d = word + fword;
+
+ /* Vary the Y offset. */
+
+ if (!(component->flags & 01))
+ word = component->argument2.b;
+ else
+ word = component->argument2.d;
+
+ fword = sfnt_mul_fixed_round (dy[i], scale);
+
+ /* Set the flag that says offsets are words. */
+ component->flags |= 01;
+ component->argument2.d = word + fword;
+ }
+
+ break;
+ }
+
+ xfree (dx);
+ xfree (dy);
+
+ if (local_points != (uint16_t *) -1)
+ xfree (local_points);
+ }
+
+ /* Return success. */
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if (points != (uint16_t *) -1)
+ xfree (points);
+
+ /* Set the glyph metrics distortion as well. */
+ glyph->advance_distortion = distortion->advance;
+ glyph->origin_distortion = distortion->origin;
+
+ return 0;
+
+ fail3:
+ xfree (dx);
+ xfree (dy);
+ xfree (local_points);
+ fail1:
+
+ if (gvar->axis_count * sizeof *coords * 3 >= 1024 * 16)
+ xfree (coords);
+
+ if (points != (uint16_t *) -1)
+ xfree (points);
+
+ return 1;
+}
+
+/* Vary the specified INTERPRETER's control value table using the
+ variations in BLEND's CVT variations table, then record the blend's
+ normalized coordinates and axis count in the interpreter.
+
+ The CVT table used to create INTERPRETER must be the same used
+ to read BLEND->cvar. If not, behavior is undefined. */
+
+TEST_STATIC void
+sfnt_vary_interpreter (struct sfnt_interpreter *interpreter,
+ struct sfnt_blend *blend)
+{
+ sfnt_fixed scale;
+ int i;
+ struct sfnt_tuple_variation *variation;
+ size_t ndeltas, j, index;
+ sfnt_f26dot6 delta;
+
+ /* Return if there's no cvar table. */
+ if (!blend->cvar)
+ return;
+
+ /* For each tuple in the cvar table... */
+ for (i = 0; i < (blend->cvar->tuple_count & 0x0fff); ++i)
+ {
+ /* See if the tuple applies. */
+ variation = &blend->cvar->variation[i];
+ scale = sfnt_compute_tuple_scale (blend,
+ variation->intermediate_start != NULL,
+ variation->coordinates,
+ variation->intermediate_start,
+ variation->intermediate_end);
+ if (!scale)
+ continue;
+
+ /* Figure out how many deltas there are. If variation->points,
+ there are num_points deltas. Otherwise, there are
+ interpreter->cvt->num_elements deltas. */
+
+ ndeltas = (variation->points
+ ? variation->num_points
+ : interpreter->cvt_size);
+
+ for (j = 0; j < ndeltas; ++j)
+ {
+ /* Figure out which CVT entry this applies to. */
+ index = variation->points ? variation->points[j] : j;
+
+ if (index > interpreter->cvt_size)
+ continue;
+
+ /* Multiply the delta by the interpreter scale factor and
+ then the tuple scale factor. */
+ delta = sfnt_mul_f26dot6_fixed (variation->deltas[j] * 64,
+ interpreter->scale);
+ delta = sfnt_mul_fixed_round (delta, scale);
+
+ /* Apply the delta to the control value table. */
+ interpreter->cvt[i] += delta;
+ }
+ }
+
+ interpreter->n_axis = blend->fvar->axis_count;
+ interpreter->norm_coords = blend->norm_coords;
+}
+
+
+
+#ifdef TEST
+
+struct sfnt_test_dcontext
+{
+ /* Context for sfnt_test_get_glyph. */
+ struct sfnt_glyf_table *glyf;
+ struct sfnt_loca_table_short *loca_short;
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_blend *blend;
+};
+
+/* Global context for test functions. Height of glyph. */
+static sfnt_fixed sfnt_test_max;
+
+static void
+sfnt_test_move_to (struct sfnt_point point, void *dcontext)
+{
+ printf ("move_to: %g, %g\n", sfnt_coerce_fixed (point.x),
+ sfnt_coerce_fixed (point.y));
+}
+
+static void
+sfnt_test_line_to (struct sfnt_point point, void *dcontext)
+{
+ printf ("line_to: %g, %g\n", sfnt_coerce_fixed (point.x),
+ sfnt_coerce_fixed (point.y));
+}
+
+static void
+sfnt_test_curve_to (struct sfnt_point control,
+ struct sfnt_point endpoint,
+ void *dcontext)
+{
+ printf ("curve_to: %g, %g - %g, %g\n",
+ sfnt_coerce_fixed (control.x),
+ sfnt_coerce_fixed (control.y),
+ sfnt_coerce_fixed (endpoint.x),
+ sfnt_coerce_fixed (endpoint.y));
+}
+
+static struct sfnt_glyph *
+sfnt_test_get_glyph (sfnt_glyph id, void *dcontext,
+ bool *need_free)
+{
+ struct sfnt_test_dcontext *tables;
+ struct sfnt_glyph *glyph;
+ struct sfnt_metrics_distortion distortion;
+
+ tables = dcontext;
+ *need_free = true;
+
+ glyph = sfnt_read_glyph (id, tables->glyf,
+ tables->loca_short,
+ tables->loca_long);
+
+ if (tables->blend && glyph)
+ {
+ if (glyph->simple)
+ sfnt_vary_simple_glyph (tables->blend, id, glyph,
+ &distortion);
+ else
+ sfnt_vary_compound_glyph (tables->blend, id, glyph,
+ &distortion);
+ }
+
+ return glyph;
+}
+
+static void
+sfnt_test_free_glyph (struct sfnt_glyph *glyph, void *dcontext)
+{
+ sfnt_free_glyph (glyph);
+}
+
+static void
+sfnt_test_span (struct sfnt_edge *edge, sfnt_fixed y,
+ void *dcontext)
+{
+#if 1
+ printf ("/* span at %g */\n", sfnt_coerce_fixed (y));
+ for (; edge; edge = edge->next)
+ {
+ if (y >= edge->bottom && y < edge->top)
+ printf ("ctx.fillRect (%g, %g, 1, 1); "
+ "/* %g top: %g bot: %g stepx: %g winding: %d */\n",
+ sfnt_coerce_fixed (edge->x),
+ sfnt_coerce_fixed (sfnt_test_max - y),
+ sfnt_coerce_fixed (y),
+ sfnt_coerce_fixed (edge->top),
+ sfnt_coerce_fixed (edge->bottom),
+ sfnt_coerce_fixed (edge->step_x),
+ edge->winding);
+ else
+ printf ("STRIPPED BAD SPAN!!! %g %g %"PRIi32
+ " %"PRIi32" (winding: %d)\n",
+ sfnt_coerce_fixed (edge->top),
+ sfnt_coerce_fixed (edge->bottom),
+ edge->top, y, edge->winding);
+ }
+#elif 0
+ int winding;
+ short x, dx;
+
+ winding = 0;
+ x = 0;
+
+ for (; edge; edge = edge->next)
+ {
+ dx = (edge->x >> 16) - x;
+ x = edge->x >> 16;
+
+ for (; dx > 0; --dx)
+ putc (winding ? '.' : ' ', stdout);
+
+ winding = !winding;
+ }
+
+ putc ('\n', stdout);
+#elif 0
+ for (; edge; edge = edge->next)
+ printf ("%g-", sfnt_coerce_fixed (edge->x));
+ puts ("");
+#endif
+}
+
+static void
+sfnt_test_edge_ignore (struct sfnt_edge *edges, size_t num_edges,
+ void *dcontext)
+{
+
+}
+
+/* The same debugger stuff is used here. */
+static void sfnt_setup_debugger (void);
+
+/* The debugger's X display. */
+static Display *display;
+
+/* The debugger window. */
+static Window window;
+
+/* The GC. */
+static GC point_gc, background_gc;
+
+static void
+sfnt_test_edges (struct sfnt_edge *edges, size_t num_edges)
+{
+ static sfnt_fixed y;
+ size_t i;
+
+ for (i = 0; i < num_edges; ++i)
+ {
+ if (y >= edges[i].bottom && y < edges[i].top)
+ {
+ XDrawPoint (display, window, point_gc,
+ edges[i].x / 65536, 100 - (y / 65536));
+ printf ("sfnt_test_edges: %d %d\n",
+ edges[i].x / 65536, 100 - (y / 65536));
+ }
+ }
+
+ y += SFNT_POLY_STEP;
+
+ for (i = 0; i < num_edges; ++i)
+ sfnt_step_edge (&edges[i]);
+}
+
+static void
+sfnt_debug_edges (struct sfnt_edge *edges, size_t num_edges)
+{
+ XEvent event;
+
+ sfnt_setup_debugger ();
+
+ while (true)
+ {
+ XNextEvent (display, &event);
+
+ switch (event.type)
+ {
+ case KeyPress:
+ XDestroyWindow (display, window);
+ XCloseDisplay (display);
+ exit (0);
+ break;
+
+ case Expose:
+
+ while (true)
+ {
+ sfnt_test_edges (edges, num_edges);
+ XFlush (display);
+ usleep (50000);
+ }
+
+ break;
+ }
+ }
+}
+
+static void
+sfnt_test_edge (struct sfnt_edge *edges, size_t num_edges,
+ void *dcontext)
+{
+ size_t i;
+
+ printf ("built %zu edges\n", num_edges);
+
+ for (i = 0; i < num_edges; ++i)
+ {
+ printf ("/* edge x, top, bot: %g, %g - %g. winding: %d */\n"
+ "/* edge step_x: %g */\n",
+ sfnt_coerce_fixed (edges[i].x),
+ sfnt_coerce_fixed (edges[i].top),
+ sfnt_coerce_fixed (edges[i].bottom),
+ edges[i].winding,
+ sfnt_coerce_fixed (edges[i].step_x));
+#ifdef TEST_VERTEX
+ printf ("ctx.fillRect (%g, %g, 1, 1);\n",
+ sfnt_coerce_fixed (edges[i].x),
+ sfnt_coerce_fixed (sfnt_test_max
+ - edges[i].y));
+#else
+ printf ("ctx.fillRect (%g, %g, 1, 1);\n",
+ sfnt_coerce_fixed (edges[i].x),
+ sfnt_coerce_fixed (sfnt_test_max
+ - edges[i].bottom));
+#endif
+ }
+
+ if (getenv ("SFNT_DEBUG_STEP"))
+ {
+ if (!fork ())
+ sfnt_debug_edges (edges, num_edges);
+ }
+
+ printf ("==end of edges==\n");
+
+ sfnt_poly_edges (edges, num_edges, sfnt_test_span, NULL);
+}
+
+static void
+sfnt_x_raster (struct sfnt_raster **rasters,
+ int *advances,
+ int nrasters,
+ struct sfnt_hhea_table *hhea,
+ sfnt_fixed scale)
+{
+ Display *display;
+ Window window;
+ Pixmap *pixmaps;
+ Picture *glyphs, drawable, solid;
+ int event_base, error_base;
+ int major, minor, *depths, count;
+ XRenderPictFormat *format, *glyph_format;
+ Visual *visual;
+ XImage image;
+ GC gc;
+ XGCValues gcvalues;
+ XEvent event;
+ XRenderColor white, black;
+ int i, ascent, origin, x, y;
+ Font font;
+
+ if (!nrasters)
+ exit (0);
+
+ display = XOpenDisplay (NULL);
+
+ if (!display)
+ exit (0);
+
+ if (!XRenderQueryExtension (display, &event_base, &error_base)
+ || !XRenderQueryVersion (display, &major, &minor))
+ exit (0);
+
+ if (major == 0 && minor < 10)
+ exit (0);
+
+ window = XCreateSimpleWindow (display, DefaultRootWindow (display),
+ 0, 0, 100, 150, 0, 0,
+ WhitePixel (display,
+ DefaultScreen (display)));
+ XSelectInput (display, window, ExposureMask);
+ XMapWindow (display, window);
+
+ visual = DefaultVisual (display, DefaultScreen (display));
+ format = XRenderFindVisualFormat (display, visual);
+
+ if (!format)
+ exit (0);
+
+ glyph_format = XRenderFindStandardFormat (display, PictStandardA8);
+ depths = XListDepths (display, DefaultScreen (display), &count);
+
+ for (i = 0; i < count; ++i)
+ {
+ if (depths[i] == 8)
+ goto depth_found;
+ }
+
+ exit (0);
+
+ depth_found:
+
+ XFree (depths);
+ pixmaps = alloca (sizeof *pixmaps * nrasters);
+ glyphs = alloca (sizeof *glyphs * nrasters);
+ gc = None;
+
+ for (i = 0; i < nrasters; ++i)
+ {
+ pixmaps[i] = XCreatePixmap (display, DefaultRootWindow (display),
+ rasters[i]->width, rasters[i]->height, 8);
+ if (!gc)
+ gc = XCreateGC (display, pixmaps[i], 0, &gcvalues);
+
+ /* Upload the raster. */
+ image.width = rasters[i]->width;
+ image.height = rasters[i]->height;
+ image.xoffset = 0;
+ image.format = ZPixmap;
+ image.data = (char *) rasters[i]->cells;
+ image.byte_order = MSBFirst;
+ image.bitmap_unit = 8;
+ image.bitmap_bit_order = LSBFirst;
+ image.bitmap_pad = SFNT_POLY_ALIGNMENT * 8;
+ image.depth = 8;
+ image.bytes_per_line = rasters[i]->stride;
+ image.bits_per_pixel = 8;
+ image.red_mask = 0;
+ image.green_mask = 0;
+ image.blue_mask = 0;
+
+ if (!XInitImage (&image))
+ abort ();
+
+ XPutImage (display, pixmaps[i], gc, &image,
+ 0, 0, 0, 0, image.width, image.height);
+
+ glyphs[i] = XRenderCreatePicture (display, pixmaps[i],
+ glyph_format, 0, NULL);
+ }
+
+ XFreeGC (display, gc);
+
+ font = XLoadFont (display, "6x13");
+
+ if (!font)
+ exit (1);
+
+ gcvalues.font = font;
+ gcvalues.foreground = BlackPixel (display, DefaultScreen (display));
+ gc = XCreateGC (display, window, GCForeground | GCFont, &gcvalues);
+
+ drawable = XRenderCreatePicture (display, window, format,
+ 0, NULL);
+ memset (&black, 0, sizeof black);
+ black.alpha = 65535;
+
+ solid = XRenderCreateSolidFill (display, &black);
+
+ while (true)
+ {
+ XNextEvent (display, &event);
+
+ if (event.type == Expose)
+ {
+ white.red = 65535;
+ white.green = 65535;
+ white.blue = 65535;
+ white.alpha = 65535;
+
+ /* Clear the background. */
+ XRenderFillRectangle (display, PictOpSrc, drawable,
+ &white, 0, 0, 65535, 65535);
+
+ /* Compute ascent line. */
+ ascent = sfnt_mul_fixed (hhea->ascent * 65536,
+ scale) / 65536;
+
+ origin = 0;
+
+ for (i = 0; i < nrasters; ++i)
+ {
+ /* Compute the base position. */
+ x = origin + rasters[i]->offx;
+ y = ascent - rasters[i]->height - rasters[i]->offy;
+
+ /* Draw the solid fill with the glyph as clip mask. */
+ XRenderComposite (display, PictOpOver, solid, glyphs[i],
+ drawable, 0, 0, 0, 0, x, y,
+ rasters[i]->width, rasters[i]->height);
+
+ origin += advances[i];
+ }
+ }
+ }
+}
+
+static void
+sfnt_test_raster (struct sfnt_raster *raster,
+ struct sfnt_hhea_table *hhea,
+ sfnt_fixed scale)
+{
+ int x, y, i;
+
+ for (y = 0; y < raster->height; ++y)
+ {
+ for (x = 0; x < raster->width; ++x)
+ printf ("%3d ", (int) raster->cells[y * raster->stride + x]);
+ puts ("");
+ }
+
+ if (hhea && getenv ("SFNT_X"))
+ {
+ i = 0;
+
+ if (!fork ())
+ sfnt_x_raster (&raster, &i, 1, hhea, scale);
+ }
+}
+
+
+
+/* Instruction execution tests. */
+
+static struct sfnt_maxp_table test_interpreter_profile =
+ {
+ 0x00010000,
+ 650,
+ 100,
+ 100,
+ 100,
+ 100,
+ 2,
+ 100,
+ 255,
+ 12,
+ 12,
+ 100,
+ 5000,
+ 100,
+ 1,
+ };
+
+static sfnt_fword test_cvt_values[] =
+ {
+ 100, 100, -100, -100, 50, 50, 50, 50, 0, 0,
+ };
+
+static struct sfnt_cvt_table test_interpreter_cvt =
+ {
+ 10,
+ test_cvt_values,
+ };
+
+static struct sfnt_head_table test_interpreter_head =
+ {
+ 0x00010000,
+ 0x00010000,
+ 0,
+ 0x5f0f3cf5,
+ 0,
+ 800,
+ 0,
+ 0,
+ 0,
+ 0,
+ -312,
+ -555,
+ 1315,
+ 2163,
+ 0,
+ 12,
+ 0,
+ 0,
+ 0,
+ };
+
+static struct sfnt_interpreter *
+sfnt_make_test_interpreter (void)
+{
+ return sfnt_make_interpreter (&test_interpreter_profile,
+ &test_interpreter_cvt,
+ &test_interpreter_head,
+ NULL, 17, 17);
+}
+
+struct sfnt_interpreter_test
+{
+ const char *name;
+ unsigned char *instructions;
+ int num_instructions;
+ void *arg;
+ void (*check) (struct sfnt_interpreter *, void *, bool);
+};
+
+static void
+sfnt_run_interpreter_test (struct sfnt_interpreter_test *test,
+ struct sfnt_interpreter *interpreter)
+{
+ fprintf (stderr, "Testing %s: ", test->name);
+
+ if (setjmp (interpreter->trap))
+ test->check (interpreter, test->arg, true);
+ else
+ {
+ interpreter->IP = 0;
+ interpreter->SP = interpreter->stack;
+ interpreter->instructions = test->instructions;
+ interpreter->num_instructions = test->num_instructions;
+
+ sfnt_interpret_run (interpreter, SFNT_RUN_CONTEXT_TEST);
+ test->check (interpreter, test->arg, false);
+ }
+}
+
+struct sfnt_generic_test_args
+{
+ uint32_t *expected_stack;
+ int expected_stack_elements;
+ bool expected_trap;
+ int expected_IP;
+};
+
+static void
+sfnt_generic_check (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ struct sfnt_generic_test_args *args;
+ int i;
+
+ args = arg;
+
+ if (((interpreter->SP - interpreter->stack)
+ != args->expected_stack_elements))
+ {
+ fprintf (stderr,
+ "failed at IP %d:%d (expected %d stack elements,"
+ " got %td); last trap string: %s\n",
+ interpreter->call_depth, interpreter->IP,
+ args->expected_stack_elements,
+ interpreter->SP - interpreter->stack,
+ ((trap && interpreter->trap_reason)
+ ? interpreter->trap_reason
+ : "NULL"));
+
+ for (i = 0; i < interpreter->SP - interpreter->stack; ++i)
+ fprintf (stderr, "%8d ", (int) interpreter->stack[i]);
+ fprintf (stderr, "\n");
+ return;
+ }
+
+ if (memcmp (interpreter->stack, args->expected_stack,
+ ((char *) interpreter->SP
+ - (char *) interpreter->stack)))
+ {
+ fprintf (stderr, "failed (inconsistent stack elements)\n"
+ "machine stack ------------------------->\n");
+
+ for (i = 0; i < args->expected_stack_elements; ++i)
+ fprintf (stderr, "%8d ", (int) interpreter->stack[i]);
+
+ fprintf (stderr,
+ "\nexpected stack ------------------------>\n");
+
+ for (i = 0; i < args->expected_stack_elements; ++i)
+ fprintf (stderr, "%8d ", (int) args->expected_stack[i]);
+
+ fprintf (stderr, "\n");
+ return;
+ }
+
+ if (args->expected_IP != -1
+ && interpreter->IP != args->expected_IP)
+ {
+ fprintf (stderr, "failed (IP is %d, not %d)\n",
+ interpreter->IP, args->expected_IP);
+ return;
+ }
+
+ if (trap)
+ {
+ if (args->expected_trap)
+ fprintf (stderr, "passed (with trap %s)\n",
+ interpreter->trap_reason);
+ else
+ fprintf (stderr, "failed (unexpected trap %s)\n",
+ interpreter->trap_reason);
+
+ return;
+ }
+
+ if (args->expected_trap)
+ fprintf (stderr, "failed, trap not encountered\n");
+ else
+ fprintf (stderr, "passed\n");
+
+ return;
+}
+
+static void
+sfnt_check_srp0 (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed (unexpected trap %s)\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->state.rp0 != 0)
+ {
+ fprintf (stderr, "failed, rp0 is not 0, but %d\n",
+ interpreter->state.rp0);
+ return;
+ }
+
+ if (interpreter->state.rp1 != 1)
+ {
+ fprintf (stderr, "failed, rp1 is not 1, but %d\n",
+ interpreter->state.rp1);
+ return;
+ }
+
+ if (interpreter->state.rp2 != 2)
+ {
+ fprintf (stderr, "failed, rp2 is not 2, but %d\n",
+ interpreter->state.rp2);
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, stack not empty\n");
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_szp0 (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (!trap)
+ {
+ fprintf (stderr, "failed, expected trap\n");
+ return;
+ }
+
+ if (interpreter->state.zp0 != 1
+ || interpreter->state.zp1 != 1
+ || interpreter->state.zp2 != 0)
+ {
+ fprintf (stderr,
+ "failed, unexpected values of zone pointers: %d %d %d\n",
+ interpreter->state.zp0, interpreter->state.zp1,
+ interpreter->state.zp2);
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, stack not empty\n");
+ return;
+ }
+
+ fprintf (stderr, "passed with expected trap %s\n",
+ interpreter->trap_reason);
+ return;
+}
+
+static void
+sfnt_check_sloop (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (interpreter->state.loop != 1)
+ {
+ /* The trap should've restored GS->loop to 1. */
+ fprintf (stderr, "failed, GS->loop should be 1, not %d\n",
+ interpreter->state.loop);
+ return;
+ }
+
+ if (!trap)
+ {
+ fprintf (stderr, "failed, expected trap\n");
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, stack not empty\n");
+ return;
+ }
+
+ fprintf (stderr, "passed with expected trap %s\n",
+ interpreter->trap_reason);
+ return;
+}
+
+struct sfnt_rounding_test_args
+{
+ sfnt_f26dot6 value;
+};
+
+static void
+sfnt_check_rounding (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ sfnt_f26dot6 value;
+ struct sfnt_rounding_test_args *args;
+
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap: %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->SP == interpreter->stack)
+ {
+ fprintf (stderr, "failed, empty stack\n");
+ return;
+ }
+
+ value = *(interpreter->SP - 1);
+ args = arg;
+
+ if (value != args->value)
+ {
+ fprintf (stderr, "failed. value is: %d %d, but wanted: %d %d\n",
+ value >> 6, value & 63, args->value >> 6,
+ args->value & 63);
+ return;
+ }
+
+ fprintf (stderr, "passed, expected value %d\n", value);
+ return;
+}
+
+static void
+sfnt_check_smd (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.minimum_distance != 32)
+ {
+ fprintf (stderr, "failed, expected minimum distance"
+ " of 32, got %d\n",
+ interpreter->state.minimum_distance);
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_scvtci (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.cvt_cut_in != 128)
+ {
+ fprintf (stderr, "failed, expected 128, got %d\n",
+ interpreter->state.cvt_cut_in);
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_sswci (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.sw_cut_in != 512)
+ {
+ fprintf (stderr, "failed, expected 512, got %d\n",
+ interpreter->state.sw_cut_in);
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_ssw (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.single_width_value
+ != sfnt_mul_f26dot6_fixed (-64, interpreter->scale))
+ {
+ fprintf (stderr, "failed, got %d at scale %d,"
+ " expected %d\n",
+ interpreter->state.single_width_value,
+ interpreter->scale,
+ sfnt_mul_f26dot6_fixed (-64, interpreter->scale));
+ return;
+ }
+
+ fprintf (stderr, "passed\n");
+ return;
+}
+
+static void
+sfnt_check_flipon (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (!interpreter->state.auto_flip)
+ fprintf (stderr, "failed, auto flip not enabled\n");
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_flipoff (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap\n");
+ return;
+ }
+
+ if (interpreter->state.auto_flip)
+ fprintf (stderr, "failed, auto flip not disabled\n");
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_sdb (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->state.delta_base != 8)
+ fprintf (stderr, "failed, delta base is %d, not 8\n",
+ interpreter->state.delta_base);
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_sds (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->state.delta_shift != 1)
+ fprintf (stderr, "failed, delta shift is %d, not 1\n",
+ interpreter->state.delta_shift);
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_scanctrl (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, expected empty stack\n");
+ return;
+ }
+
+ if (interpreter->state.scan_control != 1)
+ fprintf (stderr, "failed, scan control is %d, not 1\n",
+ interpreter->state.scan_control);
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static void
+sfnt_check_instctrl (struct sfnt_interpreter *interpreter,
+ void *arg, bool trap)
+{
+ if (trap)
+ {
+ fprintf (stderr, "failed, unexpected trap %s\n",
+ interpreter->trap_reason);
+ return;
+ }
+
+ if (interpreter->SP != interpreter->stack)
+ {
+ fprintf (stderr, "failed, expected empty stack\n");
+ return;
+ }
+
+ if (interpreter->state.instruct_control != 2)
+ fprintf (stderr, "failed, inst control is %d, not 2\n",
+ interpreter->state.instruct_control);
+ else
+ fprintf (stderr, "pass\n");
+
+ return;
+}
+
+static struct sfnt_generic_test_args npushb_test_args =
+ {
+ (uint32_t []) { 1U, 2U, 3U, 4U, },
+ 4,
+ true,
+ 6,
+ };
+
+static struct sfnt_generic_test_args npushw_test_args =
+ {
+ (uint32_t []) { 0x101U, 0x202U, 0x303U, 0x404U, },
+ 4,
+ true,
+ 10,
+ };
+
+static struct sfnt_generic_test_args pushb_test_args =
+ {
+ (uint32_t []) { 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U,
+ 1U, },
+ 9,
+ true,
+ 11,
+ };
+
+static struct sfnt_generic_test_args pushw_test_args =
+ {
+ (uint32_t []) { 0x203U, 0x204U, 0x205U, 0x206U, 0x207U, 0x208U,
+ 0x909U, 0x909U, (uint32_t) -1, },
+ 9,
+ true,
+ 20,
+ };
+
+static struct sfnt_generic_test_args stack_overflow_test_args =
+ {
+ (uint32_t[100]) { },
+ 100,
+ true,
+ 0,
+ };
+
+static struct sfnt_generic_test_args stack_underflow_test_args =
+ {
+ /* GCC BUG, this should be []! */
+ (uint32_t []) { },
+ 0,
+ true,
+ 4,
+ };
+
+static struct sfnt_rounding_test_args rtg_test_args =
+ {
+ 64,
+ };
+
+static struct sfnt_rounding_test_args rtg_symmetric_test_args =
+ {
+ -64,
+ };
+
+static struct sfnt_rounding_test_args rtg_1_test_args =
+ {
+ 0,
+ };
+
+static struct sfnt_rounding_test_args rtg_1_symmetric_test_args =
+ {
+ 0,
+ };
+
+static struct sfnt_rounding_test_args rthg_test_args =
+ {
+ 32,
+ };
+
+static struct sfnt_rounding_test_args rthg_1_test_args =
+ {
+ 96,
+ };
+
+static struct sfnt_rounding_test_args rtdg_test_args =
+ {
+ 32,
+ };
+
+static struct sfnt_rounding_test_args rtdg_1_test_args =
+ {
+ 0,
+ };
+
+static struct sfnt_rounding_test_args rtdg_2_test_args =
+ {
+ 32,
+ };
+
+static struct sfnt_rounding_test_args rtdg_3_test_args =
+ {
+ 64,
+ };
+
+static struct sfnt_generic_test_args else_test_args =
+ {
+ (uint32_t []) { 77U, 90U, 83U, },
+ 3,
+ false,
+ 40,
+ };
+
+static struct sfnt_generic_test_args jmpr_test_args =
+ {
+ /* What ends up on the stack?
+
+ First, there are the three words that the first PUSHW[2]
+ instruction has pushed:
+
+ 0, 0xb2, -3
+
+ After those three words are pushed, JMPR[] is called, and pops an
+ offset:
+
+ -3
+
+ so now the stack is:
+
+ 0, 0xb2
+
+ as a result of the relative jump, IP is now at the least
+ significant byte of the word inside what was originally a
+ PUSHW[2] instruction, 0xb2, which itself is PUSHB[2]!
+
+ As a result of that instruction, three more bytes, including
+ JMPR[] itself are pushed onto the stack, making it:
+
+ 0, 0xb2, 255, 253, 0x1c
+
+ Then, execution continues as usual. 4 is pushed on to the
+ stack, making it:
+
+ 0, 0xb2, 255, 253, 0x1c, 4
+
+ Another JMPR[] pops:
+
+ 4
+
+ making the stack:
+
+ 0, 0xb2, 255, 253, 0x1c
+
+ And skips the next three padding bytes, finally reaching a
+ PUSHW[0] instruction which pushes -30 onto the stack:
+
+ 0, 0xb2, 255, 253, 0x1c, -30
+
+ and a JMPR[] instruction, which pops:
+
+ -30
+
+ making:
+
+ 0, 0xb2, 255, 253,
+
+ and subsequently traps, as -30 would underflow the instruction
+ stream. */
+ (uint32_t []) { 0, 0xb2, 255, 253, 0x1c, },
+ 5,
+ true,
+ 17,
+ };
+
+static struct sfnt_generic_test_args dup_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 5,
+ };
+
+static struct sfnt_generic_test_args pop_test_args =
+ {
+ (uint32_t []) { 70, 70, },
+ 2,
+ false,
+ 5,
+ };
+
+static struct sfnt_generic_test_args clear_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args swap_test_args =
+ {
+ (uint32_t []) { 2, 1, },
+ 2,
+ false,
+ 4,
+ };
+
+static struct sfnt_generic_test_args depth_test_args =
+ {
+ (uint32_t []) { 3, 3, 3, 3, },
+ 4,
+ false,
+ 5,
+ };
+
+static struct sfnt_generic_test_args cindex_test_args =
+ {
+ (uint32_t []) { 0, 3, 3, 4, 0, },
+ 5,
+ true,
+ 10,
+ };
+
+static struct sfnt_generic_test_args mindex_test_args =
+ {
+ (uint32_t []) { 0, 3, 7, 4, 4, },
+ 5,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args raw_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 0,
+ };
+
+static struct sfnt_generic_test_args loopcall_test_args =
+ {
+ (uint32_t []) { 10, },
+ 1,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args call_test_args =
+ {
+ (uint32_t []) { 11, },
+ 1,
+ true,
+ 2,
+ };
+
+static struct sfnt_generic_test_args fdef_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 4,
+ };
+
+static struct sfnt_generic_test_args fdef_1_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 9,
+ };
+
+static struct sfnt_generic_test_args endf_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 0,
+ };
+
+static struct sfnt_generic_test_args ws_test_args =
+ {
+ (uint32_t []) { 40, },
+ 1,
+ true,
+ 10,
+ };
+
+static struct sfnt_generic_test_args rs_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 2,
+ };
+
+static struct sfnt_generic_test_args wcvtp_test_args =
+ {
+ (uint32_t []) { 32, },
+ 1,
+ true,
+ 10,
+ };
+
+static struct sfnt_generic_test_args rcvt_test_args =
+ {
+ (uint32_t []) { 136, },
+ 1,
+ true,
+ 5,
+ };
+
+static struct sfnt_generic_test_args mppem_test_args =
+ {
+ (uint32_t []) { 17, },
+ 1,
+ false,
+ 1,
+ };
+
+static struct sfnt_generic_test_args mps_test_args =
+ {
+ (uint32_t []) { 17, },
+ 1,
+ false,
+ 1,
+ };
+
+static struct sfnt_generic_test_args debug_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args lt_test_args =
+ {
+ (uint32_t []) { 1, 0, 0, },
+ 3,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args lteq_test_args =
+ {
+ (uint32_t []) { 1, 0, 1, },
+ 3,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args gt_test_args =
+ {
+ (uint32_t []) { 0, 1, 0, },
+ 3,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args gteq_test_args =
+ {
+ (uint32_t []) { 0, 1, 1, },
+ 3,
+ false,
+ 12,
+ };
+
+static struct sfnt_generic_test_args eq_test_args =
+ {
+ (uint32_t []) { 0, 1, 0, },
+ 3,
+ false,
+ 18,
+ };
+
+static struct sfnt_generic_test_args neq_test_args =
+ {
+ (uint32_t []) { 1, 0, 1, },
+ 3,
+ false,
+ 18,
+ };
+
+static struct sfnt_generic_test_args odd_test_args =
+ {
+ (uint32_t []) { 1, 0, },
+ 2,
+ false,
+ 9,
+ };
+
+static struct sfnt_generic_test_args even_test_args =
+ {
+ (uint32_t []) { 0, 1, },
+ 2,
+ false,
+ 9,
+ };
+
+static struct sfnt_generic_test_args if_test_args =
+ {
+ (uint32_t []) { 17, 24, 1, 2, 3, 4, 5, -1, -1,
+ 88, 1, 3, },
+ 12,
+ false,
+ 185,
+ };
+
+static struct sfnt_generic_test_args eif_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args and_test_args =
+ {
+ (uint32_t []) { 0, 0, 1, 0, },
+ 4,
+ false,
+ 16,
+ };
+
+static struct sfnt_generic_test_args or_test_args =
+ {
+ (uint32_t []) { 1, 1, 1, 0, },
+ 4,
+ false,
+ 16,
+ };
+
+static struct sfnt_generic_test_args not_test_args =
+ {
+ (uint32_t []) { 0, 1, },
+ 2,
+ false,
+ 6,
+ };
+
+static struct sfnt_generic_test_args sds_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 5,
+ };
+
+static struct sfnt_generic_test_args add_test_args =
+ {
+ (uint32_t []) { 96, -1, },
+ 2,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args sub_test_args =
+ {
+ (uint32_t []) { 64, -64, 431, },
+ 3,
+ false,
+ 14,
+ };
+
+static struct sfnt_generic_test_args div_test_args =
+ {
+ (uint32_t []) { 32, -64, },
+ 2,
+ true,
+ 15,
+ };
+
+static struct sfnt_generic_test_args mul_test_args =
+ {
+ (uint32_t []) { 255, -255, 255, },
+ 3,
+ false,
+ 16,
+ };
+
+static struct sfnt_generic_test_args abs_test_args =
+ {
+ (uint32_t []) { 1, 1, },
+ 2,
+ false,
+ 7,
+ };
+
+static struct sfnt_generic_test_args neg_test_args =
+ {
+ (uint32_t []) { 1, -1, },
+ 2,
+ false,
+ 7,
+ };
+
+static struct sfnt_generic_test_args floor_test_args =
+ {
+ (uint32_t []) { -128, -64, 0, 64, 128, },
+ 5,
+ false,
+ 17,
+ };
+
+static struct sfnt_generic_test_args ceiling_test_args =
+ {
+ (uint32_t []) { -128, -128, -64, 0, 64, 128, 128, },
+ 7,
+ false,
+ 25,
+ };
+
+static struct sfnt_generic_test_args round_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ true,
+ 0,
+ };
+
+static struct sfnt_generic_test_args nround_test_args =
+ {
+ (uint32_t []) { 63, },
+ 1,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args wcvtf_test_args =
+ {
+ (uint32_t []) { (63 * 17 * 65535 / 800) >> 10, },
+ 1,
+ false,
+ 7,
+ };
+
+static struct sfnt_generic_test_args jrot_test_args =
+ {
+ (uint32_t []) { 40, 40, },
+ 2,
+ false,
+ 13,
+ };
+
+static struct sfnt_generic_test_args jrof_test_args =
+ {
+ (uint32_t []) { 4, },
+ 1,
+ false,
+ 13,
+ };
+
+static struct sfnt_generic_test_args deltac1_test_args =
+ {
+ (uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
+ ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
+ 2,
+ false,
+ 22,
+ };
+
+static struct sfnt_generic_test_args deltac2_test_args =
+ {
+ (uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
+ ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
+ 2,
+ false,
+ 22,
+ };
+
+static struct sfnt_generic_test_args deltac3_test_args =
+ {
+ (uint32_t []) { ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8,
+ ((((50 * 17 * 65535) + 32767) / 800) >> 10) + 8, },
+ 2,
+ false,
+ 22,
+ };
+
+/* Macros and instructions for detailed rounding tests. */
+
+/* PUSHB[0] period:phase:threshold
+ SROUND[] */
+#define SFNT_ROUNDING_OPERAND(period, phase, threshold) \
+ 0xb0, (((unsigned char) period << 6) \
+ | ((unsigned char) phase & 3) << 4 \
+ | ((unsigned char) threshold & 15)), 0x76
+
+/* PUSHB[0] period:phase:threshold
+ S45ROUND[] */
+#define SFNT_ROUNDING_OPERAND_45(period, phase, threshold) \
+ 0xb0, (((unsigned char) period << 6) \
+ | ((unsigned char) phase & 3) << 4 \
+ | ((unsigned char) threshold & 15)), 0x77
+
+/* PUSHB[0] value
+ ROUND[] */
+#define SFNT_ROUND_VALUE(value) 0xb0, value, 0x68
+
+static unsigned char sfnt_sround_instructions[] =
+ {
+ SFNT_ROUNDING_OPERAND (0, 0, 8),
+ SFNT_ROUND_VALUE (15),
+ SFNT_ROUND_VALUE (17),
+ SFNT_ROUNDING_OPERAND (1, 0, 8),
+ SFNT_ROUND_VALUE (32),
+ SFNT_ROUND_VALUE (16),
+ SFNT_ROUNDING_OPERAND (2, 0, 8),
+ SFNT_ROUND_VALUE (64),
+ SFNT_ROUND_VALUE (63),
+ SFNT_ROUNDING_OPERAND (0, 1, 8),
+ SFNT_ROUND_VALUE (16),
+ SFNT_ROUND_VALUE (24),
+ SFNT_ROUNDING_OPERAND (0, 2, 8),
+ SFNT_ROUND_VALUE (20),
+ SFNT_ROUND_VALUE (48),
+ SFNT_ROUNDING_OPERAND (0, 3, 8),
+ SFNT_ROUND_VALUE (7),
+ SFNT_ROUND_VALUE (70),
+ };
+
+static uint32_t sfnt_sround_values[] =
+ {
+ /* 0, 0, 8 = RTDG; 15 rounded to the double grid and becomes 0, 17
+ is 32. */
+ 0, 32,
+ /* 1, 0, 8 = RTG; 32 rounded to the grid is 64, 16 is 0. */
+ 64, 0,
+ /* 2, 0, 8 = round to a grid separated by 128s. 64 is 128, 63 is
+ 0. */
+ 128, 0,
+ /* 0, 1, 8 = round to a double grid with a phase of 8. 16 rounds
+ down to 8, 24 rounds up to 40. */
+ 8, 40,
+ /* 0, 2, 8 = round to a double grid with a phase of 16. 20 rounds
+ down to 16, 40 rounds up to 48. */
+ 16, 48,
+ /* 0, 3, 8 = round to a double grid with a phase of 48. 7 rounds
+ up to 16, 70 rounds up to 80. */
+ 16, 80,
+ };
+
+static struct sfnt_generic_test_args sround_test_args =
+ {
+ sfnt_sround_values,
+ ARRAYELTS (sfnt_sround_values),
+ false,
+ ARRAYELTS (sfnt_sround_instructions),
+ };
+
+static unsigned char sfnt_s45round_instructions[] =
+ {
+ SFNT_ROUNDING_OPERAND_45 (0, 0, 0),
+ SFNT_ROUND_VALUE (1),
+ SFNT_ROUND_VALUE (45),
+ };
+
+static uint32_t sfnt_s45round_values[] =
+ {
+ /* 0, 0, 0: 1 rounded to the double cubic grid becomes 45, and 46
+ rounded to the double cubic grid becomes 90. */
+ 45, 90,
+ };
+
+static struct sfnt_generic_test_args s45round_test_args =
+ {
+ sfnt_s45round_values,
+ ARRAYELTS (sfnt_s45round_values),
+ false,
+ ARRAYELTS (sfnt_s45round_instructions),
+ };
+
+static struct sfnt_generic_test_args rutg_test_args =
+ {
+ (uint32_t []) { 64, 64, 0, },
+ 3,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args rdtg_test_args =
+ {
+ (uint32_t []) { 0, 0, 64, },
+ 3,
+ false,
+ 10,
+ };
+
+static struct sfnt_generic_test_args sangw_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args aa_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_generic_test_args getinfo_test_args =
+ {
+ /* Pretend to be the Macintosh System 7 scaler.
+
+ This lets the interpreter get away with only two phantom
+ points, as specified in Apple's TrueType reference manual. */
+ (uint32_t []) { 2, 0, },
+ 2,
+ false,
+ 6,
+ };
+
+static struct sfnt_generic_test_args idef_test_args =
+ {
+ (uint32_t []) { 1, 2, 3, },
+ 3,
+ false,
+ 11,
+ };
+
+static struct sfnt_generic_test_args roll_test_args =
+ {
+ (uint32_t []) { 1, 2, 4, 5, 3, },
+ 5,
+ false,
+ 7,
+ };
+
+static struct sfnt_generic_test_args roll_1_test_args =
+ {
+ (uint32_t []) { 1, 2, },
+ 2,
+ true,
+ 3,
+ };
+
+static struct sfnt_generic_test_args max_test_args =
+ {
+ (uint32_t []) { 70, },
+ 1,
+ false,
+ 6,
+ };
+
+static struct sfnt_generic_test_args min_test_args =
+ {
+ (uint32_t []) { -70, },
+ 1,
+ false,
+ 6,
+ };
+
+static struct sfnt_generic_test_args scantype_test_args =
+ {
+ (uint32_t []) { },
+ 0,
+ false,
+ 3,
+ };
+
+static struct sfnt_interpreter_test all_tests[] =
+ {
+ {
+ "NPUSHB",
+ /* NPUSHB[] 4 1 2 3 4
+ NPUSHB[] 5 1 2 3 4 */
+ (unsigned char []) { 0x40, 4, 1, 2, 3, 4,
+ 0x40, 5, 1, 2, 3, 4, },
+ 10,
+ &npushb_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NPUSHW",
+ /* NPUSHW[] 4 0x101 0x202 0x303 0x404
+ NPUSHW[] 4 0x101 0x202 0x303 0x4?? */
+ (unsigned char []) { 0x41, 4, 1, 1, 2, 2, 3, 3, 4, 4,
+ 0x41, 4, 1, 1, 2, 2, 3, 3, 4, },
+ 19,
+ &npushw_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "PUSHB",
+ /* PUSHB[7] 1 2 3 4 5 6 7 8
+ PUSHB[0] 1
+ PUSHB[5] 1 2 3 4 5 ? */
+ (unsigned char []) { 0xb7, 1, 2, 3, 4, 5, 6, 7, 8,
+ 0xb0, 1,
+ 0xb5, 1, 2, 3, 4, 5, },
+ 17,
+ &pushb_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "PUSHW",
+ /* PUSHW[7] 2 3 2 4 2 5 2 6 2 7 2 8 9 9 9 9
+ PUSHW[0] 255 255 -- this should get sign-extended
+ PUSHW[5] 1 1 2 2 3 3 4 4 5 5 6 ? */
+ (unsigned char []) { 0xbf, 2, 3, 2, 4, 2, 5, 2, 6, 2, 7, 2, 8, 9, 9, 9, 9,
+ 0xb8, 255, 255,
+ 0xbc, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, },
+ 28,
+ &pushw_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that stack overflow is handled correctly",
+ /* NPUSHB[] 101 0... */
+ (unsigned char [103]) { 0x40, 101, },
+ 103,
+ &stack_overflow_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that stack underflow is handled correctly",
+ /* PUSHW[0] 100 100
+ POP[]
+ POP[] */
+ (unsigned char []) { 0xb8, 100, 100,
+ 0x21,
+ 0x21, },
+ 5,
+ &stack_underflow_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SRP0, SRP1, SRP2",
+ /* PUSHB[0] 0
+ SRP0[]
+ PUSHB[0] 1
+ SRP1[]
+ PUSHB[0] 2
+ SRP2[] */
+ (unsigned char []) { 0xb0, 0,
+ 0x10,
+ 0xb0, 1,
+ 0x11,
+ 0xb0, 2,
+ 0x12, },
+ 9,
+ NULL,
+ sfnt_check_srp0,
+ },
+ {
+ "SZP0, SZP1, SZP2, SZPS",
+ /* PUSHB[0] 1
+ SZP0[]
+ PUSHB[0] 1
+ SZP1[]
+ PUSHB[0] 0
+ SZP2[]
+ PUSHB[0] 5
+ SZPS[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x13,
+ 0xb0, 1,
+ 0x14,
+ 0xb0, 0,
+ 0x15,
+ 0xb0, 5,
+ 0x16, },
+ 12,
+ NULL,
+ sfnt_check_szp0,
+ },
+ {
+ "SLOOP",
+ /* PUSHB[0] 2
+ SLOOP[]
+ PUSHB[0] 0
+ SLOOP[] */
+ (unsigned char []) { 0xb0, 2,
+ 0x17,
+ 0xb0, 0,
+ 0x17, },
+ 6,
+ NULL,
+ sfnt_check_sloop,
+ },
+ {
+ "RTG",
+ /* RTG[]
+ PUSHB[0] 32
+ ROUND[] */
+ (unsigned char []) { 0x18,
+ 0xb0, 32,
+ 0x68, },
+ 4,
+ &rtg_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "rounding symmetry",
+ /* RTG[]
+ PUSHW[0] 255 -32
+ ROUND[] */
+ (unsigned char []) { 0x18,
+ 0xb8, 255, - (signed char) 32,
+ 0x68, },
+ 5,
+ &rtg_symmetric_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTG to 0",
+ /* RTG[]
+ PUSHB[0] 31
+ ROUND[] */
+ (unsigned char []) { 0x18,
+ 0xb0, 31,
+ 0x68, },
+ 4,
+ &rtg_1_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "rounding symmetry to 0",
+ /* RTG[]
+ PUSHB[0] 255 -31
+ ROUND[] */
+ (unsigned char []) { 0x18,
+ 0xb8, 255, - (signed char) 31,
+ 0x68, },
+ 5,
+ &rtg_1_symmetric_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTHG",
+ /* RTHG[]
+ PUSHB[0] 0
+ ROUND[] */
+ (unsigned char []) { 0x19,
+ 0xb0, 0,
+ 0x68, },
+ 4,
+ &rthg_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTHG to 96",
+ /* RTHG[]
+ PUSHB[0] 64
+ ROUND[] */
+ (unsigned char []) { 0x19,
+ 0xb0, 64,
+ 0x68, },
+ 4,
+ &rthg_1_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "SMD",
+ /* PUSHB[0] 32
+ SMD[] */
+ (unsigned char []) { 0xb0, 32,
+ 0x1a, },
+ 3,
+ NULL,
+ sfnt_check_smd,
+ },
+ {
+ "ELSE",
+ /* ELSE[]
+ ;; Lots of variable length instructions
+ ;; which will not be executed, like:
+ NPUSHW[] 3 11 22 33 44 55 66
+ NPUSHB[] 1 3
+ PUSHW[2] 1 1 2 2 3 3
+ PUSHB[2] 1 2 3
+ ;; Also test nested ifs.
+ PUSHW[0] 1 1
+ IF[]
+ PUSHW[0] 1 1
+ ELSE[]
+ PUSHW[0] 1 1
+ EIF[]
+ EIF[]
+ PUSHW[0] 1 1
+ ;; the actual contents of the stack.
+ PUSHB[2] 77 90 83 */
+ (unsigned char []) { 0x1b,
+ 0x41, 3, 11, 22, 33, 44, 55, 66,
+ 0x40, 1, 3,
+ 0xba, 1, 1, 2, 2, 3, 3,
+ 0xb2, 1, 2, 3,
+ 0xb8, 1, 1,
+ 0x58,
+ 0xb8, 1, 1,
+ 0x1b,
+ 0xb8, 1, 1,
+ 0x59,
+ 0x59,
+ 0xb2, 77, 90, 83, },
+ 40,
+ &else_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "JMPR",
+ /* PUSHW[2] 00 00 00 PUSHB[2] 255 253 JMPR[]
+ PUSHB[0] 4
+ JMPR[]
+ 255 255 255
+ PUSHW[0] 255 -30
+ JMPR[] */
+ (unsigned char []) { 0xba, 00, 00, 00, 0xb2, 255, 253, 0x1c,
+ 0xb0, 4,
+ 0x1c,
+ 255, 255, 255,
+ 0xb8, 255, -30,
+ 0x1c, },
+ 18,
+ &jmpr_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SCVTCI",
+ /* PUSHB[0] 128
+ SCVTCI[] */
+ (unsigned char []) { 0xb0, 128,
+ 0x1d, },
+ 3,
+ NULL,
+ sfnt_check_scvtci,
+ },
+ {
+ "SSWCI",
+ /* PUSHW[0] 2 0 ;; 512
+ SSWCI[] */
+ (unsigned char []) { 0xb8, 2, 0,
+ 0x1e, },
+ 4,
+ NULL,
+ sfnt_check_sswci,
+ },
+ {
+ "SSW",
+ /* PUSHW[0] 255 255 ; -1
+ SSW[] ; this should be converted to device-space */
+ (unsigned char []) { 0xb8, 255, 255,
+ 0x1f, },
+ 4,
+ NULL,
+ sfnt_check_ssw,
+ },
+ {
+ "DUP",
+ /* PUSHB[0] 70
+ DUP[]
+ POP[]
+ POP[]
+ DUP[] */
+ (unsigned char []) { 0xb0, 70,
+ 0x20,
+ 0x21,
+ 0x21,
+ 0x70, },
+ 6,
+ &dup_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "POP",
+ /* PUSHB[0] 70
+ DUP[]
+ DUP[]
+ POP[] */
+ (unsigned char []) { 0xb0, 70,
+ 0x20,
+ 0x20,
+ 0x21, },
+ 5,
+ &pop_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "CLEAR",
+ /* PUSHB[7] 1 2 3 4 5 6 7 8
+ CLEAR[] */
+ (unsigned char []) { 0xb7, 1, 2, 3, 4, 5, 6, 7, 8,
+ 0x22, },
+ 10,
+ &clear_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SWAP",
+ /* PUSHB[1] 1 2
+ SWAP[] */
+ (unsigned char []) { 0xb1, 1, 2,
+ 0x23, },
+ 4,
+ &swap_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DEPTH",
+ /* PUSHB[2] 3 3 3
+ DEPTH[] */
+ (unsigned char []) { 0xb2, 3, 3, 3,
+ 0x24, },
+ 5,
+ &depth_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "CINDEX",
+ /* PUSHB[4] 0 3 3 4 1
+ CINDEX[] ; pops 1, indices 4
+ CINDEX[] ; pops 4, indices 0
+ PUSHB[0] 6
+ CINDEX[] ; pops 6, trap */
+ (unsigned char []) { 0xb4, 0, 3, 3, 4, 1,
+ 0x25,
+ 0x25,
+ 0xb0, 6,
+ 0x25, },
+ 11,
+ &cindex_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MINDEX",
+ /* PUSHB[6] 0 3 4 7 3 4 2
+ MINDEX[] ; pops 2, array becomes 0 3 4 7 4 3
+ MINDEX[] ; pops 3, array becomes 0 3 7 4 4 */
+ (unsigned char []) { 0xb6, 0, 3, 4, 7, 3, 4, 2,
+ 0x26,
+ 0x26, },
+ 10,
+ &mindex_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RAW",
+ /* RAW[] */
+ (unsigned char []) { 0x28, },
+ 1,
+ &raw_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "LOOPCALL",
+ /* PUSHB[1] 0 2
+ FDEF[]
+ PUSHB[0] 1
+ ADD[]
+ ENDF[]
+ PUSHB[1] 10 2
+ LOOPCALL[] */
+ (unsigned char []) { 0xb1, 0, 2,
+ 0x2c,
+ 0xb0, 1,
+ 0x60,
+ 0x2d,
+ 0xb1, 10, 2,
+ 0x2a, },
+ 12,
+ &loopcall_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "CALL",
+ /* PUSHB[1] 7 2
+ FDEF[]
+ PUSHB[0] 1
+ ADD[]
+ ENDF[]
+ PUSHB[0] 2
+ CALL[]
+ PUSHB[0] 3
+ ADD[]
+ ;; Test that infinite recursion fails.
+ PUSHB[0] 3
+ FDEF[]
+ PUSHB[0] 3
+ CALL[]
+ ENDF[]
+ PUSHB[0] 3
+ CALL[] */
+ (unsigned char []) { 0xb1, 7, 2,
+ 0x2c,
+ 0xb0, 1,
+ 0x60,
+ 0x2d,
+ 0xb0, 2,
+ 0x2b,
+ 0xb0, 3,
+ 0x60,
+ 0xb0, 3,
+ 0x2c,
+ 0xb0, 3,
+ 0x2b,
+ 0x2d,
+ 0xb0, 3,
+ 0x2b, },
+ 24,
+ &call_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that FDEF traps inside nested definitions",
+ /* PUSHB[0] 1
+ FDEF[]
+ FDEF[]
+ ENDF[]
+ ENDF[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x2c,
+ 0x2c,
+ 0x2d,
+ 0x2d, },
+ 6,
+ &fdef_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that FDEF traps upon missing ENDF",
+ /* PUSHB[0] 1
+ FDEF[]
+ PUSHB[3] 1 2 3 4
+ POP[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x2c,
+ 0xb3, 1, 2, 3, 4,
+ 0x21, },
+ 9,
+ &fdef_1_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ENDF",
+ /* ENDF[] */
+ (unsigned char []) { 0x2d, },
+ 1,
+ &endf_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RTDG",
+ /* RTDG[]
+ PUSHB[0] 16
+ ROUND[] */
+ (unsigned char []) { 0x3d,
+ 0xb0, 16,
+ 0x68, },
+ 4,
+ &rtdg_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTDG down to 0",
+ /* RTDG[]
+ PUSHB[0] 15
+ ROUND[] */
+ (unsigned char []) { 0x3d,
+ 0xb0, 15,
+ 0x68, },
+ 4,
+ &rtdg_1_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTDG down to 32",
+ /* RTDG[]
+ PUSHB[0] 47
+ ROUND[] */
+ (unsigned char []) { 0x3d,
+ 0xb0, 47,
+ 0x68, },
+ 4,
+ &rtdg_2_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "RTDG up to 64",
+ /* RTDG[]
+ PUSHB[0] 48
+ ROUND[] */
+ (unsigned char []) { 0x3d,
+ 0xb0, 48,
+ 0x68, },
+ 4,
+ &rtdg_3_test_args,
+ sfnt_check_rounding,
+ },
+ {
+ "WS",
+ /* PUSHB[1] 240 40
+ WS[]
+ PUSHB[0] 240
+ RS[]
+ PUSHB[1] 255 40
+ WS[] */
+ (unsigned char []) { 0xb1, 240, 40,
+ 0x42,
+ 0xb0, 240,
+ 0x43,
+ 0xb1, 255, 40,
+ 0x42, },
+ 11,
+ &ws_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RS",
+ /* PUSHB[0] 255
+ RS[] */
+ (unsigned char []) { 0xb0, 255,
+ 0x43, },
+ 3,
+ &rs_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "WCVTP",
+ /* PUSHB[1] 9 32
+ WCVTP[]
+ PUSHB[0] 9
+ RCVT[]
+ PUSHB[1] 10 10
+ WCVTP[] */
+ (unsigned char []) { 0xb1, 9, 32,
+ 0x44,
+ 0xb0, 9,
+ 0x45,
+ 0xb1, 10, 10,
+ 0x44, },
+ 11,
+ &wcvtp_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RCVT",
+ /* PUSHB[0] 1
+ RCVT[]
+ PUSHB[0] 10
+ RCVT[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x45,
+ 0xb0, 10,
+ 0x45, },
+ 6,
+ &rcvt_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MPPEM",
+ /* MPPEM[] */
+ (unsigned char []) { 0x4b, },
+ 1,
+ &mppem_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MPS",
+ /* MPS[] */
+ (unsigned char []) { 0x4c, },
+ 1,
+ &mps_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "FLIPON",
+ /* FLIPON[] */
+ (unsigned char []) { 0x4d, },
+ 1,
+ NULL,
+ sfnt_check_flipon,
+ },
+ {
+ "FLIPOFF",
+ /* FLIPOFF[] */
+ (unsigned char []) { 0x4e, },
+ 1,
+ NULL,
+ sfnt_check_flipoff,
+ },
+ {
+ "DEBUG",
+ /* PUSHB[0] 1
+ DEBUG[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x4f, },
+ 3,
+ &debug_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "LT",
+ /* PUSHB[1] 47 48
+ LT[]
+ PUSHB[1] 48 47
+ LT[]
+ PUSHB[1] 47 47
+ LT[] */
+ (unsigned char []) { 0xb1, 47, 48,
+ 0x50,
+ 0xb1, 48, 47,
+ 0x50,
+ 0xb1, 47, 47,
+ 0x50, },
+ 12,
+ &lt_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "LTEQ",
+ /* PUSHB[1] 47 48
+ LTEQ[]
+ PUSHB[1] 48 47
+ LTEQ[]
+ PUSHB[1] 47 47
+ LTEQ[] */
+ (unsigned char []) { 0xb1, 47, 48,
+ 0x51,
+ 0xb1, 48, 47,
+ 0x51,
+ 0xb1, 47, 47,
+ 0x51, },
+ 12,
+ &lteq_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "GT",
+ /* PUSHB[1] 47 48
+ GT[]
+ PUSHB[1] 48 47
+ GT[]
+ GT[1] 47 47
+ LTEQ[] */
+ (unsigned char []) { 0xb1, 47, 48,
+ 0x52,
+ 0xb1, 48, 47,
+ 0x52,
+ 0xb1, 47, 47,
+ 0x52, },
+ 12,
+ &gt_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "GTEQ",
+ /* PUSHB[1] 47 48
+ GTEQ[]
+ PUSHB[1] 48 47
+ GTEQ[]
+ GTEQ[1] 47 47
+ LTEQ[] */
+ (unsigned char []) { 0xb1, 47, 48,
+ 0x53,
+ 0xb1, 48, 47,
+ 0x53,
+ 0xb1, 47, 47,
+ 0x53, },
+ 12,
+ &gteq_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "EQ",
+ /* PUSHW[1] 255 253 255 255
+ EQ[]
+ PUSHW[1] 27 27 27 27
+ EQ[]
+ PUSHB[0] 3
+ PUSHW[0] 255 254
+ EQ[] */
+ (unsigned char []) { 0xb9, 255, 253, 255, 255,
+ 0x54,
+ 0xb9, 27, 27, 27, 27,
+ 0x54,
+ 0xb0, 3,
+ 0xb8, 255, 254,
+ 0x54, },
+ 18,
+ &eq_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NEQ",
+ /* PUSHW[1] 255 253 255 255
+ NEQ[]
+ PUSHW[1] 27 27 27 27
+ NEQ[]
+ PUSHB[0] 3
+ PUSHW[0] 255 254
+ NEQ[] */
+ (unsigned char []) { 0xb9, 255, 253, 255, 255,
+ 0x55,
+ 0xb9, 27, 27, 27, 27,
+ 0x55,
+ 0xb0, 3,
+ 0xb8, 255, 254,
+ 0x55, },
+ 18,
+ &neq_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ODD",
+ /* RTG[]
+ PUSHW[1] 255 224 ;; -32
+ ODD[] ;; Rounds symmetrically to -64, which is odd.
+ PUSHW[1] 255 159 ;; -96
+ ODD[] ;; Rounds symmetrically to -128, which is even. */
+ (unsigned char []) { 0x18,
+ 0xb8, 255, 224,
+ 0x56,
+ 0xb8, 255, 159,
+ 0x56, },
+ 9,
+ &odd_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "EVEN",
+ /* RTG[]
+ PUSHW[1] 255 224 ;; -32
+ EVEN[] ;; Rounds symmetrically to -64, which is odd.
+ PUSHW[1] 255 159 ;; -96
+ EVEN[] ;; Rounds symmetrically to -128, which is even. */
+ (unsigned char []) { 0x18,
+ 0xb8, 255, 224,
+ 0x57,
+ 0xb8, 255, 159,
+ 0x57, },
+ 9,
+ &even_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "IF",
+ /* NPUSHB[] 1 0
+ IF[]
+ PUSHW[0] 1 1
+ PUSHW[1] 1 1 2 2
+ PUSHW[2] 1 1 2 2 3 3
+ PUSHW[3] 1 1 2 2 3 3 4 4
+ PUSHW[4] 1 1 2 2 3 3 4 4 5 5
+ PUSHW[5] 1 1 2 2 3 3 4 4 5 5 6 6
+ PUSHW[6] 1 1 2 2 3 3 4 4 5 5 6 6 7 7
+ PUSHW[7] 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8
+ PUSHB[0] 1
+ PUSHB[1] 2 1
+ PUSHB[2] 3 2 1
+ PUSHB[3] 4 3 2 1
+ PUSHB[4] 5 4 3 2 1
+ PUSHB[5] 6 5 4 3 2 1
+ PUSHB[6] 7 6 5 4 3 2 1
+ PUSHB[7] 8 7 6 5 4 3 2 1
+ DEBUG[]
+ IF[]
+ PUSHB[7] 12 12 12 12 12 12 12 12
+ ELSE[]
+ EIF[]
+ ELSE[]
+ PUSHB[1] 17 24
+ NPUSHB[] 5 1 2 3 4 5
+ NPUSHW[] 2 255 255 255 255
+ EIF[]
+
+ PUSHB[0] 1
+ IF[]
+ NPUSHB[] 2 43 43
+ IF[]
+ PUSHB[0] 45
+ ELSE[]
+ PUSHB[0] 14
+ EIF[]
+ ADD[]
+ ELSE[]
+ NPUSHB[] 4 3 2 1 0
+ EIF[]
+ PUSHB[1] 1 3 */
+ (unsigned char []) { 0x40, 1, 0,
+ 0x58,
+ 0xb8, 1, 1,
+ 0xb9, 1, 1, 2, 2,
+ 0xba, 1, 1, 2, 2, 3, 3,
+ 0xbb, 1, 1, 2, 2, 3, 3, 4, 4,
+ 0xbc, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5,
+ 0xbd, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+ 0xbe, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7,
+ 0xbf, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8,
+ 0xb0, 1,
+ 0xb1, 2, 1,
+ 0xb2, 3, 2, 1,
+ 0xb3, 4, 3, 2, 1,
+ 0xb4, 5, 4, 3, 2, 1,
+ 0xb5, 6, 5, 4, 3, 2, 1,
+ 0xb6, 7, 6, 5, 4, 3, 2, 1,
+ 0xb7, 8, 7, 6, 5, 4, 3, 2, 1,
+ 0x4f,
+ 0x58,
+ 0xb7, 12, 12, 12, 12, 12, 12, 12, 12,
+ 0x1b,
+ 0x59,
+ 0x1b,
+ 0xb1, 17, 24,
+ 0x40, 5, 1, 2, 3, 4, 5,
+ 0x41, 2, 255, 255, 255, 255,
+ 0x59,
+ 0xb0, 1,
+ 0x58,
+ 0x40, 2, 43, 43,
+ 0x58,
+ 0xb0, 45,
+ 0x1b,
+ 0xb0, 14,
+ 0x59,
+ 0x60,
+ 0x1b,
+ 0x40, 4, 3, 2, 1, 0,
+ 0x59,
+ 0xb1, 1, 3, },
+ 185,
+ &if_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "EIF",
+ /* PUSHB[0] 1
+ IF[]
+ EIF[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x58,
+ 0x59, },
+ 3,
+ &eif_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "AND",
+ /* PUSHB[1] 0 1
+ AND[]
+ PUSHB[1] 37 0
+ AND[]
+ PUSHB[1] 40 1
+ AND[]
+ PUSHB[1] 0 0
+ AND[] */
+ (unsigned char []) { 0xb1, 0, 1,
+ 0x5a,
+ 0xb1, 37, 0,
+ 0x5a,
+ 0xb1, 40, 1,
+ 0x5a,
+ 0xb1, 0, 0,
+ 0x5a, },
+ 16,
+ &and_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "OR",
+ /* PUSHB[1] 0 1
+ OR[]
+ PUSHB[1] 37 0
+ OR[]
+ PUSHB[1] 40 1
+ OR[]
+ PUSHB[1] 0 0
+ OR[] */
+ (unsigned char []) { 0xb1, 0, 1,
+ 0x5b,
+ 0xb1, 37, 0,
+ 0x5b,
+ 0xb1, 40, 1,
+ 0x5b,
+ 0xb1, 0, 0,
+ 0x5b, },
+ 16,
+ &or_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NOT",
+ /* PUSHB[0] 1
+ NOT[]
+ PUSHB[0] 0
+ NOT[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x5c,
+ 0xb0, 0,
+ 0x5c, },
+ 6,
+ &not_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SDB",
+ /* PUSHB[0] 8
+ SDB[] */
+ (unsigned char []) { 0xb0, 8,
+ 0x5e, },
+ 3,
+ NULL,
+ sfnt_check_sdb,
+ },
+ {
+ "SDS",
+ /* PUSHB[0] 1
+ SDS[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x5f, },
+ 3,
+ NULL,
+ sfnt_check_sds,
+ },
+ {
+ "that SDS rejects invalid values",
+ /* PUSHB[0] 1,
+ SDS[]
+ PUSHB[0] 7
+ SDS[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x5f,
+ 0xb0, 7,
+ 0x5f, },
+ 6,
+ &sds_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ADD",
+ /* PUSHB[1] 64 32
+ ADD[]
+ PUSHW[1] 255 40 0 215 ;; -216 + 215
+ ADD[] */
+ (unsigned char []) { 0xb1, 64, 32,
+ 0x60,
+ 0xb9, 255, 40, 0, 215,
+ 0x60, },
+ 10,
+ &add_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SUB",
+ /* PUSHB[1] 96 32
+ SUB[]
+ PUSHB[1] 32 96
+ SUB[]
+ PUSHW[1] 0 215 255 40 ;; 215 - -216
+ SUB[] */
+ (unsigned char []) { 0xb1, 96, 32,
+ 0x61,
+ 0xb1, 32, 96,
+ 0x61,
+ 0xb9, 0, 215, 255, 40,
+ 0x61, },
+ 14,
+ &sub_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DIV",
+ /* PUSHB[1] 64 128
+ DIV[] ; 1 / 2 = 0.5
+ PUSHW[1] 0 32 255 224
+ DIV[] ; 0.5 / -0.5 = -1.0
+ PUSHW[1] 255 255 0 0
+ DIV[] ; -1 / 0 = trap */
+ (unsigned char []) { 0xb1, 64, 128,
+ 0x62,
+ 0xb9, 0, 32, 255, 224,
+ 0x62,
+ 0xb9, 255, 255, 0, 0,
+ 0x62, },
+ 16,
+ &div_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MUL",
+ /* PUSHB[1] 255 64
+ MUL[] ; 255 * 1 = 255
+ PUSHW[1] 0 255 255 192
+ MUL[] ; 255 * -1 = -255
+ PUSHW[1] 255 1 255 192
+ MUL[] ; -255 * -1 = 255 */
+ (unsigned char []) { 0xb1, 255, 64,
+ 0x63,
+ 0xb9, 0, 255, 255, 192,
+ 0x63,
+ 0xb9, 255, 1, 255, 192,
+ 0x63, },
+ 16,
+ &mul_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ABS",
+ /* PUSHW[0] 255 255
+ ABS[] ;; abs (-1) == 1
+ PUSHB[0] 1
+ ABS[] ;; abs (1) == 1 */
+ (unsigned char []) { 0xb8, 255, 255,
+ 0x64,
+ 0xb0, 1,
+ 0x64, },
+ 7,
+ &abs_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NEG",
+ /* PUSHW[0] 255 255
+ NEG[] ;; neg (-1) == 1
+ PUSHB[0] 1
+ NEG[] ;; neg (1) == -1 */
+ (unsigned char []) { 0xb8, 255, 255,
+ 0x65,
+ 0xb0, 1,
+ 0x65, },
+ 7,
+ &neg_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "FLOOR",
+ /* PUSHW[0] 255 129 ; -127
+ FLOOR[] ; floor (-127) == -128
+ PUSHW[0] 255 193 ; -63
+ FLOOR[] ; floor (-63) == -64
+ PUSHB[0] 63
+ FLOOR[] ; floor (63) == 0
+ PUSHB[0] 127
+ FLOOR[] ; floor (127) == 64
+ PUSHB[0] 191
+ FLOOR[] ; floor (191) == 128 */
+ (unsigned char []) { 0xb8, 255, 129,
+ 0x66,
+ 0xb8, 255, 193,
+ 0x66,
+ 0xb0, 63,
+ 0x66,
+ 0xb0, 127,
+ 0x66,
+ 0xb0, 191,
+ 0x66, },
+ 17,
+ &floor_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "CEILING",
+ /* PUSHW[0] 255 128 ; -128
+ CEILING[] ; ceiling (-128) == -128
+ PUSHW[0] 255 127 ; -129
+ CEILING[] ; ceiling (-129) == -128
+ PUSHW[0] 255 191 ; -65
+ CEILING[] ; ceiling (-65) == -64
+ PUSHW[0] 255 255 ; -1
+ CEILING[] ; ceiling (-1) == 0
+ PUSHB[0] 63
+ CEILING[] ; ceiling (63) == 64
+ PUSHB[0] 65
+ CEILING[] ; ceiling (65) == 128
+ PUSHB[0] 128
+ CEILING[] ; ceiling (128) == 128 */
+ (unsigned char []) { 0xb8, 255, 128,
+ 0x67,
+ 0xb8, 255, 127,
+ 0x67,
+ 0xb8, 255, 191,
+ 0x67,
+ 0xb8, 255, 255,
+ 0x67,
+ 0xb0, 63,
+ 0x67,
+ 0xb0, 65,
+ 0x67,
+ 0xb0, 128,
+ 0x67, },
+ 25,
+ &ceiling_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ROUND",
+ /* ROUND[] */
+ (unsigned char []) { 0x68, },
+ 1,
+ &round_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "NROUND",
+ /* PUSHB[0] 63
+ NROUND[] */
+ (unsigned char []) { 0xb0, 63,
+ 0x6c, },
+ 3,
+ &nround_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "WCVTF",
+ /* PUSHB[1] 1 63
+ WCVTF[]
+ PUSHB[0] 1
+ RCVT[] */
+ (unsigned char []) { 0xb1, 1, 63,
+ 0x70,
+ 0xb0, 1,
+ 0x45, },
+ 7,
+ &wcvtf_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "JROT",
+ /* PUSHB[1] 4 0
+ JROT[] ; this should not skip past the next instruction
+ PUSHB[1] 40 40
+ PUSHB[1] 3 1
+ JROT[] ; this should skip past the next instruction
+ PUSHB[0] 4 */
+ (unsigned char []) { 0xb1, 4, 0,
+ 0x78,
+ 0xb1, 40, 40,
+ 0xb1, 3, 1,
+ 0x78,
+ 0xb0, 4, },
+ 13,
+ &jrot_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "JROF",
+ /* PUSHB[1] 4 0
+ JROF[] ; this should skip past the next instruction
+ PUSHB[1] 40 40
+ PUSHB[1] 3 1
+ JROF[] ; this should not skip past the next instruction
+ PUSHB[0] 4 */
+ (unsigned char []) { 0xb1, 4, 0,
+ 0x79,
+ 0xb1, 40, 40,
+ 0xb1, 3, 1,
+ 0x79,
+ 0xb0, 4, },
+ 13,
+ &jrof_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DELTAC1",
+ /* PUSHB[0] 2
+ SDB[] ; delta base now 2
+ PUSHB[0] 6
+ SDS[] ; delta shift now 6
+ PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2, magnitude 15
+ DELTAC1[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should now be greater by 8 / 64
+
+ PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2, magnitude 15
+ DELTAC1[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged */
+ (unsigned char []) { 0xb0, 2,
+ 0x5e,
+ 0xb0, 6,
+ 0x5f,
+ 0xb2, 255, 5, 1,
+ 0x73,
+ 0xb0, 5,
+ 0x45,
+ 0xb2, 239, 5, 1,
+ 0x73,
+ 0xb0, 5,
+ 0x45, },
+ 22,
+ &deltac1_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DELTAC2",
+ /* PUSHB[0] 2
+ SDB[] ; delta base now 2
+ PUSHB[0] 6
+ SDS[] ; delta shift now 6
+ PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2 + 16, magnitude 15
+ DELTAC2[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged
+
+ PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2 + 16, magnitude 15
+ DELTAC2[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged */
+ (unsigned char []) { 0xb0, 2,
+ 0x5e,
+ 0xb0, 6,
+ 0x5f,
+ 0xb2, 255, 5, 1,
+ 0x74,
+ 0xb0, 5,
+ 0x45,
+ 0xb2, 239, 5, 1,
+ 0x74,
+ 0xb0, 5,
+ 0x45, },
+ 22,
+ &deltac2_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "DELTAC3",
+ /* PUSHB[0] 2
+ SDB[] ; delta base now 2
+ PUSHB[0] 6
+ SDS[] ; delta shift now 6
+ PUSHB[2] 0xff 5 1 ; CVT index 5, ppem 15 + 2 + 32, magnitude 15
+ DELTAC3[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged
+
+ PUSHB[2] 0xef 5 1 ; CVT index 5, ppem 14 + 2 + 32, magnitude 15
+ DELTAC3[]
+ PUSHB[0] 1
+ RCVT[] ; CVT index 5 should be unchanged */
+ (unsigned char []) { 0xb0, 2,
+ 0x5e,
+ 0xb0, 6,
+ 0x5f,
+ 0xb2, 255, 5, 1,
+ 0x75,
+ 0xb0, 5,
+ 0x45,
+ 0xb2, 239, 5, 1,
+ 0x75,
+ 0xb0, 5,
+ 0x45, },
+ 22,
+ &deltac3_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SROUND",
+ sfnt_sround_instructions,
+ ARRAYELTS (sfnt_sround_instructions),
+ &sround_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "S45ROUND",
+ sfnt_s45round_instructions,
+ ARRAYELTS (sfnt_s45round_instructions),
+ &s45round_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RUTG",
+ /* RUTG[]
+ PUSHB[0] 1
+ ROUND[]
+ PUSHB[0] 64
+ ROUND[]
+ PUSHB[0] 0
+ ROUND[] */
+ (unsigned char []) { 0x7c,
+ 0xb0, 1,
+ 0x68,
+ 0xb0, 64,
+ 0x68,
+ 0xb0, 0,
+ 0x68, },
+ 10,
+ &rutg_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "RDTG",
+ /* RUTG[]
+ PUSHB[0] 1
+ ROUND[]
+ PUSHB[0] 63
+ ROUND[]
+ PUSHB[0] 64
+ ROUND[] */
+ (unsigned char []) { 0x7d,
+ 0xb0, 1,
+ 0x68,
+ 0xb0, 63,
+ 0x68,
+ 0xb0, 64,
+ 0x68, },
+ 10,
+ &rdtg_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SANGW",
+ /* PUSHB[0] 3
+ SANGW[] */
+ (unsigned char []) { 0xb0, 3,
+ 0x7e, },
+ 3,
+ &sangw_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "AA",
+ /* PUSHB[0] 3
+ AA[] */
+ (unsigned char []) { 0xb0, 3,
+ 0x7f, },
+ 3,
+ &aa_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SCANCTRL",
+ /* PUSHB[0] 1
+ SCANCTRL[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x85, },
+ 3,
+ NULL,
+ sfnt_check_scanctrl,
+ },
+ {
+ "GETINFO",
+ /* PUSHB[0] 1
+ GETINFO[]
+ PUSHB[0] 6
+ GETINFO[] */
+ (unsigned char []) { 0xb0, 1,
+ 0x88,
+ 0xb0, 6,
+ 0x88, },
+ 6,
+ &getinfo_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "IDEF",
+ /* PUSHB[0] 0x83
+ IDEF[]
+ PUSHB[3] 1 2 3 4
+ POP[]
+ ENDF[]
+ 0x83 */
+ (unsigned char []) { 0xb0, 0x83,
+ 0x89,
+ 0xb3, 1, 2, 3, 4,
+ 0x21,
+ 0x2d,
+ 0x83, },
+ 11,
+ &idef_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "ROLL",
+ /* PUSHB[4] 1 2 3 4 5
+ ROLL[] ; this should become 1 2 4 5 3 */
+ (unsigned char []) { 0xb4, 1, 2, 3, 4, 5,
+ 0x8a, },
+ 7,
+ &roll_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "that ROLL correctly handles underflow",
+ /* PUSHB[1] 1 2
+ ROLL[] */
+ (unsigned char []) { 0xb1, 1, 2,
+ 0x8a, },
+ 4,
+ &roll_1_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MAX",
+ /* PUSHW[1] 0 70 255 186 ; 70, -70
+ MAX[] */
+ (unsigned char []) { 0xb9, 0, 70, 255, 186,
+ 0x8b, },
+ 6,
+ &max_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "MIN",
+ /* PUSHW[1] 0 70 255 186 ; 70, -70
+ MIN[] */
+ (unsigned char []) { 0xb9, 0, 70, 255, 186,
+ 0x8c, },
+ 6,
+ &min_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "SCANTYPE",
+ /* PUSHB[0] 0
+ SCANTYPE[] */
+ (unsigned char []) { 0xb0, 0,
+ 0x8d, },
+ 3,
+ &scantype_test_args,
+ sfnt_generic_check,
+ },
+ {
+ "INSTCTRL",
+ /* PUSHB[1] 1 1
+ INSTCTRL[] ; (1 << 1) should now be set
+ PUSHB[1] 2 1
+ INSTCTRL[] ; (1 << 2) should now be set
+ PUSHB[1] 2 0
+ INSTCTRL[] ; (1 << 2) should no longer be set */
+ (unsigned char []) { 0xb1, 1, 1,
+ 0x8e,
+ 0xb1, 2, 1,
+ 0x8e,
+ 0xb1, 2, 0,
+ 0x8e, },
+ 12,
+ NULL,
+ sfnt_check_instctrl,
+ },
+ };
+
+
+
+/* Instruction debugger. */
+
+static void
+sfnt_setup_debugger (void)
+{
+ XGCValues gcv;
+ Font font;
+
+ display = XOpenDisplay (NULL);
+
+ if (!display)
+ exit (1);
+
+ window = XCreateSimpleWindow (display, DefaultRootWindow (display),
+ 0, 0, 200, 200, 0, 0,
+ WhitePixel (display,
+ DefaultScreen (display)));
+ XMapWindow (display, window);
+
+ /* Select for the appropriate events. */
+ XSelectInput (display, window, KeyPressMask | ExposureMask);
+
+ /* Find an appropriate font. */
+ font = XLoadFont (display, "6x13");
+
+ if (!font)
+ exit (1);
+
+ /* The debugger has been set up. Set up the GCs for drawing points
+ and backgrounds. */
+
+ gcv.foreground = BlackPixel (display, DefaultScreen (display));
+ gcv.font = font;
+ point_gc = XCreateGC (display, window, GCForeground | GCFont,
+ &gcv);
+ gcv.foreground = WhitePixel (display, DefaultScreen (display));
+ background_gc = XCreateGC (display, window, GCForeground, &gcv);
+}
+
+static const char *
+sfnt_name_instruction (unsigned char opcode)
+{
+ static const char *const opcode_names[256] = {
+ "7 SVTCA y",
+ "7 SVTCA x",
+ "8 SPvTCA y",
+ "8 SPvTCA x",
+ "8 SFvTCA y",
+ "8 SFvTCA x",
+ "8 SPvTL ||",
+ "7 SPvTL +",
+ "8 SFvTL ||",
+ "7 SFvTL +",
+ "5 SPvFS",
+ "5 SFvFS",
+ "3 GPv",
+ "3 GFv",
+ "6 SFvTPv",
+ "5 ISECT",
+
+ "4 SRP0",
+ "4 SRP1",
+ "4 SRP2",
+ "4 SZP0",
+ "4 SZP1",
+ "4 SZP2",
+ "4 SZPS",
+ "5 SLOOP",
+ "3 RTG",
+ "4 RTHG",
+ "3 SMD",
+ "4 ELSE",
+ "4 JMPR",
+ "6 SCvTCi",
+ "5 SSwCi",
+ "3 SSW",
+
+ "3 DUP",
+ "3 POP",
+ "5 CLEAR",
+ "4 SWAP",
+ "5 DEPTH",
+ "6 CINDEX",
+ "6 MINDEX",
+ "8 AlignPTS",
+ "7 INS_$28",
+ "3 UTP",
+ "8 LOOPCALL",
+ "4 CALL",
+ "4 FDEF",
+ "4 ENDF",
+ "7 MDAP[0]",
+ "7 MDAP[1]",
+
+ "6 IUP[0]",
+ "6 IUP[1]",
+ "6 SHP[0]",
+ "6 SHP[1]",
+ "6 SHC[0]",
+ "6 SHC[1]",
+ "6 SHZ[0]",
+ "6 SHZ[1]",
+ "5 SHPIX",
+ "2 IP",
+ "8 MSIRP[0]",
+ "8 MSIRP[1]",
+ "7 AlignRP",
+ "4 RTDG",
+ "7 MIAP[0]",
+ "7 MIAP[1]",
+
+ "6 NPushB",
+ "6 NPushW",
+ "2 WS",
+ "2 RS",
+ "5 WCvtP",
+ "4 RCvt",
+ "5 GC[0]",
+ "5 GC[1]",
+ "4 SCFS",
+ "5 MD[0]",
+ "5 MD[1]",
+ "5 MPPEM",
+ "3 MPS",
+ "6 FlipON",
+ "7 FlipOFF",
+ "5 DEBUG",
+
+ "2 LT",
+ "4 LTEQ",
+ "2 GT",
+ "4 GTEQ",
+ "2 EQ",
+ "3 NEQ",
+ "3 ODD",
+ "4 EVEN",
+ "2 IF",
+ "3 EIF",
+ "3 AND",
+ "2 OR",
+ "3 NOT",
+ "7 DeltaP1",
+ "3 SDB",
+ "3 SDS",
+
+ "3 ADD",
+ "3 SUB",
+ "3 DIV",
+ "3 MUL",
+ "3 ABS",
+ "3 NEG",
+ "5 FLOOR",
+ "7 CEILING",
+ "8 ROUND[0]",
+ "8 ROUND[1]",
+ "8 ROUND[2]",
+ "8 ROUND[3]",
+ "9 NROUND[0]",
+ "9 NROUND[1]",
+ "9 NROUND[2]",
+ "9 NROUND[3]",
+
+ "5 WCvtF",
+ "7 DeltaP2",
+ "7 DeltaP3",
+ "A DeltaCn[0]",
+ "A DeltaCn[1]",
+ "A DeltaCn[2]",
+ "6 SROUND",
+ "8 S45Round",
+ "4 JROT",
+ "4 JROF",
+ "4 ROFF",
+ "7 INS_$7B",
+ "4 RUTG",
+ "4 RDTG",
+ "5 SANGW",
+ "2 AA",
+
+ "6 FlipPT",
+ "8 FlipRgON",
+ "9 FlipRgOFF",
+ "7 INS_$83",
+ "7 INS_$84",
+ "8 ScanCTRL",
+ "9 SDPvTL[0]",
+ "9 SDPvTL[1]",
+ "7 GetINFO",
+ "4 IDEF",
+ "4 ROLL",
+ "3 MAX",
+ "3 MIN",
+ "8 ScanTYPE",
+ "8 InstCTRL",
+ "7 INS_$8F",
+
+ "7 INS_$90",
+ "7 GXAXIS",
+ "7 INS_$92",
+ "7 INS_$93",
+ "7 INS_$94",
+ "7 INS_$95",
+ "7 INS_$96",
+ "7 INS_$97",
+ "7 INS_$98",
+ "7 INS_$99",
+ "7 INS_$9A",
+ "7 INS_$9B",
+ "7 INS_$9C",
+ "7 INS_$9D",
+ "7 INS_$9E",
+ "7 INS_$9F",
+
+ "7 INS_$A0",
+ "7 INS_$A1",
+ "7 INS_$A2",
+ "7 INS_$A3",
+ "7 INS_$A4",
+ "7 INS_$A5",
+ "7 INS_$A6",
+ "7 INS_$A7",
+ "7 INS_$A8",
+ "7 INS_$A9",
+ "7 INS_$AA",
+ "7 INS_$AB",
+ "7 INS_$AC",
+ "7 INS_$AD",
+ "7 INS_$AE",
+ "7 INS_$AF",
+
+ "8 PushB[0]",
+ "8 PushB[1]",
+ "8 PushB[2]",
+ "8 PushB[3]",
+ "8 PushB[4]",
+ "8 PushB[5]",
+ "8 PushB[6]",
+ "8 PushB[7]",
+ "8 PushW[0]",
+ "8 PushW[1]",
+ "8 PushW[2]",
+ "8 PushW[3]",
+ "8 PushW[4]",
+ "8 PushW[5]",
+ "8 PushW[6]",
+ "8 PushW[7]",
+
+ "7 MDRP[G]",
+ "7 MDRP[B]",
+ "7 MDRP[W]",
+ "7 MDRP[?]",
+ "8 MDRP[rG]",
+ "8 MDRP[rB]",
+ "8 MDRP[rW]",
+ "8 MDRP[r?]",
+ "8 MDRP[mG]",
+ "8 MDRP[mB]",
+ "8 MDRP[mW]",
+ "8 MDRP[m?]",
+ "9 MDRP[mrG]",
+ "9 MDRP[mrB]",
+ "9 MDRP[mrW]",
+ "9 MDRP[mr?]",
+
+ "8 MDRP[pG]",
+ "8 MDRP[pB]",
+ "8 MDRP[pW]",
+ "8 MDRP[p?]",
+ "9 MDRP[prG]",
+ "9 MDRP[prB]",
+ "9 MDRP[prW]",
+ "9 MDRP[pr?]",
+ "9 MDRP[pmG]",
+ "9 MDRP[pmB]",
+ "9 MDRP[pmW]",
+ "9 MDRP[pm?]",
+ "A MDRP[pmrG]",
+ "A MDRP[pmrB]",
+ "A MDRP[pmrW]",
+ "A MDRP[pmr?]",
+
+ "7 MIRP[G]",
+ "7 MIRP[B]",
+ "7 MIRP[W]",
+ "7 MIRP[?]",
+ "8 MIRP[rG]",
+ "8 MIRP[rB]",
+ "8 MIRP[rW]",
+ "8 MIRP[r?]",
+ "8 MIRP[mG]",
+ "8 MIRP[mB]",
+ "8 MIRP[mW]",
+ "8 MIRP[m?]",
+ "9 MIRP[mrG]",
+ "9 MIRP[mrB]",
+ "9 MIRP[mrW]",
+ "9 MIRP[mr?]",
+
+ "8 MIRP[pG]",
+ "8 MIRP[pB]",
+ "8 MIRP[pW]",
+ "8 MIRP[p?]",
+ "9 MIRP[prG]",
+ "9 MIRP[prB]",
+ "9 MIRP[prW]",
+ "9 MIRP[pr?]",
+ "9 MIRP[pmG]",
+ "9 MIRP[pmB]",
+ "9 MIRP[pmW]",
+ "9 MIRP[pm?]",
+ "A MIRP[pmrG]",
+ "A MIRP[pmrB]",
+ "A MIRP[pmrW]",
+ "A MIRP[pmr?]"
+ };
+
+ return opcode_names[opcode];
+}
+
+static void
+sfnt_draw_debugger (struct sfnt_interpreter *interpreter)
+{
+ int x, y, i;
+ char buffer[80];
+ const char *name;
+ int opcode;
+
+ sprintf (buffer, "opcode:IP:depth: 0x%x:%d:%d",
+ interpreter->instructions[interpreter->IP],
+ interpreter->IP,
+ interpreter->call_depth);
+
+ /* Clear the window. */
+ XFillRectangle (display, window, background_gc,
+ 0, 0, 65535, 65535);
+
+ /* Draw some information about the opcode. */
+ XDrawString (display, window, point_gc, 0, 13, buffer,
+ strlen (buffer));
+
+ opcode = interpreter->instructions[interpreter->IP];
+
+ sprintf (buffer, "opcode: %s",
+ sfnt_name_instruction (opcode));
+
+ XDrawString (display, window, point_gc, 14, 27, buffer,
+ strlen (buffer));
+
+ if (interpreter->state.project
+ == sfnt_project_onto_x_axis_vector)
+ name = "X axis";
+ else if (interpreter->state.project
+ == sfnt_project_onto_y_axis_vector)
+ name = "Y axis";
+ else
+ name = "Any";
+
+ sprintf (buffer, "projection function: %s", name);
+
+ XDrawString (display, window, point_gc, 28, 42, buffer,
+ strlen (buffer));
+
+ /* Draw each point onto the window. */
+ for (i = 0; i < interpreter->glyph_zone->num_points; ++i)
+ {
+ x = interpreter->glyph_zone->x_current[i] / 16;
+ y = (200 - interpreter->glyph_zone->y_current[i] / 16);
+
+ XFillRectangle (display, window, point_gc, x, y, 4, 4);
+ }
+}
+
+static void
+sfnt_run_hook (struct sfnt_interpreter *interpreter)
+{
+ pid_t pid;
+ XEvent event;
+
+#ifdef TEST_BREAK_AFTER
+ static unsigned int instructions;
+
+ if (++instructions < TEST_BREAK_AFTER)
+ return;
+#endif
+
+ pid = fork ();
+
+ if (pid == 0)
+ {
+ sfnt_setup_debugger ();
+
+ while (true)
+ {
+ XNextEvent (display, &event);
+
+ switch (event.type)
+ {
+ case KeyPress:
+ XDestroyWindow (display, window);
+ XCloseDisplay (display);
+ exit (0);
+ break;
+
+ case Expose:
+ sfnt_draw_debugger (interpreter);
+ break;
+ }
+ }
+ }
+ else
+ {
+ while (waitpid (pid, NULL, 0) != pid && errno == EINTR)
+ /* Spin. */;
+ }
+}
+
+static struct sfnt_prep_table *exec_prep;
+static struct sfnt_fpgm_table *exec_fpgm;
+
+static const char *
+sfnt_identify_instruction (struct sfnt_interpreter *interpreter)
+{
+ static char buffer[256];
+ unsigned char *where;
+
+ where = interpreter->instructions + interpreter->IP;
+
+ if (exec_prep
+ && where >= exec_prep->instructions
+ && where < (exec_prep->instructions
+ + exec_prep->num_instructions))
+ {
+ sprintf (buffer, "prep+%td",
+ where - exec_prep->instructions);
+ return buffer;
+ }
+
+ if (exec_fpgm->instructions
+ && where >= exec_fpgm->instructions
+ && where < (exec_fpgm->instructions
+ + exec_fpgm->num_instructions))
+ {
+ sprintf (buffer, "fpgm+%td",
+ where - exec_fpgm->instructions);
+ return buffer;
+ }
+
+ sprintf (buffer, "IP+%td", where - interpreter->instructions);
+ return buffer;
+}
+
+static void
+sfnt_verbose (struct sfnt_interpreter *interpreter)
+{
+ struct sfnt_instructed_outline temp;
+ struct sfnt_glyph_outline *outline;
+ struct sfnt_raster *raster;
+ unsigned char opcode;
+ const char *name;
+ static unsigned int instructions;
+
+ /* Build a temporary outline containing the values of the
+ interpreter's glyph zone. */
+
+ if (interpreter->glyph_zone)
+ {
+ temp.num_points = interpreter->glyph_zone->num_points;
+ temp.num_contours = interpreter->glyph_zone->num_contours;
+ temp.contour_end_points = interpreter->glyph_zone->contour_end_points;
+ temp.x_points = interpreter->glyph_zone->x_current;
+ temp.y_points = interpreter->glyph_zone->y_current;
+ temp.flags = interpreter->glyph_zone->flags;
+
+ outline = sfnt_build_instructed_outline (&temp);
+
+ if (!outline)
+ return;
+
+ printf ("outline bounds: %g %g, %g %g\n",
+ sfnt_coerce_fixed (outline->xmin),
+ sfnt_coerce_fixed (outline->ymin),
+ sfnt_coerce_fixed (outline->xmax),
+ sfnt_coerce_fixed (outline->ymax));
+
+ raster = sfnt_raster_glyph_outline (outline);
+
+ if (raster)
+ sfnt_test_raster (raster, NULL, 0);
+
+ xfree (outline);
+ xfree (raster);
+ }
+
+ opcode = interpreter->instructions[interpreter->IP];
+ printf ("opcode, number of instructions: %s %u\n",
+ sfnt_name_instruction (opcode), instructions++);
+ printf ("instruction: %s\n",
+ sfnt_identify_instruction (interpreter));
+
+ if (interpreter->state.project
+ == sfnt_project_onto_x_axis_vector)
+ name = "X axis";
+ else if (interpreter->state.project
+ == sfnt_project_onto_y_axis_vector)
+ name = "Y axis";
+ else
+ name = "Any";
+
+ printf ("projection function: %s\n", name);
+
+ printf ("proj and free vecs: %d %d %d %d\n",
+ interpreter->state.projection_vector.x,
+ interpreter->state.projection_vector.y,
+ interpreter->state.freedom_vector.x,
+ interpreter->state.freedom_vector.y);
+}
+
+static void
+sfnt_push_hook (struct sfnt_interpreter *interpreter,
+ uint32_t value)
+{
+ int32_t alternate;
+
+ alternate = value;
+
+ fprintf (stderr, "--> %"PRIi32"\n", alternate);
+}
+
+static void
+sfnt_pop_hook (struct sfnt_interpreter *interpreter,
+ uint32_t value)
+{
+ int32_t alternate;
+
+ alternate = value;
+
+ fprintf (stderr, "<<- %"PRIi32"\n", alternate);
+}
+
+
+
+static void
+sfnt_test_uvs (int fd, struct sfnt_cmap_format_14 *format14)
+{
+ struct sfnt_uvs_context *context;
+ size_t i, j;
+ sfnt_glyph glyph;
+ sfnt_char c;
+ struct sfnt_nondefault_uvs_table *uvs;
+
+ context = sfnt_create_uvs_context (format14, fd);
+
+ /* Print each variation selector and its associated ranges. */
+
+ if (!context)
+ fprintf (stderr, "failed to read uvs data\n");
+ else
+ {
+ fprintf (stderr, "UVS context with %zu records and %zu tables\n",
+ context->num_records, context->nmemb);
+
+ for (i = 0; i < context->num_records; ++i)
+ {
+ if (!context->records[i].nondefault_uvs)
+ continue;
+
+ uvs = context->records[i].nondefault_uvs;
+
+ for (j = 0; j < uvs->num_uvs_mappings; ++j)
+ {
+ c = uvs->mappings[j].unicode_value;
+ glyph = sfnt_variation_glyph_for_char (uvs, c);
+
+ if (glyph != uvs->mappings[j].base_character_value)
+ abort ();
+
+ fprintf (stderr, " UVS: %"PRIx32" (%"PRIx32") -> %"PRIu32"\n",
+ c, context->records[i].selector, glyph);
+ }
+ }
+
+ sfnt_free_uvs_context (context);
+ }
+}
+
+
+
+/* Main entry point. */
+
+/* Simple tests that were used while developing this file. By the
+ time you are reading this, they probably no longer work.
+
+ Compile like so in this directory:
+
+ gcc -Demacs -I. -I. -I../lib -I../lib -MMD -MF deps/.d -MP
+ -fno-common -Wall -Warith-conversion -Wdate-time
+ -Wdisabled-optimization -Wdouble-promotion -Wduplicated-cond
+ -Wextra -Wformat-signedness -Winit-self -Winvalid-pch -Wlogical-op
+ -Wmissing-declarations -Wmissing-include-dirs -Wmissing-prototypes
+ -Wnested-externs -Wnull-dereference -Wold-style-definition
+ -Wopenmp-simd -Wpacked -Wpointer-arith -Wstrict-prototypes
+ -Wsuggest-attribute=format -Wsuggest-final-methods
+ -Wsuggest-final-types -Wtrampolines -Wuninitialized
+ -Wunknown-pragmas -Wunused-macros -Wvariadic-macros
+ -Wvector-operation-performance -Wwrite-strings -Warray-bounds=2
+ -Wattribute-alias=2 -Wformat=2 -Wformat-truncation=2
+ -Wimplicit-fallthrough=5 -Wshift-overflow=2 -Wuse-after-free=3
+ -Wvla-larger-than=4031 -Wredundant-decls
+ -Wno-missing-field-initializers -Wno-override-init
+ -Wno-sign-compare -Wno-type-limits -Wno-unused-parameter
+ -Wno-format-nonliteral -Wno-bidi-chars -g3 -O0 -DTEST sfnt.c -o
+ sfnt ../lib/libgnu.a -lX11 -lXrender
+
+ after gnulib has been built. Then, run ./sfnt
+ /path/to/font.ttf. */
+
+int
+main (int argc, char **argv)
+{
+ struct sfnt_offset_subtable *font;
+ struct sfnt_cmap_encoding_subtable *subtables;
+ struct sfnt_cmap_encoding_subtable_data **data;
+ struct sfnt_cmap_table *table;
+ int fd, i, j;
+ sfnt_char character;
+ struct sfnt_head_table *head;
+ struct sfnt_hhea_table *hhea;
+ struct sfnt_loca_table_short *loca_short;
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_glyf_table *glyf;
+ struct sfnt_glyph *glyph;
+ sfnt_glyph code;
+ struct sfnt_test_dcontext dcontext;
+ struct sfnt_glyph_outline *outline;
+ struct timespec start, end, sub, sub1, sub2, sub3;
+ static struct sfnt_maxp_table *maxp;
+ struct sfnt_raster *raster;
+ struct sfnt_hmtx_table *hmtx;
+ struct sfnt_glyph_metrics metrics;
+ struct sfnt_name_table *name;
+ unsigned char *string;
+ struct sfnt_name_record record;
+ struct sfnt_meta_table *meta;
+ struct sfnt_ttc_header *ttc;
+ struct sfnt_interpreter *interpreter;
+ struct sfnt_cvt_table *cvt;
+ struct sfnt_fpgm_table *fpgm;
+ const char *trap;
+ struct sfnt_prep_table *prep;
+ struct sfnt_graphics_state state;
+ struct sfnt_instructed_outline *value;
+ struct sfnt_fvar_table *fvar;
+ struct sfnt_gvar_table *gvar;
+ struct sfnt_avar_table *avar;
+ struct sfnt_cvar_table *cvar;
+ sfnt_fixed scale;
+ char *fancy;
+ int *advances;
+ struct sfnt_raster **rasters;
+ size_t length;
+ char *axis_name;
+ struct sfnt_instance *instance;
+ struct sfnt_blend blend;
+ struct sfnt_metrics_distortion distortion;
+
+ if (argc < 2)
+ return 1;
+
+ instance = NULL;
+
+ if (!strcmp (argv[1], "--check-interpreter"))
+ {
+ interpreter = sfnt_make_test_interpreter ();
+
+ if (!interpreter)
+ abort ();
+
+ for (i = 0; i < ARRAYELTS (all_tests); ++i)
+ sfnt_run_interpreter_test (&all_tests[i], interpreter);
+
+ exit (0);
+ }
+
+ fd = open (argv[1], O_RDONLY);
+
+ if (fd < 1)
+ return 1;
+
+ ttc = NULL;
+
+ font = sfnt_read_table_directory (fd);
+
+ if (font == (struct sfnt_offset_subtable *) -1)
+ {
+ if (lseek (fd, 0, SEEK_SET) != 0)
+ return 1;
+
+ ttc = sfnt_read_ttc_header (fd);
+
+ if (!ttc)
+ return 1;
+
+ fprintf (stderr, "TrueType collection: %"PRIu32" fonts installed\n",
+ ttc->num_fonts);
+ fflush (stderr);
+
+ printf ("Which font? ");
+ if (scanf ("%d", &i) == EOF)
+ return 1;
+
+ if (i >= ttc->num_fonts || i < 0)
+ {
+ printf ("out of range\n");
+ return 1;
+ }
+
+ if (lseek (fd, ttc->offset_table[i], SEEK_SET)
+ != ttc->offset_table[i])
+ return 1;
+
+ font = sfnt_read_table_directory (fd);
+ }
+
+ if (!font || font == (struct sfnt_offset_subtable *) -1)
+ {
+ close (fd);
+ return 1;
+ }
+
+ for (i = 0; i < font->num_tables; ++i)
+ fprintf (stderr, "Found new subtable with tag %"PRIx32
+ " at offset %"PRIu32"\n",
+ font->subtables[i].tag,
+ font->subtables[i].offset);
+
+ table = sfnt_read_cmap_table (fd, font, &subtables, &data);
+
+ if (!table)
+ {
+ close (fd);
+ xfree (font);
+ return 1;
+ }
+
+ fprintf (stderr, "number of subtables: %"PRIu16"\n",
+ table->num_subtables);
+
+ for (i = 0; i < table->num_subtables; ++i)
+ {
+ fprintf (stderr, "Found cmap table %"PRIu32": %p\n",
+ subtables[i].offset, (void *) data[i]);
+
+ if (data[i])
+ fprintf (stderr, " format: %"PRIu16"\n",
+ data[i]->format);
+ }
+
+ if (argc >= 3 && !strcmp (argv[2], "--check-variation-selectors"))
+ {
+ /* Look for a format 14 cmap table. */
+
+ for (i = 0; i < table->num_subtables; ++i)
+ {
+ if (data[i]->format == 14)
+ {
+ fprintf (stderr, "format 14 subtable found\n");
+ sfnt_test_uvs (fd, (struct sfnt_cmap_format_14 *) data[i]);
+ return 0;
+ }
+ }
+
+ return 1;
+ }
+
+#define FANCY_PPEM 19
+#define EASY_PPEM 19
+
+ interpreter = NULL;
+ head = sfnt_read_head_table (fd, font);
+ hhea = sfnt_read_hhea_table (fd, font);
+ glyf = sfnt_read_glyf_table (fd, font);
+ maxp = sfnt_read_maxp_table (fd, font);
+ name = sfnt_read_name_table (fd, font);
+ meta = sfnt_read_meta_table (fd, font);
+ cvt = sfnt_read_cvt_table (fd, font);
+ fpgm = sfnt_read_fpgm_table (fd, font);
+ prep = sfnt_read_prep_table (fd, font);
+ fvar = sfnt_read_fvar_table (fd, font);
+ gvar = sfnt_read_gvar_table (fd, font);
+ avar = sfnt_read_avar_table (fd, font);
+ cvar = NULL;
+ hmtx = NULL;
+
+ if (fvar && cvt)
+ cvar = sfnt_read_cvar_table (fd, font, fvar, cvt);
+
+ if (cvar)
+ fprintf (stderr, "cvar table found\n");
+
+ exec_prep = prep;
+ exec_fpgm = fpgm;
+ fancy = getenv ("SFNT_FANCY_TEST");
+
+ loca_long = NULL;
+ loca_short = NULL;
+
+ if (fvar)
+ {
+ fprintf (stderr, "FVAR table found!\n"
+ "version: %"PRIu16".%"PRIu16"\n"
+ "axis_count: %"PRIu16"\n"
+ "axis_size: %"PRIu16"\n"
+ "instance_count: %"PRIu16"\n"
+ "instance_size: %"PRIu16"\n",
+ fvar->major_version,
+ fvar->minor_version,
+ fvar->axis_count,
+ fvar->axis_size,
+ fvar->instance_count,
+ fvar->instance_size);
+
+ for (i = 0; i < fvar->axis_count; ++i)
+ {
+ if (name)
+ {
+ axis_name
+ = (char *) sfnt_find_name (name, fvar->axis[i].name_id,
+ &record);
+
+ if (axis_name)
+ fprintf (stderr, "axis no: %d; name: %.*s\n",
+ i, record.length, axis_name);
+ }
+
+ fprintf (stderr, " axis: %"PRIx32" %g %g %g\n",
+ fvar->axis[i].axis_tag,
+ sfnt_coerce_fixed (fvar->axis[i].min_value),
+ sfnt_coerce_fixed (fvar->axis[i].default_value),
+ sfnt_coerce_fixed (fvar->axis[i].max_value));
+ }
+
+ for (i = 0; i < fvar->instance_count; ++i)
+ {
+ if (name)
+ {
+ axis_name
+ = (char *) sfnt_find_name (name, fvar->instance[i].name_id,
+ &record);
+
+ if (axis_name)
+ fprintf (stderr, "instance no: %d; name: %.*s\n",
+ i, record.length, axis_name);
+ }
+ }
+
+ if (fvar->instance_count > 1)
+ {
+ printf ("instance? ");
+
+ if (scanf ("%d", &i) == EOF)
+ goto free_lab;
+
+ if (i >= fvar->instance_count)
+ goto free_lab;
+
+ if (i >= 0)
+ instance = &fvar->instance[i];
+ }
+ }
+
+ if (gvar)
+ fprintf (stderr, "gvar table found\n");
+
+ if (avar)
+ {
+ fprintf (stderr, "avar table found\n");
+
+ for (i = 0; i < avar->axis_count; ++i)
+ {
+ fprintf (stderr, "axis: %d, %"PRIu16" pairs\n",
+ i, avar->segments[i].pair_count);
+
+ for (j = 0; j < avar->segments[i].pair_count; ++j)
+ fprintf (stderr, "pair: %g, %g\n",
+ (avar->segments[i].correspondence[j].from_coord
+ / 16384.0),
+ (avar->segments[i].correspondence[j].to_coord
+ / 16384.0));
+ }
+ }
+
+ memset (&blend, 0, sizeof blend);
+
+ if (instance && gvar)
+ {
+ sfnt_init_blend (&blend, fvar, gvar, avar,
+ cvar);
+
+ for (i = 0; i < fvar->axis_count; ++i)
+ blend.coords[i] = instance->coords[i];
+
+ sfnt_normalize_blend (&blend);
+ }
+
+ if (fancy)
+ {
+ length = strlen (fancy);
+ scale = sfnt_div_fixed (FANCY_PPEM, head->units_per_em);
+
+ if (hhea && maxp)
+ hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp);
+
+ if (!maxp || !head || !prep || !hmtx || !hhea
+ || table->num_subtables < 1)
+ exit (1);
+
+ if (head->index_to_loc_format)
+ {
+ loca_long = sfnt_read_loca_table_long (fd, font);
+ if (!loca_long)
+ return 1;
+
+ fprintf (stderr, "long loca table has %zu glyphs\n",
+ loca_long->num_offsets);
+ }
+ else
+ {
+ loca_short = sfnt_read_loca_table_short (fd, font);
+ if (!loca_short)
+ return 1;
+
+ fprintf (stderr, "short loca table has %zu glyphs\n",
+ loca_short->num_offsets);
+ }
+
+ interpreter = sfnt_make_interpreter (maxp, cvt, head, fvar,
+ FANCY_PPEM, FANCY_PPEM);
+ if (instance && gvar)
+ sfnt_vary_interpreter (interpreter, &blend);
+
+ if (!interpreter)
+ exit (1);
+
+ if (fpgm)
+ {
+ fprintf (stderr, "interpreting the font program, with"
+ " %zu instructions\n", fpgm->num_instructions);
+ trap = sfnt_interpret_font_program (interpreter, fpgm);
+
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
+ }
+
+ if (prep)
+ {
+ fprintf (stderr, "interpreting the control value program, with"
+ " %zu instructions\n", prep->num_instructions);
+ trap = sfnt_interpret_control_value_program (interpreter, prep,
+ &state);
+
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
+ }
+
+ state = interpreter->state;
+
+ advances = alloca (sizeof *advances * length);
+ rasters = alloca (sizeof *rasters * length);
+
+ for (i = 0; i < length; ++i)
+ {
+ code = sfnt_lookup_glyph (fancy[i], data[0]);
+
+ if (!code)
+ exit (2);
+
+ glyph = sfnt_read_glyph (code, glyf, loca_short,
+ loca_long);
+
+ if (!glyph || !glyph->simple)
+ exit (3);
+
+ if (instance && gvar)
+ sfnt_vary_simple_glyph (&blend, code, glyph,
+ &distortion);
+
+ if (sfnt_lookup_glyph_metrics (code, -1,
+ &metrics,
+ hmtx, hhea,
+ head, maxp))
+ exit (4);
+
+ interpreter->state = state;
+ trap = sfnt_interpret_simple_glyph (glyph, interpreter,
+ &metrics, &value);
+
+ if (trap)
+ {
+ fprintf (stderr, "*TRAP*: %s\n", trap);
+ exit (5);
+ }
+
+ outline = sfnt_build_instructed_outline (value);
+
+ if (!outline)
+ exit (6);
+
+ xfree (value);
+
+ raster = sfnt_raster_glyph_outline (outline);
+
+ if (!raster)
+ exit (7);
+
+ xfree (outline);
+
+ rasters[i] = raster;
+ advances[i] = (sfnt_mul_fixed (metrics.advance, scale)
+ + sfnt_mul_fixed (distortion.advance, scale));
+ }
+
+ sfnt_x_raster (rasters, advances, length, hhea, scale);
+ exit (0);
+ }
+
+ if (hhea && maxp)
+ hmtx = sfnt_read_hmtx_table (fd, font, hhea, maxp);
+
+ if (maxp)
+ fprintf (stderr, "maxp says num glyphs is %"PRIu16"\n",
+ maxp->num_glyphs);
+
+ if (name)
+ {
+ fprintf (stderr, "name table of format: %"PRIu16" count: %"
+ PRIu16"\n", name->format, name->count);
+
+ string = sfnt_find_name (name, SFNT_NAME_FONT_FAMILY,
+ &record);
+
+ if (string)
+ fprintf (stderr, "FONT_FAMILY: %"PRIu16", %"PRIu16"\n",
+ record.platform_id, record.length);
+ }
+
+ if (meta)
+ {
+ 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;
+
+ if (head)
+ {
+ fprintf (stderr, "HEAD table:\n"
+ "version: \t\t\t%g\n"
+ "revision: \t\t\t%g\n"
+ "checksum_adjustment: \t\t%"PRIu32"\n"
+ "magic: \t\t\t\t%"PRIx32"\n"
+ "flags: \t\t\t\t%"PRIx16"\n"
+ "units_per_em: \t\t\t%"PRIu16"\n"
+ "xmin, ymin, xmax, ymax: \t%d, %d, %d, %d\n"
+ "mac_style: \t\t\t%"PRIx16"\n"
+ "lowest_rec_ppem: \t\t%"PRIu16"\n"
+ "font_direction_hint: \t\t%"PRIi16"\n"
+ "index_to_loc_format: \t\t%"PRIi16"\n"
+ "glyph_data_format: \t\t%"PRIi16"\n",
+ sfnt_coerce_fixed (head->version),
+ sfnt_coerce_fixed (head->revision),
+ head->checksum_adjustment,
+ head->magic,
+ head->flags,
+ head->units_per_em,
+ (int) head->xmin,
+ (int) head->ymin,
+ (int) head->xmax,
+ (int) head->ymax,
+ head->mac_style,
+ head->lowest_rec_ppem,
+ head->font_direction_hint,
+ head->index_to_loc_format,
+ head->glyph_data_format);
+
+ if (head->index_to_loc_format)
+ {
+ loca_long = sfnt_read_loca_table_long (fd, font);
+ if (!loca_long)
+ return 1;
+
+ fprintf (stderr, "long loca table has %zu glyphs\n",
+ loca_long->num_offsets);
+ }
+ else
+ {
+ loca_short = sfnt_read_loca_table_short (fd, font);
+ if (!loca_short)
+ return 1;
+
+ fprintf (stderr, "short loca table has %zu glyphs\n",
+ loca_short->num_offsets);
+ }
+ }
+
+ if (hhea)
+ fprintf (stderr, "HHEA table:\n"
+ "version: \t\t\t%g\n"
+ "ascent, descent: \t\t%d %d\n"
+ "line_gap: \t\t\t%d\n"
+ "advance_width_max: \t\t%u\n"
+ "min_lsb: \t\t\t%d\n"
+ "min_rsb: \t\t\t%d\n"
+ "caret_srise: \t\t\t%d\n"
+ "caret_srun: \t\t\t%d\n",
+ sfnt_coerce_fixed (hhea->version),
+ (int) hhea->ascent,
+ (int) hhea->descent,
+ (int) hhea->line_gap,
+ (unsigned int) hhea->advance_width_max,
+ (int) hhea->min_left_side_bearing,
+ (int) hhea->min_right_side_bearing,
+ (int) hhea->caret_slope_rise,
+ (int) hhea->caret_slope_run);
+
+ if (head && maxp && maxp->version >= 0x00010000)
+ {
+ fprintf (stderr, "creating interpreter\n"
+ "the size of the stack is %"PRIu16"\n"
+ "the size of the twilight zone is %"PRIu16"\n"
+ "the size of the storage area is %"PRIu16"\n"
+ "there are at most %"PRIu16" idefs\n"
+ "there are at most %"PRIu16" fdefs\n"
+ "the cvt is %zu fwords in length\n",
+ maxp->max_stack_elements,
+ maxp->max_twilight_points,
+ maxp->max_storage,
+ maxp->max_instruction_defs,
+ maxp->max_function_defs,
+ cvt ? cvt->num_elements : 0ul);
+
+ interpreter = sfnt_make_interpreter (maxp, cvt, head,
+ fvar, FANCY_PPEM,
+ FANCY_PPEM);
+ state = interpreter->state;
+
+ if (instance && gvar)
+ sfnt_vary_interpreter (interpreter, &blend);
+
+ if (fpgm)
+ {
+ fprintf (stderr, "interpreting the font program, with"
+ " %zu instructions\n", fpgm->num_instructions);
+
+ trap = sfnt_interpret_font_program (interpreter, fpgm);
+
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
+ }
+
+ if (prep)
+ {
+ fprintf (stderr, "interpreting the control value program, with"
+ " %zu instructions\n", prep->num_instructions);
+
+ trap = sfnt_interpret_control_value_program (interpreter, prep,
+ &state);
+
+ if (trap)
+ fprintf (stderr, "**TRAP**: %s\n", trap);
+ }
+ }
+
+ while (true)
+ {
+ printf ("table, character? ");
+
+ if (scanf ("%d %"SCNu32"", &i, &character) == EOF)
+ break;
+
+ if (i < 0 || i >= table->num_subtables)
+ {
+ printf ("table out of range\n");
+ continue;
+ }
+
+ if (!data[i])
+ {
+ printf ("table not present\n");
+ continue;
+ }
+
+ code = sfnt_lookup_glyph (character, data[i]);
+ printf ("glyph is %"PRIu32"\n", code);
+
+ if ((loca_long || loca_short) && glyf)
+ {
+ scale = sfnt_div_fixed (EASY_PPEM, head->units_per_em);
+ glyph = sfnt_read_glyph (code, glyf, loca_short,
+ loca_long);
+
+ if (glyph)
+ {
+ printf ("glyph is: %s\n",
+ glyph->simple ? "simple" : "compound");
+
+ dcontext.glyf = glyf;
+ dcontext.loca_short = loca_short;
+ dcontext.loca_long = loca_long;
+
+ if (instance && gvar)
+ dcontext.blend = &blend;
+ else
+ dcontext.blend = NULL;
+
+ if (glyph->simple && instance && gvar)
+ {
+ printf ("applying variations to simple glyph...\n");
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+ if (sfnt_vary_simple_glyph (&blend, code, glyph,
+ &distortion))
+ printf ("variation failed!\n");
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub = timespec_sub (end, start);
+
+ printf ("time spent varying: %lld sec %ld nsec\n",
+ (long long) sub.tv_sec, sub.tv_nsec);
+ printf ("distortions: %"PRIi16", %"PRIi16"\n",
+ distortion.origin, distortion.advance);
+ }
+ else if (instance && gvar)
+ {
+ printf ("applying variations to compound glyph...\n");
+
+ if (sfnt_vary_compound_glyph (&blend, code, glyph,
+ &distortion))
+ printf ("variation failed!\n");
+ }
+
+ if (sfnt_decompose_glyph (glyph, sfnt_test_move_to,
+ sfnt_test_line_to,
+ sfnt_test_curve_to,
+ sfnt_test_get_glyph,
+ sfnt_test_free_glyph,
+ &dcontext))
+ printf ("decomposition failure\n");
+
+ if (sfnt_lookup_glyph_metrics (code, -1,
+ &metrics,
+ hmtx, hhea,
+ head, maxp))
+ {
+ printf ("metrics lookup failure");
+ memset (&metrics, 0, sizeof metrics);
+ }
+
+ /* Time this important bit. */
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+ outline = sfnt_build_glyph_outline (glyph, scale,
+ &metrics,
+ sfnt_test_get_glyph,
+ sfnt_test_free_glyph,
+ &dcontext);
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub = timespec_sub (end, start);
+ memset (&sub1, 0, sizeof sub1);
+
+ if (outline)
+ {
+ fprintf (stderr, "outline origin, rbearing: %"
+ PRIi32" %"PRIi32"\n",
+ outline->origin,
+ outline->ymax - outline->origin);
+ sfnt_test_max = outline->ymax - outline->ymin;
+
+ for (i = 0; i < outline->outline_used; i++)
+ printf ("ctx.%s (%g, %g) /* %g, %g */\n",
+ ((outline->outline[i].flags
+ & SFNT_GLYPH_OUTLINE_LINETO)
+ ? "lineTo" : "moveTo"),
+ sfnt_coerce_fixed (outline->outline[i].x
+ - outline->xmin),
+ sfnt_coerce_fixed (sfnt_test_max
+ - (outline->outline[i].y
+ - outline->ymin)),
+ sfnt_coerce_fixed (outline->outline[i].x
+ - outline->xmin),
+ sfnt_coerce_fixed (outline->outline[i].y
+ - outline->ymin));
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+ sfnt_build_outline_edges (outline, sfnt_test_edge_ignore,
+ NULL);
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub1 = timespec_sub (end, start);
+
+ sfnt_build_outline_edges (outline, sfnt_test_edge,
+ NULL);
+
+ raster = NULL;
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+
+ for (i = 0; i < 120; ++i)
+ {
+ xfree (raster);
+ raster = sfnt_raster_glyph_outline (outline);
+ }
+
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub2 = timespec_sub (end, start);
+
+ /* Print out the raster. */
+ sfnt_test_raster (raster, hhea, scale);
+ printf ("raster offsets: %d, %d\n",
+ raster->offx, raster->offy);
+
+ xfree (raster);
+
+ printf ("outline bounds: %g %g, %g %g\n",
+ sfnt_coerce_fixed (outline->xmin),
+ sfnt_coerce_fixed (outline->ymin),
+ sfnt_coerce_fixed (outline->xmax),
+ sfnt_coerce_fixed (outline->ymax));
+ }
+
+ if (hmtx && head)
+ {
+ if (!sfnt_lookup_glyph_metrics (code, EASY_PPEM,
+ &metrics,
+ hmtx, hhea,
+ head, maxp))
+ printf ("lbearing, advance: %g, %g\n",
+ sfnt_coerce_fixed (metrics.lbearing),
+ sfnt_coerce_fixed (metrics.advance));
+
+ if (interpreter)
+ {
+ if (getenv ("SFNT_DEBUG"))
+ interpreter->run_hook = sfnt_run_hook;
+ else if (getenv ("SFNT_VERBOSE"))
+ {
+ interpreter->run_hook = sfnt_verbose;
+ interpreter->push_hook = sfnt_push_hook;
+ interpreter->pop_hook = sfnt_pop_hook;
+ }
+
+ if (!sfnt_lookup_glyph_metrics (code, -1,
+ &metrics,
+ hmtx, hhea,
+ head, maxp))
+ {
+ printf ("interpreting glyph\n");
+ interpreter->state = state;
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &start);
+ if (glyph->simple)
+ trap
+ = sfnt_interpret_simple_glyph (glyph,
+ interpreter,
+ &metrics,
+ &value);
+ else
+#define GG sfnt_test_get_glyph
+#define FG sfnt_test_free_glyph
+ trap
+ = sfnt_interpret_compound_glyph (glyph,
+ interpreter,
+ &state,
+ GG, FG,
+ hmtx, hhea,
+ maxp,
+ &metrics,
+ &dcontext,
+ &value);
+#undef GG
+#undef FG
+ clock_gettime (CLOCK_THREAD_CPUTIME_ID, &end);
+ sub3 = timespec_sub (end, start);
+
+ if (trap)
+ printf ("**TRAP**: %s\n", trap);
+ else
+ {
+ printf ("rasterizing instructed outline\n");
+ if (outline)
+ xfree (outline);
+ outline = sfnt_build_instructed_outline (value);
+ xfree (value);
+
+ if (outline)
+ {
+ raster = sfnt_raster_glyph_outline (outline);
+
+ if (raster)
+ {
+ sfnt_test_raster (raster, hhea, scale);
+ printf ("raster offsets: %d, %d\n",
+ raster->offx, raster->offy);
+ xfree (raster);
+ }
+ }
+ }
+
+ fprintf (stderr, "execution time: %lld sec %ld nse"
+ "c\n",
+ (long long) sub3.tv_sec, sub3.tv_nsec);
+ }
+
+ interpreter->run_hook = NULL;
+ }
+ }
+
+ printf ("time spent outlining: %lld sec %ld nsec\n",
+ (long long) sub.tv_sec, sub.tv_nsec);
+ printf ("time spent building edges: %lld sec %ld nsec\n",
+ (long long) sub1.tv_sec, sub1.tv_nsec);
+ printf ("time spent rasterizing: %lld sec %ld nsec\n",
+ (long long) sub2.tv_sec / 120, sub2.tv_nsec / 120);
+
+ xfree (outline);
+ }
+
+ sfnt_free_glyph (glyph);
+ }
+ }
+
+ free_lab:
+
+ xfree (font);
+
+ for (i = 0; i < table->num_subtables; ++i)
+ xfree (data[i]);
+
+ if (instance && gvar)
+ sfnt_free_blend (&blend);
+
+ xfree (table);
+ xfree (data);
+ xfree (subtables);
+ xfree (head);
+ xfree (hhea);
+ xfree (loca_long);
+ xfree (loca_short);
+ xfree (glyf);
+ xfree (maxp);
+ xfree (hmtx);
+ xfree (name);
+ xfree (meta);
+ xfree (ttc);
+ xfree (cvt);
+ xfree (fpgm);
+ xfree (interpreter);
+ xfree (prep);
+ xfree (fvar);
+ xfree (gvar);
+ xfree (avar);
+ xfree (cvar);
+
+ return 0;
+}
+
+#endif
diff --git a/src/sfnt.h b/src/sfnt.h
new file mode 100644
index 00000000000..365595fa37d
--- /dev/null
+++ b/src/sfnt.h
@@ -0,0 +1,1994 @@
+/* sfnt format font support for GNU Emacs.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _SFNT_H_
+#define _SFNT_H_
+
+#include <stdint.h>
+#include <stddef.h>
+#include <setjmp.h>
+
+#include <sys/types.h>
+
+
+
+/* Container structure and enumerator definitions. */
+
+/* The sfnt container format is organized into different tables, such
+ as ``cmap'' or ``glyf''. Each of these tables has a specific
+ format and use. These are all the tables known to Emacs. */
+
+enum sfnt_table
+ {
+ SFNT_TABLE_CMAP,
+ SFNT_TABLE_GLYF,
+ SFNT_TABLE_HEAD,
+ SFNT_TABLE_HHEA,
+ SFNT_TABLE_HMTX,
+ SFNT_TABLE_LOCA,
+ SFNT_TABLE_MAXP,
+ SFNT_TABLE_NAME,
+ SFNT_TABLE_META,
+ SFNT_TABLE_CVT ,
+ SFNT_TABLE_FPGM,
+ SFNT_TABLE_PREP,
+ SFNT_TABLE_FVAR,
+ SFNT_TABLE_GVAR,
+ SFNT_TABLE_CVAR,
+ SFNT_TABLE_AVAR,
+ };
+
+#define SFNT_ENDOF(type, field, type1) \
+ ((size_t) offsetof (type, field) + sizeof (type1))
+
+/* Each of these structures must be aligned so that no compiler will
+ ever generate padding bytes on platforms where the alignment
+ requirements for uint32_t and uint16_t are no larger than 4 and 2
+ bytes respectively.
+
+ Pointer types are assumed to impose an alignmnent requirement no
+ less than that of uint32_t.
+
+ If a table has more than one kind of variable-length subtable array
+ at the end, make sure to pad subsequent subtables
+ appropriately. */
+
+struct sfnt_offset_subtable
+{
+ /* The scaler type. */
+ uint32_t scaler_type;
+
+ /* The number of tables. */
+ uint16_t num_tables;
+
+ /* (Maximum power of 2 <= numTables) * 16. */
+ uint16_t search_range;
+
+ /* log2 (maximum power of 2 <= numTables) */
+ uint16_t entry_selector;
+
+ /* numTables * 16 - searchRange. */
+ uint16_t range_shift;
+
+ /* Variable length data. */
+ struct sfnt_table_directory *subtables;
+};
+
+/* The table directory. Follows the offset subtable, with one for
+ each table. */
+
+struct sfnt_table_directory
+{
+ /* 4-byte identifier for each table. See sfnt_table_names. */
+ uint32_t tag;
+
+ /* Table checksum. */
+ uint32_t checksum;
+
+ /* Offset from the start of the file. */
+ uint32_t offset;
+
+ /* Length of the table in bytes, not subject to padding. */
+ uint32_t length;
+};
+
+enum sfnt_scaler_type
+ {
+ SFNT_SCALER_TRUE = 0x74727565,
+ SFNT_SCALER_VER1 = 0x00010000,
+ SFNT_SCALER_TYP1 = 0x74797031,
+ SFNT_SCALER_OTTO = 0x4F54544F,
+ };
+
+typedef int32_t sfnt_fixed;
+typedef int16_t sfnt_fword;
+typedef uint16_t sfnt_ufword;
+
+#define sfnt_coerce_fixed(fixed) ((sfnt_fixed) (fixed) / 65535.0)
+
+typedef unsigned int sfnt_glyph;
+typedef unsigned int sfnt_char;
+
+struct sfnt_head_table
+{
+ /* The version. This is a 16.16 fixed point number. */
+ sfnt_fixed version;
+
+ /* The revision. */
+ sfnt_fixed revision;
+
+ /* Checksum adjustment. */
+ uint32_t checksum_adjustment;
+
+ /* Magic number, should be 0x5F0F3CF5. */
+ uint32_t magic;
+
+ /* Flags for the font. */
+ uint16_t flags;
+
+ /* Units per em. */
+ uint16_t units_per_em;
+
+ /* Time of creation. */
+ uint32_t created_high, created_low;
+
+ /* Time of modification. */
+ uint32_t modified_high, modified_low;
+
+ /* Minimum bounds. */
+ sfnt_fword xmin, ymin, xmax, ymax;
+
+ /* Mac specific stuff. */
+ uint16_t mac_style;
+
+ /* Smallest readable size in pixels. */
+ uint16_t lowest_rec_ppem;
+
+ /* Font direction hint. */
+ int16_t font_direction_hint;
+
+ /* Index to loc format. 0 for short offsets, 1 for long. */
+ int16_t index_to_loc_format;
+
+ /* Unused. */
+ int16_t glyph_data_format;
+};
+
+struct sfnt_hhea_table
+{
+ /* The version. This is a 16.16 fixed point number. */
+ sfnt_fixed version;
+
+ /* The maximum ascent and descent values for this font. */
+ sfnt_fword ascent, descent;
+
+ /* The typographic line gap. */
+ sfnt_fword line_gap;
+
+ /* The maximum advance width. */
+ sfnt_ufword advance_width_max;
+
+ /* The minimum bearings on either side. */
+ sfnt_fword min_left_side_bearing, min_right_side_bearing;
+
+ /* The maximum extent. */
+ sfnt_fword x_max_extent;
+
+ /* Caret slope. */
+ int16_t caret_slope_rise, caret_slope_run;
+
+ /* Caret offset for non slanted fonts. */
+ sfnt_fword caret_offset;
+
+ /* Reserved values. */
+ int16_t reserved1, reserved2, reserved3, reserved4;
+
+ /* Should always be zero. */
+ int16_t metric_data_format;
+
+ /* Number of advanced widths in metrics table. */
+ uint16_t num_of_long_hor_metrics;
+};
+
+struct sfnt_cmap_table
+{
+ /* Should be zero. */
+ uint16_t version;
+
+ /* Number of subtables. */
+ uint16_t num_subtables;
+};
+
+enum sfnt_platform_id
+ {
+ SFNT_PLATFORM_UNICODE = 0,
+ SFNT_PLATFORM_MACINTOSH = 1,
+ SFNT_PLATFORM_RESERVED = 2,
+ SFNT_PLATFORM_MICROSOFT = 3,
+ };
+
+enum sfnt_unicode_platform_specific_id
+ {
+ SFNT_UNICODE_1_0 = 0,
+ SFNT_UNICODE_1_1 = 1,
+ SFNT_UNICODE_ISO_10646_1993 = 2,
+ SFNT_UNICODE_2_0_BMP = 3,
+ SFNT_UNICODE_2_0 = 4,
+ SFNT_UNICODE_VARIATION_SEQUENCES = 5,
+ SFNT_UNICODE_LAST_RESORT = 6,
+ };
+
+enum sfnt_macintosh_platform_specific_id
+ {
+ SFNT_MACINTOSH_ROMAN = 0,
+ SFNT_MACINTOSH_JAPANESE = 1,
+ SFNT_MACINTOSH_TRADITIONAL_CHINESE = 2,
+ SFNT_MACINTOSH_KOREAN = 3,
+ SFNT_MACINTOSH_ARABIC = 4,
+ SFNT_MACINTOSH_HEBREW = 5,
+ SFNT_MACINTOSH_GREEK = 6,
+ SFNT_MACINTOSH_RUSSIAN = 7,
+ SFNT_MACINTOSH_RSYMBOL = 8,
+ SFNT_MACINTOSH_DEVANGARI = 9,
+ SFNT_MACINTOSH_GURMUKHI = 10,
+ SFNT_MACINTOSH_GUJARATI = 11,
+ SFNT_MACINTOSH_ORIYA = 12,
+ SFNT_MACINTOSH_BENGALI = 13,
+ SFNT_MACINTOSH_TAMIL = 14,
+ SFNT_MACINTOSH_TELUGU = 15,
+ SFNT_MACINTOSH_KANNADA = 16,
+ SFNT_MACINTOSH_MALAYALAM = 17,
+ SFNT_MACINTOSH_SINHALESE = 18,
+ SFNT_MACINTOSH_BURMESE = 19,
+ SFNT_MACINTOSH_KHMER = 20,
+ SFNT_MACINTOSH_THAI = 21,
+ SFNT_MACINTOSH_LAOTIAN = 22,
+ SFNT_MACINTOSH_GEORGIAN = 23,
+ SFNT_MACINTOSH_ARMENIAN = 24,
+ SFNT_MACINTOSH_SIMPLIFIED_CHINESE = 25,
+ SFNT_MACINTOSH_TIBETIAN = 26,
+ SFNT_MACINTOSH_MONGOLIAN = 27,
+ SFNT_MACINTOSH_GEEZ = 28,
+ SFNT_MACINTOSH_SLAVIC = 29,
+ SFNT_MACINTOSH_VIETNAMESE = 30,
+ SFNT_MACINTOSH_SINDHI = 31,
+ SFNT_MACINTOSH_UNINTERPRETED = 32,
+ };
+
+enum sfnt_microsoft_platform_specific_id
+ {
+ SFNT_MICROSOFT_SYMBOL = 0,
+ SFNT_MICROSOFT_UNICODE_BMP = 1,
+ SFNT_MICROSOFT_SHIFT_JIS = 2,
+ SFNT_MICROSOFT_PRC = 3,
+ SFNT_MICROSOFT_BIG_FIVE = 4,
+ SFNT_MICROSOFT_WANSUNG = 5,
+ SFNT_MICROSOFT_JOHAB = 6,
+ SFNT_MICROSOFT_UNICODE_UCS_4 = 10,
+ };
+
+struct sfnt_cmap_encoding_subtable
+{
+ /* The platform ID. */
+ uint16_t platform_id;
+
+ /* Platform specific ID. */
+ uint16_t platform_specific_id;
+
+ /* Mapping table offset. */
+ uint32_t offset;
+};
+
+struct sfnt_cmap_encoding_subtable_data
+{
+ /* Format and possibly the length in bytes. */
+ uint16_t format, length;
+};
+
+struct sfnt_cmap_format_0
+{
+ /* Format, set to 0. */
+ uint16_t format;
+
+ /* Length in bytes. Should be 262. */
+ uint16_t length;
+
+ /* Language code. */
+ uint16_t language;
+
+ /* Character code to glyph index map. */
+ uint8_t glyph_index_array[256];
+};
+
+struct sfnt_cmap_format_2_subheader
+{
+ uint16_t first_code;
+ uint16_t entry_count;
+ int16_t id_delta;
+ uint16_t id_range_offset;
+};
+
+struct sfnt_cmap_format_2
+{
+ /* Format, set to 2. */
+ uint16_t format;
+
+ /* Length in bytes. */
+ uint16_t length;
+
+ /* Language code. */
+ uint16_t language;
+
+ /* Array mapping high bytes to subheaders. */
+ uint16_t sub_header_keys[256];
+
+ /* Variable length data. */
+ struct sfnt_cmap_format_2_subheader *subheaders;
+ uint16_t *glyph_index_array;
+ uint16_t num_glyphs;
+};
+
+struct sfnt_cmap_format_4
+{
+ /* Format, set to 4. */
+ uint16_t format;
+
+ /* Length in bytes. */
+ uint16_t length;
+
+ /* Language code. */
+ uint16_t language;
+
+ /* 2 * seg_count. */
+ uint16_t seg_count_x2;
+
+ /* 2 * (2**FLOOR(log2(segCount))) */
+ uint16_t search_range;
+
+ /* log2(searchRange/2) */
+ uint16_t entry_selector;
+
+ /* Variable-length data. */
+ uint16_t *end_code;
+ uint16_t *reserved_pad;
+ uint16_t *start_code;
+ int16_t *id_delta;
+ int16_t *id_range_offset;
+ uint16_t *glyph_index_array;
+
+ /* The number of elements in glyph_index_array. */
+ size_t glyph_index_size;
+};
+
+struct sfnt_cmap_format_6
+{
+ /* Format, set to 6. */
+ uint16_t format;
+
+ /* Length in bytes. */
+ uint16_t length;
+
+ /* Language code. */
+ uint16_t language;
+
+ /* First character code in subrange. */
+ uint16_t first_code;
+
+ /* Number of character codes. */
+ uint16_t entry_count;
+
+ /* Variable-length data. */
+ uint16_t *glyph_index_array;
+};
+
+struct sfnt_cmap_format_8_or_12_group
+{
+ uint32_t start_char_code;
+ uint32_t end_char_code;
+ uint32_t start_glyph_code;
+};
+
+struct sfnt_cmap_format_8
+{
+ /* Format, set to 8. */
+ uint16_t format;
+
+ /* Reserved. */
+ uint16_t reserved;
+
+ /* Length in bytes. */
+ uint32_t length;
+
+ /* Language code. */
+ uint32_t language;
+
+ /* Tightly packed array of bits (8K bytes total) indicating whether
+ the particular 16-bit (index) value is the start of a 32-bit
+ character code. */
+ uint8_t is32[65536];
+
+ /* Number of groups. */
+ uint32_t num_groups;
+
+ /* Variable length data. */
+ struct sfnt_cmap_format_8_or_12_group *groups;
+};
+
+/* cmap formats 10, 13 unsupported. */
+
+struct sfnt_cmap_format_12
+{
+ /* Format, set to 12. */
+ uint16_t format;
+
+ /* Reserved. */
+ uint16_t reserved;
+
+ /* Length in bytes. */
+ uint32_t length;
+
+ /* Language code. */
+ uint32_t language;
+
+ /* Number of groups. */
+ uint32_t num_groups;
+
+ /* Variable length data. */
+ struct sfnt_cmap_format_8_or_12_group *groups;
+};
+
+struct sfnt_cmap_format_14
+{
+ /* Format, set to 14. */
+ uint16_t format;
+
+ /* The length of the table in bytes. */
+ uint32_t length;
+
+ /* Number of variation selector records. */
+ uint16_t num_var_selector_records;
+
+ /* The offset of this table in the font file. */
+ off_t offset;
+
+ /* Variable length data. */
+ struct sfnt_variation_selector_record *records;
+};
+
+struct sfnt_variation_selector_record
+{
+ /* 24-bit unsigned variation selector. */
+ unsigned int var_selector;
+
+ /* Offset to default UVS table. */
+ uint32_t default_uvs_offset;
+
+ /* Offset to non-default UVS table. */
+ uint32_t nondefault_uvs_offset;
+};
+
+struct sfnt_maxp_table
+{
+ /* Table version. */
+ sfnt_fixed version;
+
+ /* The number of glyphs in this font - 1. Set at version 0.5 or
+ later. */
+ uint16_t num_glyphs;
+
+ /* These fields are only set in version 1.0 or later. Maximum
+ points in a non-composite glyph. */
+ uint16_t max_points;
+
+ /* Maximum contours in a non-composite glyph. */
+ uint16_t max_contours;
+
+ /* Maximum points in a composite glyph. */
+ uint16_t max_composite_points;
+
+ /* Maximum contours in a composite glyph. */
+ uint16_t max_composite_contours;
+
+ /* 1 if instructions do not use the twilight zone (Z0), or 2 if
+ instructions do use Z0; should be set to 2 in most cases. */
+ uint16_t max_zones;
+
+ /* Maximum points used in Z0. */
+ uint16_t max_twilight_points;
+
+ /* Number of Storage Area locations. */
+ uint16_t max_storage;
+
+ /* Number of FDEFs, equal to the highest function number + 1. */
+ uint16_t max_function_defs;
+
+ /* Number of IDEFs. */
+ uint16_t max_instruction_defs;
+
+ /* Maximum stack depth across Font Program ('fpgm' table), CVT
+ Program ('prep' table) and all glyph instructions (in the 'glyf'
+ table). */
+ uint16_t max_stack_elements;
+
+ /* Maximum byte count for glyph instructions. */
+ uint16_t max_size_of_instructions;
+
+ /* Maximum number of components referenced at ``top level'' for any
+ composite glyph. */
+ uint16_t max_component_elements;
+
+ /* Maximum levels of recursion; 1 for simple components. */
+ uint16_t max_component_depth;
+};
+
+struct sfnt_loca_table_short
+{
+ /* Offsets to glyph data divided by two. */
+ uint16_t *offsets;
+
+ /* Size of the offsets list. */
+ size_t num_offsets;
+};
+
+struct sfnt_loca_table_long
+{
+ /* Offsets to glyph data. */
+ uint32_t *offsets;
+
+ /* Size of the offsets list. */
+ size_t num_offsets;
+};
+
+struct sfnt_glyf_table
+{
+ /* Size of the glyph data. */
+ size_t size;
+
+ /* Pointer to possibly unaligned glyph data. */
+ unsigned char *glyphs;
+
+ /* Pointer to the start of the mapping.
+ Only initialized if this table was mmapped. */
+ unsigned char *start;
+};
+
+struct sfnt_simple_glyph
+{
+ /* The total number of points in this glyph. */
+ size_t number_of_points;
+
+ /* Array containing the last points of each contour. */
+ uint16_t *restrict end_pts_of_contours;
+
+ /* Total number of bytes needed for instructions. */
+ uint16_t instruction_length;
+
+ /* Instruction data. */
+ uint8_t *restrict instructions;
+
+ /* Array of flags. */
+ uint8_t *restrict flags;
+
+ /* Array of X coordinates. */
+ int16_t *restrict x_coordinates;
+
+ /* Array of Y coordinates. */
+ int16_t *restrict y_coordinates;
+
+ /* Pointer to the end of that array. */
+ int16_t *restrict y_coordinates_end;
+};
+
+struct sfnt_compound_glyph_component
+{
+ /* Compound glyph flags. */
+ uint16_t flags;
+
+ /* Component glyph index. */
+ uint16_t glyph_index;
+
+ /* X-offset for component or point number; type depends on bits 0
+ and 1 in component flags. */
+ union {
+ uint8_t a;
+ int8_t b;
+ uint16_t c;
+ int16_t d;
+ } argument1;
+
+ /* Y-offset for component or point number; type depends on bits 0
+ and 1 in component flags. */
+ union {
+ uint8_t a;
+ int8_t b;
+ uint16_t c;
+ int16_t d;
+ } argument2;
+
+ /* Various scale formats. */
+ union {
+ uint16_t scale;
+ struct {
+ uint16_t xscale;
+ uint16_t yscale;
+ } a;
+ struct {
+ uint16_t xscale;
+ uint16_t scale01;
+ uint16_t scale10;
+ uint16_t yscale;
+ } b;
+ } u;
+};
+
+struct sfnt_compound_glyph
+{
+ /* Pointer to array of components. */
+ struct sfnt_compound_glyph_component *components;
+
+ /* Number of elements in that array. */
+ size_t num_components;
+
+ /* Instruction data. */
+ uint8_t *instructions;
+
+ /* Length of instructions. */
+ uint16_t instruction_length;
+};
+
+struct sfnt_glyph
+{
+ /* Number of contours in this glyph. */
+ int16_t number_of_contours;
+
+ /* Coordinate bounds. */
+ sfnt_fword xmin, ymin, xmax, ymax;
+
+ /* Distortion applied to the right side phantom point. */
+ sfnt_fword advance_distortion;
+
+ /* Distortion applied to the origin point. */
+ sfnt_fword origin_distortion;
+
+ /* Either a simple glyph or a compound glyph, depending on which is
+ set. */
+ struct sfnt_simple_glyph *simple;
+ struct sfnt_compound_glyph *compound;
+};
+
+
+
+/* Glyph outline decomposition. */
+
+struct sfnt_point
+{
+ /* X and Y in em space. */
+ sfnt_fixed x, y;
+};
+
+typedef void (*sfnt_move_to_proc) (struct sfnt_point, void *);
+typedef void (*sfnt_line_to_proc) (struct sfnt_point, void *);
+typedef void (*sfnt_curve_to_proc) (struct sfnt_point,
+ struct sfnt_point,
+ void *);
+
+typedef struct sfnt_glyph *(*sfnt_get_glyph_proc) (sfnt_glyph, void *,
+ bool *);
+typedef void (*sfnt_free_glyph_proc) (struct sfnt_glyph *, void *);
+
+
+
+/* Decomposed glyph outline. */
+
+struct sfnt_glyph_outline_command
+{
+ /* Flags for this outline command. */
+ int flags;
+
+ /* X and Y position of this command. */
+ sfnt_fixed x, y;
+};
+
+/* Structure describing a single recorded outline in fixed pixel
+ space. */
+
+struct sfnt_glyph_outline
+{
+ /* Array of outlines elements. */
+ struct sfnt_glyph_outline_command *outline;
+
+ /* Size of the outline data, and how much is full. */
+ size_t outline_size, outline_used;
+
+ /* Rectangle defining bounds of the outline. Namely, the minimum
+ and maximum X and Y positions. */
+ sfnt_fixed xmin, ymin, xmax, ymax;
+
+ /* The origin point of the outline on the X axis. Value defaults to
+ 0. */
+ sfnt_fixed origin;
+
+ /* Reference count. Initially zero. */
+ short refcount;
+};
+
+enum sfnt_glyph_outline_flags
+ {
+ SFNT_GLYPH_OUTLINE_LINETO = (1 << 1),
+ };
+
+
+
+/* Glyph rasterization. */
+
+struct sfnt_raster
+{
+ /* Pointer to coverage data. */
+ unsigned char *cells;
+
+ /* Basic dimensions of the raster. */
+ unsigned short width, height;
+
+ /* Integer offset to apply to positions in the raster so that they
+ start from the origin point of the glyph. */
+ short offx, offy;
+
+ /* The raster stride. */
+ unsigned short stride;
+
+ /* Reference count. Initially zero. */
+ unsigned short refcount;
+};
+
+struct sfnt_edge
+{
+ /* Next edge in this chain. */
+ struct sfnt_edge *next;
+
+ /* Winding direction. 1 if clockwise, -1 if counterclockwise. */
+ int winding;
+
+ /* X position, top and bottom of edges. */
+ sfnt_fixed x, top, bottom;
+
+ /* Amount to move X by upon each change of Y. */
+ sfnt_fixed step_x;
+};
+
+
+
+/* Polygon rasterization constants. */
+
+enum
+ {
+ SFNT_POLY_SHIFT = 3,
+ SFNT_POLY_SAMPLE = (1 << SFNT_POLY_SHIFT),
+ SFNT_POLY_MASK = (SFNT_POLY_SAMPLE - 1),
+ SFNT_POLY_STEP = (0x10000 >> SFNT_POLY_SHIFT),
+ SFNT_POLY_START = (SFNT_POLY_STEP >> 1),
+ };
+
+
+
+/* Glyph metrics computation. */
+
+struct sfnt_long_hor_metric
+{
+ uint16_t advance_width;
+ int16_t left_side_bearing;
+};
+
+struct sfnt_hmtx_table
+{
+ /* Array of horizontal metrics for each glyph. */
+ struct sfnt_long_hor_metric *h_metrics;
+
+ /* Lbearing for remaining glyphs. */
+ int16_t *left_side_bearing;
+};
+
+/* Structure describing the metrics of a single glyph. The fields
+ mean the same as in XCharStruct, except they are 16.16 fixed point
+ values, and are missing significant information. */
+
+struct sfnt_glyph_metrics
+{
+ /* Distance between origin and left edge of raster. Positive
+ changes move rightwards.
+
+ If sfnt_lookup_glyph_metrics is given a pixel size of -1,
+ this is actually a sign extended fword. */
+ sfnt_fixed lbearing;
+
+ /* Advance to next glyph's origin.
+
+ If sfnt_lookup_glyph_metrics is given a pixel size of -1, this is
+ actually a sign extended fword. */
+ sfnt_fixed advance;
+};
+
+
+
+/* Font style parsing. */
+
+struct sfnt_name_record
+{
+ /* Platform identifier code. */
+ uint16_t platform_id;
+
+ /* Platform specific ID. */
+ uint16_t platform_specific_id;
+
+ /* Language identifier. */
+ uint16_t language_id;
+
+ /* Name identifier. */
+ uint16_t name_id;
+
+ /* String length in bytes. */
+ uint16_t length;
+
+ /* Offset from start of storage area. */
+ uint16_t offset;
+};
+
+struct sfnt_name_table
+{
+ /* Format selector of name table. */
+ uint16_t format;
+
+ /* Number of name records. */
+ uint16_t count;
+
+ /* Offset to start of string data. */
+ uint16_t string_offset;
+
+ /* Variable length data. */
+ struct sfnt_name_record *name_records;
+
+ /* Start of string data. */
+ unsigned char *data;
+};
+
+/* Name identifier codes. These are Apple's codes, not
+ Microsoft's. */
+
+enum sfnt_name_identifier_code
+ {
+ SFNT_NAME_COPYRIGHT_NOTICE = 0,
+ SFNT_NAME_FONT_FAMILY = 1,
+ SFNT_NAME_FONT_SUBFAMILY = 2,
+ SFNT_NAME_UNIQUE_SUBFAMILY_IDENTIFICATION = 3,
+ SFNT_NAME_FULL_NAME = 4,
+ SFNT_NAME_NAME_TABLE_VERSION = 5,
+ SFNT_NAME_POSTSCRIPT_NAME = 6,
+ SFNT_NAME_TRADEMARK_NOTICE = 7,
+ SFNT_NAME_MANUFACTURER_NAME = 8,
+ SFNT_NAME_DESIGNER = 9,
+ SFNT_NAME_DESCRIPTION = 10,
+ SFNT_NAME_FONT_VENDOR_URL = 11,
+ SFNT_NAME_FONT_DESIGNER_URL = 12,
+ SFNT_NAME_LICENSE_DESCRIPTION = 13,
+ SFNT_NAME_LICENSE_INFORMATION_URL = 14,
+ SFNT_NAME_PREFERRED_FAMILY = 16,
+ SFNT_NAME_PREFERRED_SUBFAMILY = 17,
+ SFNT_NAME_COMPATIBLE_FULL = 18,
+ SFNT_NAME_SAMPLE_TEXT = 19,
+ SFNT_NAME_VARIATIONS_POSTSCRIPT_NAME_PREFIX = 25,
+ };
+
+struct sfnt_meta_data_map
+{
+ /* Identifier for the tag. */
+ uint32_t tag;
+
+ /* Offset from start of table to data. */
+ uint32_t data_offset;
+
+ /* Length of the data. */
+ uint32_t data_length;
+};
+
+struct sfnt_meta_table
+{
+ /* Version of the table. Currently set to 1. */
+ uint32_t version;
+
+ /* Flags. Currently 0. */
+ uint32_t flags;
+
+ /* Offset from start of table to beginning of variable length
+ data. */
+ uint32_t data_offset;
+
+ /* Number of data maps in the table. */
+ uint32_t num_data_maps;
+
+ /* Beginning of variable length data. */
+ struct sfnt_meta_data_map *data_maps;
+
+ /* The whole table contents. */
+ unsigned char *data;
+};
+
+enum sfnt_meta_data_tag
+ {
+ SFNT_META_DATA_TAG_DLNG = 0x646c6e67,
+ SFNT_META_DATA_TAG_SLNG = 0x736c6e67,
+ };
+
+
+
+/* TrueType collection format support. */
+
+struct sfnt_ttc_header
+{
+ /* TrueType collection ID tag. */
+ uint32_t ttctag;
+
+ /* Version of the TTC header. */
+ uint32_t version;
+
+ /* Number of fonts in the TTC header. */
+ uint32_t num_fonts;
+
+ /* Array of offsets to the offset table for each font in the
+ file. */
+ uint32_t *offset_table;
+
+ /* Tag indicating that a DSIG table exists, or 0. Fields from here
+ on are only set on version 2.0 headers or later. */
+ uint32_t ul_dsig_tag;
+
+ /* Length in bytes of the signature table, or 0 if there is no
+ signature. */
+ uint32_t ul_dsig_length;
+
+ /* Offset in bytes of the dsig table from the beginning of the TTC
+ file. */
+ uint32_t ul_dsig_offset;
+};
+
+enum sfnt_ttc_tag
+ {
+ SFNT_TTC_TTCF = 0x74746366,
+ SFNT_TTC_DSIG = 0x44534947,
+ };
+
+
+
+/* Unicode Variation Sequence (UVS) support. */
+
+struct sfnt_default_uvs_table
+{
+ /* Number of ranges that follow. */
+ uint32_t num_unicode_value_ranges;
+
+ /* Variable length data. */
+ struct sfnt_unicode_value_range *ranges;
+};
+
+struct sfnt_unicode_value_range
+{
+ /* First value in this range. */
+ unsigned int start_unicode_value;
+
+ /* Number of additional values in this range. */
+ unsigned char additional_count;
+};
+
+struct sfnt_nondefault_uvs_table
+{
+ /* Number of UVS mappings which follow. */
+ uint32_t num_uvs_mappings;
+
+ /* Variable length data. */
+ struct sfnt_uvs_mapping *mappings;
+};
+
+struct sfnt_uvs_mapping
+{
+ /* Base character value. */
+ unsigned int unicode_value;
+
+ /* Glyph ID of the base character value. */
+ uint16_t base_character_value;
+};
+
+struct sfnt_mapped_variation_selector_record
+{
+ /* The variation selector. */
+ unsigned int selector;
+
+ /* Its default UVS table. */
+ struct sfnt_default_uvs_table *default_uvs;
+
+ /* Its nondefault UVS table. */
+ struct sfnt_nondefault_uvs_table *nondefault_uvs;
+};
+
+/* Structure describing a single offset to load into a variation
+ selection context. */
+
+struct sfnt_table_offset_rec
+{
+ /* The offset from the start of the font file. */
+ off_t offset;
+
+ /* Whether or not the offset points to a non-default UVS table. */
+ bool is_nondefault_table;
+
+ /* Pointer to the UVS table. */
+ void *table;
+};
+
+struct sfnt_uvs_context
+{
+ /* Number of records and tables. */
+ size_t num_records, nmemb;
+
+ /* Array of UVS tables. */
+ struct sfnt_table_offset_rec *tables;
+
+ /* Array of variation selector records mapped to
+ their corresponding tables. */
+ struct sfnt_mapped_variation_selector_record *records;
+};
+
+
+
+#if defined HAVE_MMAP && !defined TEST
+
+/* Memory mapping support. */
+
+struct sfnt_mapped_table
+{
+ /* Pointer to table data. */
+ void *data;
+
+ /* Pointer to table mapping. */
+ void *mapping;
+
+ /* Size of mapped data and size of mapping. */
+ size_t length, size;
+};
+
+#endif /* HAVE_MMAP && !TEST */
+
+
+
+/* Glyph variation support. */
+
+/* 2.14 fixed point type used to represent versors of unit
+ vectors. */
+typedef int16_t sfnt_f2dot14;
+
+/* Forward declaration used only for the distortable font stuff. */
+struct sfnt_cvt_table;
+
+struct sfnt_variation_axis
+{
+ /* The axis tag. */
+ uint32_t axis_tag;
+
+ /* The minimum style coordinate for the axis. */
+ sfnt_fixed min_value;
+
+ /* The default style coordinate for the axis. */
+ sfnt_fixed default_value;
+
+ /* The maximum style coordinate for the axis. */
+ sfnt_fixed max_value;
+
+ /* Set to zero. */
+ uint16_t flags;
+
+ /* Identifier under which this axis's name will be found in the
+ `name' table. */
+ uint16_t name_id;
+};
+
+struct sfnt_instance
+{
+ /* The instance name ID. */
+ uint16_t name_id;
+
+ /* Flags. */
+ uint16_t flags;
+
+ /* Optional PostScript name. */
+ uint16_t ps_name_id;
+
+ /* Coordinates of each defined instance. */
+ sfnt_fixed *coords;
+};
+
+struct sfnt_fvar_table
+{
+ /* Major version; should be 1. */
+ uint16_t major_version;
+
+ /* Minor version; should be 0. */
+ uint16_t minor_version;
+
+ /* Offset in bytes from the beginning of the table to the beginning
+ of the first axis data. */
+ uint16_t offset_to_data;
+
+ /* Reserved field; always 2. */
+ uint16_t count_size_pairs;
+
+ /* Number of style axes in this font. */
+ uint16_t axis_count;
+
+ /* The number of bytes in each variation axis record. Currently 20
+ bytes. */
+ uint16_t axis_size;
+
+ /* The number of named instances for the font found in the
+ instance array. */
+ uint16_t instance_count;
+
+ /* The size of each instance record. */
+ uint16_t instance_size;
+
+ /* Variable length data. */
+ struct sfnt_variation_axis *axis;
+ struct sfnt_instance *instance;
+};
+
+struct sfnt_short_frac_correspondence
+{
+ /* Value in normalized user space. */
+ sfnt_f2dot14 from_coord;
+
+ /* Value in normalized axis space. */
+ sfnt_f2dot14 to_coord;
+};
+
+struct sfnt_short_frac_segment
+{
+ /* The number of pairs for this axis. */
+ uint16_t pair_count;
+
+ /* Variable length data. */
+ struct sfnt_short_frac_correspondence *correspondence;
+};
+
+struct sfnt_avar_table
+{
+ /* The version of the table. Should be 1.0. */
+ sfnt_fixed version;
+
+ /* Number of variation axes defined in this table.
+ XXX: why is this signed? */
+ int32_t axis_count;
+
+ /* Variable length data. */
+ struct sfnt_short_frac_segment *segments;
+};
+
+struct sfnt_tuple_variation
+{
+ /* Tuple point numbers. */
+ uint16_t *points;
+
+ /* Deltas. */
+ sfnt_fword *deltas;
+
+ /* Tuple coordinates. One for each axis specified in the [gaf]var
+ tables. */
+ sfnt_f2dot14 *coordinates;
+
+ /* Intermediate start and end coordinates. */
+ sfnt_f2dot14 *restrict intermediate_start;
+
+ /* Intermediate start and end coordinates. */
+ sfnt_f2dot14 *restrict intermediate_end;
+
+ /* The number of points and deltas present.
+
+ UINT16_MAX and POINTS set to NULL means there are deltas for each
+ CVT entry. */
+ uint16_t num_points;
+};
+
+struct sfnt_cvar_table
+{
+ /* The version of this CVT variations table. */
+ sfnt_fixed version;
+
+ /* Flags. */
+ uint16_t tuple_count;
+
+ /* Offset from the beginning of the table to the tuple data. */
+ uint16_t data_offset;
+
+ /* Variable length data. */
+ struct sfnt_tuple_variation *variation;
+};
+
+struct sfnt_gvar_table
+{
+ /* Version of the glyph variations table. */
+ uint16_t version;
+
+ /* Reserved, currently 0. */
+ uint16_t reserved;
+
+ /* The number of style axes for this font. This must be the same
+ number as axisCount in the 'fvar' table. */
+ uint16_t axis_count;
+
+ /* The number of shared coordinates. */
+ uint16_t shared_coord_count;
+
+ /* Byte offset from the beginning of this table to the list of
+ shared style coordinates. */
+ uint32_t offset_to_coord;
+
+ /* The number of glyphs in this font; this should match the number
+ of the glyphs store elsewhere in the font. */
+ uint16_t glyph_count;
+
+ /* Bit-field that gives the format of the offset array that
+ follows. If the flag is 0, the type is uint16. If the flag is 1,
+ the type is unit 32. */
+ uint16_t flags;
+
+ /* Byte offset from the beginning of this table to the first glyph
+ glyphVariationData. */
+ uint32_t offset_to_data;
+
+ /* Number of bytes in the glyph variation data. */
+ size_t data_size;
+
+ /* Byte offsets from the beginning of the glyphVariationData array
+ to the glyphVariationData for each glyph in the font. The format
+ of this field is set by the flags field. */
+ union {
+ uint16_t *offset_word;
+ uint32_t *offset_long;
+ } u;
+
+ /* Other variable length data. */
+ sfnt_f2dot14 *global_coords;
+ unsigned char *glyph_variation_data;
+};
+
+/* Structure repesenting a set of axis coordinates and their
+ normalized equivalents.
+
+ To use this structure, call
+
+ sfnt_init_blend (&blend, fvar, gvar)
+
+ on a `struct sfnt_blend *', with an appropriate fvar and gvar
+ table.
+
+ Then, fill in blend.coords with the un-normalized coordinates,
+ and call
+
+ sfnt_normalize_blend (&blend)
+
+ finally, call sfnt_vary_simple_glyph and related functions. */
+
+struct sfnt_blend
+{
+ /* The fvar table. This determines the number of elements in each
+ of the arrays below. */
+ struct sfnt_fvar_table *fvar;
+
+ /* The gvar table. This provides the glyph variation data. */
+ struct sfnt_gvar_table *gvar;
+
+ /* The avar table. This provides adjustments to normalized axis
+ values, and may be NULL. */
+ struct sfnt_avar_table *avar;
+
+ /* The cvar table. This provides adjustments to CVT values, and may
+ be NULL. */
+ struct sfnt_cvar_table *cvar;
+
+ /* Un-normalized coordinates. */
+ sfnt_fixed *coords;
+
+ /* Normalized coordinates. */
+ sfnt_fixed *norm_coords;
+};
+
+struct sfnt_metrics_distortion
+{
+ /* Distortion applied to the origin point. */
+ sfnt_fword origin;
+
+ /* Distortion applied to the advance point. */
+ sfnt_fword advance;
+};
+
+
+
+#define SFNT_CEIL_FIXED(fixed) (((fixed) + 0177777) & 037777600000)
+#define SFNT_FLOOR_FIXED(fixed) ((fixed) & 037777600000)
+
+
+
+/* Function declarations. Keep these sorted by the order in which
+ they appear in sfnt.c. Keep each line no longer than 80
+ columns. */
+
+#ifndef TEST
+
+extern struct sfnt_offset_subtable *sfnt_read_table_directory (int);
+
+#define PROTOTYPE \
+ int, struct sfnt_offset_subtable *, \
+ struct sfnt_cmap_encoding_subtable **, \
+ struct sfnt_cmap_encoding_subtable_data ***
+extern struct sfnt_cmap_table *sfnt_read_cmap_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern sfnt_glyph sfnt_lookup_glyph (sfnt_char,
+ struct sfnt_cmap_encoding_subtable_data *);
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+extern struct sfnt_head_table *sfnt_read_head_table (PROTOTYPE);
+extern struct sfnt_hhea_table *sfnt_read_hhea_table (PROTOTYPE);
+extern struct sfnt_loca_table_short *sfnt_read_loca_table_short (PROTOTYPE);
+extern struct sfnt_loca_table_long *sfnt_read_loca_table_long (PROTOTYPE);
+extern struct sfnt_maxp_table *sfnt_read_maxp_table (PROTOTYPE);
+extern struct sfnt_glyf_table *sfnt_read_glyf_table (PROTOTYPE);
+
+#ifdef HAVE_MMAP
+extern struct sfnt_glyf_table *sfnt_map_glyf_table (PROTOTYPE);
+extern int sfnt_unmap_glyf_table (struct sfnt_glyf_table *);
+#endif /* HAVE_MMAP */
+#undef PROTOTYPE
+
+extern struct sfnt_glyph *sfnt_read_glyph (sfnt_glyph, struct sfnt_glyf_table *,
+ struct sfnt_loca_table_short *,
+ struct sfnt_loca_table_long *);
+extern void sfnt_free_glyph (struct sfnt_glyph *);
+
+#define PROTOTYPE \
+ struct sfnt_glyph *, \
+ sfnt_fixed, \
+ struct sfnt_glyph_metrics *, \
+ sfnt_get_glyph_proc, \
+ sfnt_free_glyph_proc, \
+ void *
+extern struct sfnt_glyph_outline *sfnt_build_glyph_outline (PROTOTYPE);
+#undef PROTOTYPE
+
+extern void sfnt_prepare_raster (struct sfnt_raster *,
+ struct sfnt_glyph_outline *);
+
+#define PROTOTYPE struct sfnt_glyph_outline *
+extern struct sfnt_raster *sfnt_raster_glyph_outline (PROTOTYPE);
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ int, \
+ struct sfnt_offset_subtable *, \
+ struct sfnt_hhea_table *, \
+ struct sfnt_maxp_table *
+extern struct sfnt_hmtx_table *sfnt_read_hmtx_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern int sfnt_lookup_glyph_metrics (sfnt_glyph, int,
+ struct sfnt_glyph_metrics *,
+ struct sfnt_hmtx_table *,
+ struct sfnt_hhea_table *,
+ struct sfnt_head_table *,
+ struct sfnt_maxp_table *);
+
+extern void sfnt_scale_metrics (struct sfnt_glyph_metrics *,
+ sfnt_fixed);
+extern sfnt_fixed sfnt_get_scale (struct sfnt_head_table *, int);
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+extern struct sfnt_name_table *sfnt_read_name_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern unsigned char *sfnt_find_name (struct sfnt_name_table *,
+ enum sfnt_name_identifier_code,
+ struct sfnt_name_record *);
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+extern struct sfnt_meta_table *sfnt_read_meta_table (PROTOTYPE);
+#undef PROTOTYPE
+
+extern char *sfnt_find_metadata (struct sfnt_meta_table *,
+ enum sfnt_meta_data_tag,
+ struct sfnt_meta_data_map *);
+
+extern struct sfnt_ttc_header *sfnt_read_ttc_header (int);
+
+
+
+#define PROTOTYPE struct sfnt_cmap_format_14 *, int
+
+extern struct sfnt_uvs_context *sfnt_create_uvs_context (PROTOTYPE);
+
+#undef PROTOTYPE
+
+extern void sfnt_free_uvs_context (struct sfnt_uvs_context *);
+
+#define PROTOTYPE struct sfnt_nondefault_uvs_table *, sfnt_char
+
+extern sfnt_glyph sfnt_variation_glyph_for_char (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+#ifdef HAVE_MMAP
+
+extern int sfnt_map_table (int, struct sfnt_offset_subtable *,
+ uint32_t, struct sfnt_mapped_table *);
+extern int sfnt_unmap_table (struct sfnt_mapped_table *);
+
+#endif /* HAVE_MMAP */
+
+
+
+extern void *sfnt_read_table (int, struct sfnt_offset_subtable *,
+ uint32_t, size_t *);
+
+
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+
+extern struct sfnt_fvar_table *sfnt_read_fvar_table (PROTOTYPE);
+extern struct sfnt_gvar_table *sfnt_read_gvar_table (PROTOTYPE);
+extern struct sfnt_avar_table *sfnt_read_avar_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ int, \
+ struct sfnt_offset_subtable *, \
+ struct sfnt_fvar_table *, \
+ struct sfnt_cvt_table *
+
+extern struct sfnt_cvar_table *sfnt_read_cvar_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+extern void sfnt_init_blend (struct sfnt_blend *,
+ struct sfnt_fvar_table *,
+ struct sfnt_gvar_table *,
+ struct sfnt_avar_table *,
+ struct sfnt_cvar_table *);
+extern void sfnt_free_blend (struct sfnt_blend *);
+extern void sfnt_normalize_blend (struct sfnt_blend *);
+
+
+
+extern int sfnt_vary_simple_glyph (struct sfnt_blend *, sfnt_glyph,
+ struct sfnt_glyph *,
+ struct sfnt_metrics_distortion *);
+extern int sfnt_vary_compound_glyph (struct sfnt_blend *, sfnt_glyph,
+ struct sfnt_glyph *,
+ struct sfnt_metrics_distortion *);
+
+#endif /* TEST */
+
+
+
+/* TrueType hinting support. */
+
+/* Structure definitions for tables used by the TrueType
+ interpreter. */
+
+struct sfnt_cvt_table
+{
+ /* Number of elements in the control value table. */
+ size_t num_elements;
+
+ /* Pointer to elements in the control value table. */
+ sfnt_fword *values;
+};
+
+struct sfnt_fpgm_table
+{
+ /* Number of instructions in the font program table. */
+ size_t num_instructions;
+
+ /* Pointer to elements in the font program table. */
+ unsigned char *instructions;
+};
+
+struct sfnt_prep_table
+{
+ /* Number of instructions in the control value program (pre-program)
+ table. */
+ size_t num_instructions;
+
+ /* Pointer to elements in the preprogram table. */
+ unsigned char *instructions;
+};
+
+
+
+/* Fixed point types used by the TrueType interpreter. */
+
+/* 26.6 fixed point type used within the interpreter. */
+typedef int32_t sfnt_f26dot6;
+
+/* 18.14 fixed point type used to calculate rounding details. */
+typedef int32_t sfnt_f18dot14;
+
+
+
+/* Interpreter execution environment. */
+
+struct sfnt_unit_vector
+{
+ /* X and Y versors of the 2d unit vector. */
+ sfnt_f2dot14 x, y;
+};
+
+struct sfnt_interpreter_definition
+{
+ /* The opcode of this instruction or function. */
+ uint16_t opcode;
+
+ /* The number of instructions. */
+ uint16_t instruction_count;
+
+ /* Pointer to instructions belonging to the definition. This
+ pointer points directly into the control value or font program.
+ Make sure both programs are kept around as long as the
+ interpreter continues to exist. */
+ unsigned char *instructions;
+};
+
+/* This structure represents a ``struct sfnt_glyph'' that has been
+ scaled to a given pixel size.
+
+ It can either contain a simple glyph, or a decomposed compound
+ glyph; instructions are interpreted for both simple glyphs, simple
+ glyph components inside a compound glyph, and compound glyphs as a
+ whole.
+
+ In addition to the glyph data itself, it also records various
+ information for the instruction interpretation process:
+
+ - ``current'' point coordinates, which have been modified
+ by the instructing process.
+
+ - two phantom points at the origin and the advance of the
+ glyph. */
+
+struct sfnt_interpreter_zone
+{
+ /* The number of points in this zone, including the two phantom
+ points at the end. */
+ size_t num_points;
+
+ /* The number of contours in this zone. */
+ size_t num_contours;
+
+ /* The end points of each contour. */
+ size_t *contour_end_points;
+
+ /* Pointer to the X axis point data. */
+ sfnt_f26dot6 *restrict x_points;
+
+ /* Pointer to the X axis current point data. */
+ sfnt_f26dot6 *restrict x_current;
+
+ /* Pointer to the Y axis point data. */
+ sfnt_f26dot6 *restrict y_points;
+
+ /* Pointer to the Y axis current point data. */
+ sfnt_f26dot6 *restrict y_current;
+
+ /* Pointer to the flags associated with this data. */
+ unsigned char *flags;
+};
+
+enum
+ {
+ /* Bits 1 stands for X_SHORT_VECTOR on disk and in the tables, but
+ this representation is not useful in memory. Inside an
+ instructed glyph, this bit is repurposed to mean that the
+ corresponding point is a phantom point. */
+ SFNT_POINT_PHANTOM = (1 << 1),
+ /* Bits 7 and 6 of a glyph point's flags is reserved. This scaler
+ uses it to mean that the point has been touched in one axis or
+ another. */
+ SFNT_POINT_TOUCHED_X = (1 << 7),
+ SFNT_POINT_TOUCHED_Y = (1 << 6),
+ SFNT_POINT_TOUCHED_BOTH = (SFNT_POINT_TOUCHED_X
+ | SFNT_POINT_TOUCHED_Y),
+ };
+
+/* This is needed because `round' below needs an interpreter
+ argument. */
+struct sfnt_interpreter;
+
+struct sfnt_graphics_state
+{
+ /* Pointer to the function used for rounding. This function is
+ asymmetric, so -0.5 rounds up to 0, not -1. It is up to the
+ caller to handle negative values.
+
+ Value is undefined unless sfnt_validate_gs has been called, and
+ the second argument may be used to provide detailed rounding
+ information (``super rounding state''.) */
+ sfnt_f26dot6 (*round) (sfnt_f26dot6, struct sfnt_interpreter *);
+
+ /* Pointer to the function used to project euclidean vectors onto
+ the projection vector. Value is the magnitude of the projected
+ vector. */
+ sfnt_f26dot6 (*project) (sfnt_f26dot6, sfnt_f26dot6,
+ struct sfnt_interpreter *);
+
+ /* Pointer to the function used to project euclidean vectors onto
+ the dual projection vector. Value is the magnitude of the
+ projected vector. */
+ sfnt_f26dot6 (*dual_project) (sfnt_f26dot6, sfnt_f26dot6,
+ struct sfnt_interpreter *);
+
+ /* Pointer to the function used to move specified points
+ along the freedom vector by a distance specified in terms
+ of the projection vector. */
+ void (*move) (sfnt_f26dot6 *restrict,
+ sfnt_f26dot6 *restrict, size_t,
+ struct sfnt_interpreter *,
+ sfnt_f26dot6, unsigned char *);
+
+ /* Dot product between the freedom and the projection vectors. */
+ sfnt_f2dot14 vector_dot_product;
+
+ /* Controls whether the sign of control value table entries will be
+ changed to match the sign of the actual distance measurement with
+ which it is compared. Setting auto flip to TRUE makes it
+ possible to control distances measured with or against the
+ projection vector with a single control value table entry. When
+ auto flip is set to FALSE, distances must be measured with the
+ projection vector. */
+ bool auto_flip;
+
+ /* Limits the regularizing effects of control value table entries to
+ cases where the difference between the table value and the
+ measurement taken from the original outline is sufficiently
+ small. */
+ sfnt_f26dot6 cvt_cut_in;
+
+ /* Establishes the base value used to calculate the range of point
+ sizes to which a given DELTAC[] or DELTAP[] instruction will
+ apply. The formulas given below are used to calculate the range
+ of the various DELTA instructions.
+
+ DELTAC1 DELTAP1 (delta_base) through (delta_base + 15)
+ DELTAC2 DELTAP2 (delta_base + 16) through (delta_base + 31)
+ DELTAC3 DELTAP3 (delta_base + 32) through (delta_base + 47)
+
+ Please keep this documentation in sync with the TrueType
+ reference manual. */
+ unsigned short delta_base;
+
+ /* Determines the range of movement and smallest magnitude of
+ movement (the step) in a DELTAC[] or DELTAP[] instruction.
+ Changing the value of the delta shift makes it possible to trade
+ off fine control of point movement for range of movement. A low
+ delta shift favors range of movement over fine control. A high
+ delta shift favors fine control over range of movement. The step
+ has the value 1/2 to the power delta shift. The range of
+ movement is calculated by taking the number of steps allowed (16)
+ and multiplying it by the step.
+
+ The legal range for delta shift is zero through six. Negative
+ values are illegal. */
+ unsigned short delta_shift;
+
+ /* A second projection vector set to a line defined by the original
+ outline location of two points. The dual projection vector is
+ used when it is necessary to measure distances from the scaled
+ outline before any instructions were executed. */
+ struct sfnt_unit_vector dual_projection_vector;
+
+ /* A unit vector that establishes an axis along which points can
+ move. */
+ struct sfnt_unit_vector freedom_vector;
+
+ /* Makes it possible to turn off instructions under some
+ circumstances. When flag 1 is set, changes to the graphics state
+ made in the control value program will be ignored. When flag is
+ 1, grid fitting instructions will be ignored. */
+ unsigned char instruct_control;
+
+ /* Makes it possible to repeat certain instructions a designated
+ number of times. The default value of one assures that unless
+ the value of loop is altered, these instructions will execute one
+ time. */
+ unsigned short loop;
+
+ /* Establishes the smallest possible value to which a distance will
+ be rounded. */
+ sfnt_f26dot6 minimum_distance;
+
+ /* A unit vector whose direction establishes an axis along which
+ distances are measured. */
+ struct sfnt_unit_vector projection_vector;
+
+ /* Determines the manner in which values are rounded. Can be set to
+ a number of predefined states or to a customized state with the
+ SROUND or S45ROUND instructions. */
+ int round_state;
+
+ /* Reference points. These reference point numbers, which together
+ with a zone designation, specify a point in either the glyph zone
+ or the twilight zone. */
+ uint16_t rp0, rp1, rp2;
+
+ /* Flags which determine whether the interpreter will activate
+ dropout control for the current glyph. */
+ int scan_control;
+
+ /* The distance difference below which the interpreter will replace
+ a CVT distance or an actual distance in favor of the single width
+ value. */
+ sfnt_f26dot6 sw_cut_in;
+
+ /* The value used in place of the control value table distance or
+ the actual distance value when the difference between that
+ distance and the single width value is less than the single width
+ cut-in. */
+ sfnt_f26dot6 single_width_value;
+
+ /* Zone pointers, which reference a zone. */
+ int zp0, zp1, zp2;
+};
+
+struct sfnt_interpreter
+{
+ /* The number of elements in the stack. */
+ uint16_t max_stack_elements;
+
+ /* The number of instructions in INSTRUCTIONS. */
+ uint16_t num_instructions;
+
+ /* Size of the storage area. */
+ uint16_t storage_size;
+
+ /* Size of the function definition area. */
+ uint16_t function_defs_size;
+
+ /* Size of the instruction definition area. */
+ uint16_t instruction_defs_size;
+
+ /* Size of the twilight zone. */
+ uint16_t twilight_zone_size;
+
+ /* The instruction pointer. This points to the instruction
+ currently being executed. */
+ int IP;
+
+ /* The current scale. */
+ sfnt_fixed scale;
+
+ /* The current ppem and point size. */
+ int ppem, point_size;
+
+ /* The execution stack. This has at most max_stack_elements
+ elements. */
+ uint32_t *stack;
+
+ /* Pointer past the top of the stack. */
+ uint32_t *SP;
+
+ /* The size of the control value table. */
+ size_t cvt_size;
+
+ /* Pointer to instructions currently being executed. */
+ unsigned char *restrict instructions;
+
+ /* The twilight zone. May not be NULL. */
+ sfnt_f26dot6 *restrict twilight_x, *restrict twilight_y;
+
+ /* The original X positions of points in the twilight zone. */
+ sfnt_f26dot6 *restrict twilight_original_x;
+
+ /* The original Y positions of points in the twilight zone.
+
+ Apple does not directly say whether or not points in the twilight
+ zone can have their original positions changed. But this is
+ implied by ``create points in the twilight zone''. */
+ sfnt_f26dot6 *restrict twilight_original_y;
+
+ /* The scaled outlines being manipulated. May be NULL. */
+ struct sfnt_interpreter_zone *glyph_zone;
+
+ /* The glyph advance width. Value is undefined unless GLYPH_ZONE is
+ set. */
+ sfnt_f26dot6 advance_width;
+
+ /* The storage area. */
+ uint32_t *storage;
+
+ /* Control value table values. */
+ sfnt_f26dot6 *cvt;
+
+ /* Function definitions. */
+ struct sfnt_interpreter_definition *function_defs;
+
+ /* Instruction definitions. */
+ struct sfnt_interpreter_definition *instruction_defs;
+
+ /* Interpreter registers. */
+ struct sfnt_graphics_state state;
+
+ /* Detailed rounding state used when state.round_state indicates
+ that fine grained rounding should be used.
+
+ PERIOD says how often a round value occurs, for numbers
+ increasing from PHASE to infinity.
+
+ THRESHOLD says when to round a value between two increasing
+ periods towards the larger period. */
+ sfnt_f26dot6 period, phase, threshold;
+
+ /* The depth of any ongoing calls. */
+ int call_depth;
+
+ /* Jump buffer for traps. */
+ jmp_buf trap;
+
+ /* What was the trap. */
+ const char *trap_reason;
+
+ /* Number of variation axes provided by this distortable font. */
+ int n_axis;
+
+ /* Normalized axis coordinates set for this distortable font. */
+ sfnt_fixed *norm_coords;
+
+#ifdef TEST
+ /* If non-NULL, function called before each instruction is
+ executed. */
+ void (*run_hook) (struct sfnt_interpreter *);
+
+ /* If non-NULL, function called before each stack element is
+ pushed. */
+ void (*push_hook) (struct sfnt_interpreter *, uint32_t);
+
+ /* If non-NULL, function called before each stack element is
+ popped. */
+ void (*pop_hook) (struct sfnt_interpreter *, uint32_t);
+#endif
+};
+
+
+
+/* Glyph hinting. */
+
+/* Structure describing a single scaled and fitted outline. */
+
+struct sfnt_instructed_outline
+{
+ /* The number of points in this contour, including the two phantom
+ points at the end. */
+ size_t num_points;
+
+ /* The number of contours in this outline. */
+ size_t num_contours;
+
+ /* The end points of each contour. */
+ size_t *contour_end_points;
+
+ /* The points of each contour, with two additional phantom points at
+ the end. */
+ sfnt_f26dot6 *restrict x_points, *restrict y_points;
+
+ /* The flags of each point. */
+ unsigned char *flags;
+};
+
+
+
+/* Functions used to read tables used by the TrueType interpreter. */
+
+#ifndef TEST
+
+#define PROTOTYPE int, struct sfnt_offset_subtable *
+
+extern struct sfnt_cvt_table *sfnt_read_cvt_table (PROTOTYPE);
+extern struct sfnt_fpgm_table *sfnt_read_fpgm_table (PROTOTYPE);
+extern struct sfnt_prep_table *sfnt_read_prep_table (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_maxp_table *, \
+ struct sfnt_cvt_table *, \
+ struct sfnt_head_table *, \
+ struct sfnt_fvar_table *, \
+ int, int
+
+extern struct sfnt_interpreter *sfnt_make_interpreter (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_interpreter *, \
+ struct sfnt_fpgm_table *
+
+extern const char *sfnt_interpret_font_program (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_interpreter *, \
+ struct sfnt_prep_table *, \
+ struct sfnt_graphics_state *
+
+extern const char *sfnt_interpret_control_value_program (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE struct sfnt_instructed_outline *
+
+extern struct sfnt_glyph_outline *sfnt_build_instructed_outline (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_glyph *, \
+ struct sfnt_interpreter *, \
+ struct sfnt_glyph_metrics *, \
+ struct sfnt_instructed_outline **
+
+extern const char *sfnt_interpret_simple_glyph (PROTOTYPE);
+
+#undef PROTOTYPE
+
+#define PROTOTYPE \
+ struct sfnt_glyph *, \
+ struct sfnt_interpreter *, \
+ struct sfnt_graphics_state *, \
+ sfnt_get_glyph_proc, \
+ sfnt_free_glyph_proc, \
+ struct sfnt_hmtx_table *, \
+ struct sfnt_hhea_table *, \
+ struct sfnt_maxp_table *, \
+ struct sfnt_glyph_metrics *, \
+ void *, \
+ struct sfnt_instructed_outline **
+
+extern const char *sfnt_interpret_compound_glyph (PROTOTYPE);
+
+#undef PROTOTYPE
+
+
+
+extern void sfnt_vary_interpreter (struct sfnt_interpreter *,
+ struct sfnt_blend *);
+
+#endif /* TEST */
+
+
+
+#endif /* _SFNT_H_ */
diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c
new file mode 100644
index 00000000000..de2a9253b57
--- /dev/null
+++ b/src/sfntfont-android.c
@@ -0,0 +1,793 @@
+/* sfnt format font driver for Android.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __aarch64__
+#include <arm_neon.h>
+#endif
+
+#include <android/api-level.h>
+#include <android/log.h>
+
+#include "androidterm.h"
+#include "sfntfont.h"
+#include "pdumper.h"
+#include "blockinput.h"
+#include "android.h"
+
+/* Structure describing a temporary buffer. */
+
+struct sfntfont_android_scanline_buffer
+{
+ /* Size of this buffer. */
+ size_t buffer_size;
+
+ /* Pointer to the buffer data. */
+ void *buffer_data;
+};
+
+/* Array of directories to search for system fonts. */
+static char *system_font_directories[] =
+ {
+ (char *) "/system/fonts",
+ (char *) "/product/fonts",
+ /* This should be filled in by init_sfntfont_android. */
+ (char[PATH_MAX]) { },
+ };
+
+/* The font cache. */
+static Lisp_Object font_cache;
+
+/* The scanline buffer. */
+static struct sfntfont_android_scanline_buffer scanline_buffer;
+
+/* The largest size of the scanline buffer since the last window
+ update. */
+static size_t max_scanline_buffer_size;
+
+
+
+/* Return a temporary buffer for storing scan lines.
+ Set BUFFER to the buffer upon success. */
+
+#ifndef __aarch64__
+
+#define GET_SCANLINE_BUFFER(buffer, height, stride) \
+ do \
+ { \
+ size_t _size; \
+ \
+ if (INT_MULTIPLY_WRAPV (height, stride, &_size)) \
+ memory_full (SIZE_MAX); \
+ \
+ if (_size < MAX_ALLOCA) \
+ (buffer) = alloca (_size); \
+ else \
+ { \
+ if (_size > scanline_buffer.buffer_size) \
+ { \
+ (buffer) \
+ = scanline_buffer.buffer_data \
+ = xrealloc (scanline_buffer.buffer_data, \
+ _size); \
+ scanline_buffer.buffer_size = _size; \
+ } \
+ else if (_size <= scanline_buffer.buffer_size) \
+ (buffer) = scanline_buffer.buffer_data; \
+ /* This is unreachable but clang says it is. */ \
+ else \
+ emacs_abort (); \
+ \
+ max_scanline_buffer_size \
+ = max (_size, max_scanline_buffer_size); \
+ } \
+ } while (false);
+
+#else
+
+#define GET_SCANLINE_BUFFER(buffer, height, stride) \
+ do \
+ { \
+ size_t _size; \
+ void *_temp; \
+ \
+ if (INT_MULTIPLY_WRAPV (height, stride, &_size)) \
+ memory_full (SIZE_MAX); \
+ \
+ if (_size > scanline_buffer.buffer_size) \
+ { \
+ if (posix_memalign (&_temp, 16, _size)) \
+ memory_full (_size); \
+ free (scanline_buffer.buffer_data); \
+ (buffer) \
+ = scanline_buffer.buffer_data \
+ = _temp; \
+ scanline_buffer.buffer_size = _size; \
+ } \
+ else if (_size <= scanline_buffer.buffer_size) \
+ (buffer) = scanline_buffer.buffer_data; \
+ /* This is unreachable but clang says it is. */ \
+ else \
+ emacs_abort (); \
+ \
+ max_scanline_buffer_size \
+ = max (_size, max_scanline_buffer_size); \
+ } while (false);
+
+#endif
+
+
+
+/* Scale each of the four packed bytes in P in the low 16 bits of P by
+ SCALE. Return the result.
+
+ SCALE is an integer between 0 and 256. */
+
+static unsigned int
+sfntfont_android_scale32 (unsigned int scale, unsigned int p)
+{
+ uint32_t ag, rb;
+ uint32_t scaled_ag, scaled_rb;
+
+ ag = (p & 0xFF00FF00) >> 8;
+ rb = (p & 0x00FF00FF);
+
+ scaled_ag = (scale * ag) & 0xFF00FF00;
+ scaled_rb = (scale * rb) >> 8 & 0x00FF00FF;
+
+ return scaled_ag | scaled_rb;
+}
+
+static unsigned int
+sfntfont_android_mul8x2 (unsigned int a8, unsigned int b32)
+{
+ unsigned int i;
+
+ b32 &= 0xff00ff;
+ i = a8 * b32 + 0x800080;
+
+ return (i + ((i >> 8) & 0xff00ff)) >> 8 & 0xff00ff;
+}
+
+#define U255TO256(x) ((unsigned short) (x) + ((x) >> 7))
+
+/* Blend two pixels SRC and DST without utilizing any control flow.
+ Both SRC and DST are expected to be in premultiplied ABGB8888
+ format. Value is returned in premultiplied ARGB8888 format. */
+
+static unsigned int
+sfntfont_android_blend (unsigned int src, unsigned int dst)
+{
+ unsigned int a, br_part, ag_part, both;
+
+ a = (src >> 24);
+ br_part = sfntfont_android_mul8x2 (255 - a, dst);
+ ag_part = sfntfont_android_mul8x2 (255 - a, dst >> 8) << 8;
+
+ both = ag_part | br_part;
+
+ /* This addition need not be saturating because both has already
+ been multiplied by 255 - a. */
+ return both + src;
+}
+
+#ifdef __aarch64__
+
+/* Like U255TO256, but operates on vectors. */
+
+static uint16x8_t
+sfntfont_android_u255to256 (uint8x8_t in)
+{
+ return vaddl_u8 (vshr_n_u8 (in, 7), in);
+}
+
+/* Use processor features to efficiently composite four pixels at SRC
+ to DST. */
+
+static void
+sfntfont_android_over_8888_1 (unsigned int *src, unsigned int *dst)
+{
+ uint8x8_t alpha;
+ uint16x8_t alpha_c16, v1, v3, v4;
+ uint8x8_t b, g, r, a, v2, v5;
+ uint8x8x4_t _src, _dst;
+
+ /* Pull in src and dst.
+
+ This loads bytes, not words, so little endian ABGR becomes
+ RGBA. */
+ _src = vld4_u8 ((const uint8_t *) src);
+ _dst = vld4_u8 ((const uint8_t *) dst);
+
+ /* Load constants. */
+ v4 = vdupq_n_u16 (256);
+ v5 = vdup_n_u8 (0);
+
+ /* Load src alpha. */
+ alpha = _src.val[3];
+
+ /* alpha_c16 = 256 - 255TO256 (alpha). */
+ alpha_c16 = sfntfont_android_u255to256 (alpha);
+ alpha_c16 = vsubq_u16 (v4, alpha_c16);
+
+ /* Cout = Csrc + Cdst * alpha_c. */
+ v1 = vaddl_u8 (_dst.val[2], v5);
+ v2 = _src.val[2];
+ v3 = vmulq_u16 (v1, alpha_c16);
+ b = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+
+ v1 = vaddl_u8 (_dst.val[1], v5);
+ v2 = _src.val[1];
+ v3 = vmulq_u16 (v1, alpha_c16);
+ g = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+
+ v1 = vaddl_u8 (_dst.val[0], v5);
+ v2 = _src.val[0];
+ v3 = vmulq_u16 (v1, alpha_c16);
+ r = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+
+#if 0
+ /* Aout = Asrc + Adst * alpha_c. */
+ v1 = vaddl_u8 (_dst.val[3], v5);
+ v2 = _src.val[3];
+ v3 = vmulq_u16 (v1, alpha_c16);
+ a = vqadd_u8 (v2, vshrn_n_u16 (v3, 8));
+#else
+ /* We know that Adst is always 1, so Asrc + Adst * (1 - Asrc) is
+ always 1. */
+ a = vdup_n_u8 (255);
+#endif
+
+ /* Store back in dst. */
+ _dst.val[0] = r;
+ _dst.val[1] = g;
+ _dst.val[2] = b;
+ _dst.val[3] = a;
+ vst4_u8 ((uint8_t *) dst, _dst);
+}
+
+/* Use processor features to efficiently composite the buffer at SRC
+ to DST. Composite at most MAX - SRC words.
+
+ If either SRC or DST are not yet properly aligned, value is 1.
+ Otherwise, value is 0, and *X is incremented to the start of any
+ trailing data which could not be composited due to data alignment
+ constraints. */
+
+static int
+sfntfont_android_over_8888 (unsigned int *src, unsigned int *dst,
+ unsigned int *max, unsigned int *x)
+{
+ size_t i;
+ ptrdiff_t how_much;
+ void *s, *d;
+
+ /* Figure out how much can be composited by this loop. */
+ how_much = (max - src) & ~7;
+
+ /* Return if there is not enough to vectorize. */
+ if (!how_much)
+ return 1;
+
+ /* Now increment *X by that much so the containing loop can process
+ the remaining pixels one-by-one. */
+
+ *x += how_much;
+
+ for (i = 0; i < how_much; i += 8)
+ {
+ s = (src + i);
+ d = (dst + i);
+
+ sfntfont_android_over_8888_1 (s, d);
+ }
+
+ return 0;
+}
+
+#endif
+
+/* Composite the bitmap described by BUFFER, STRIDE and TEXT_RECTANGLE
+ onto the native-endian ABGR8888 bitmap described by DEST and
+ BITMAP_INFO. RECT is the subset of the bitmap to composite. */
+
+static void
+sfntfont_android_composite_bitmap (unsigned char *restrict buffer,
+ size_t stride,
+ unsigned char *restrict dest,
+ AndroidBitmapInfo *bitmap_info,
+ struct android_rectangle *text_rectangle,
+ struct android_rectangle *rect)
+{
+ unsigned int *src_row;
+ unsigned int *dst_row;
+ unsigned int i, src_y, x, src_x, max_x, dst_x;
+#ifdef __aarch64__
+ unsigned int lim_x;
+#endif
+
+ if ((intptr_t) dest & 3 || bitmap_info->stride & 3)
+ /* This shouldn't be possible as Android is supposed to align the
+ bitmap to at least a 4 byte boundary. */
+ emacs_abort ();
+ else
+ {
+ for (i = 0; i < rect->height; ++i)
+ {
+ if (i + rect->y >= bitmap_info->height)
+ /* Done. */
+ return;
+
+ src_y = i + (rect->y - text_rectangle->y);
+
+ if (src_y > text_rectangle->height)
+ /* Huh? */
+ return;
+
+ src_row = (unsigned int *) ((buffer + src_y * stride));
+ dst_row = (unsigned int *) (dest + ((i + rect->y)
+ * bitmap_info->stride));
+
+ /* Figure out where the loop below should end. */
+ max_x = min (rect->width, bitmap_info->width - rect->x);
+
+ /* Keep this loop simple! */
+ for (x = 0; x < max_x; ++x)
+ {
+ src_x = x + (rect->x - text_rectangle->x);
+ dst_x = x + rect->x;
+
+#ifdef __aarch64__
+ /* This is the largest value of src_x. */
+ lim_x = max_x + (rect->x - text_rectangle->x);
+
+ if (!sfntfont_android_over_8888 (src_row + src_x,
+ dst_row + dst_x,
+ src_row + lim_x,
+ &x))
+ {
+ /* Decrement X by one so the for loop can increment
+ it again. */
+ x--;
+ continue;
+ }
+#endif
+ dst_row[dst_x]
+ = sfntfont_android_blend (src_row[src_x],
+ dst_row[dst_x]);
+ }
+ }
+ }
+}
+
+/* Calculate the union containing both A and B, both boxes. Place the
+ result in RESULT. */
+
+static void
+sfntfont_android_union_boxes (struct gui_box a, struct gui_box b,
+ struct gui_box *result)
+{
+ result->x1 = min (a.x1, b.x1);
+ result->y1 = min (a.y1, b.y1);
+ result->x2 = max (a.x2, b.x2);
+ result->y2 = max (a.y2, b.y2);
+}
+
+/* Draw the specified glyph rasters from FROM to TO on behalf of S,
+ using S->gc. Fill the background if WITH_BACKGROUND is true.
+
+ See init_sfntfont_vendor and sfntfont_draw for more details. */
+
+static void
+sfntfont_android_put_glyphs (struct glyph_string *s, int from,
+ int to, int x, int y, bool with_background,
+ struct sfnt_raster **rasters,
+ int *x_coords)
+{
+ struct android_rectangle background, text_rectangle, rect;
+ struct gui_box text, character;
+ unsigned int *buffer, *row;
+ unsigned char *restrict raster_row;
+ size_t stride, i;
+ AndroidBitmapInfo bitmap_info;
+ unsigned char *bitmap_data;
+ jobject bitmap;
+ int left, top, temp_y;
+ unsigned int prod, raster_y;
+ unsigned long foreground, back_pixel, rb;
+
+ if (!s->gc->num_clip_rects)
+ /* Clip region is empty. */
+ return;
+
+ if (from == to)
+ /* Nothing to draw. */
+ return;
+
+ /* Swizzle the foreground and background in s->gc into BGR, then add
+ an alpha channel. */
+ foreground = s->gc->foreground;
+ back_pixel = s->gc->background;
+ rb = foreground & 0x00ff00ff;
+ foreground &= ~0x00ff00ff;
+ foreground |= rb >> 16 | rb << 16 | 0xff000000;
+ rb = back_pixel & 0x00ff00ff;
+ back_pixel &= ~0x00ff00ff;
+ back_pixel |= rb >> 16 | rb << 16 | 0xff000000;
+
+ prepare_face_for_display (s->f, s->face);
+
+ /* Build the scanline buffer. Figure out the bounds of the
+ background. */
+ memset (&background, 0, sizeof background);
+
+ if (with_background)
+ {
+ background.x = x;
+ background.y = y - FONT_BASE (s->font);
+ background.width = s->width;
+ background.height = FONT_HEIGHT (s->font);
+ }
+
+ /* Now figure out the bounds of the text. */
+
+ if (rasters[0])
+ {
+ text.x1 = x_coords[0] + rasters[0]->offx;
+ text.x2 = text.x1 + rasters[0]->width;
+ text.y1 = y - rasters[0]->height - rasters[0]->offy;
+ text.y2 = y - rasters[0]->offy;
+ }
+ else
+ memset (&text, 0, sizeof text);
+
+ for (i = 1; i < to - from; ++i)
+ {
+ /* See if text has to be extended. */
+
+ if (!rasters[i])
+ continue;
+
+ character.x1 = x_coords[i] + rasters[i]->offx;
+ character.x2 = character.x1 + rasters[i]->width;
+ character.y1 = y - rasters[i]->height - rasters[i]->offy;
+ character.y2 = y - rasters[i]->offy;
+
+ sfntfont_android_union_boxes (text, character, &text);
+ }
+
+ /* Union the background rect with the text rectangle. */
+ text_rectangle.x = text.x1;
+ text_rectangle.y = text.y1;
+ text_rectangle.width = text.x2 - text.x1;
+ text_rectangle.height = text.y2 - text.y1;
+ gui_union_rectangles (&background, &text_rectangle,
+ &text_rectangle);
+
+ /* Allocate enough to hold text_rectangle.height, aligned to 8 (or
+ 16) bytes. Then fill it with the background. */
+#ifndef __aarch64__
+ stride = ((text_rectangle.width * sizeof *buffer) + 7) & ~7;
+#else
+ stride = ((text_rectangle.width * sizeof *buffer) + 15) & ~15;
+#endif
+ GET_SCANLINE_BUFFER (buffer, text_rectangle.height, stride);
+
+ /* Try to optimize out this memset if the background rectangle
+ contains the whole text rectangle. */
+
+ if (!with_background || memcmp (&background, &text_rectangle,
+ sizeof text_rectangle))
+ memset (buffer, 0, text_rectangle.height * stride);
+
+ if (with_background)
+ {
+ /* Fill the background. First, offset the background rectangle
+ to become relative from text_rectangle.x,
+ text_rectangle.y. */
+ background.x = background.x - text_rectangle.x;
+ background.y = background.y - text_rectangle.y;
+ eassert (background.x >= 0 && background.y >= 0);
+
+ for (temp_y = background.y; (temp_y
+ < (background.y
+ + background.height));
+ ++temp_y)
+ {
+ row = (unsigned int *) ((unsigned char *) buffer
+ + stride * temp_y);
+
+ for (x = background.x; x < background.x + background.width; ++x)
+ row[x] = back_pixel;
+ }
+ }
+
+ /* Draw all the rasters onto the buffer. */
+ for (i = 0; i < to - from; ++i)
+ {
+ if (!rasters[i])
+ continue;
+
+ /* Figure out the top and left of the raster relative to
+ text_rectangle. */
+ left = x_coords[i] + rasters[i]->offx - text_rectangle.x;
+
+ /* Note that negative offy represents the part of the text that
+ lies below the baseline. */
+ top = (y - (rasters[i]->height + rasters[i]->offy)
+ - text_rectangle.y);
+ eassert (left >= 0 && top >= 0);
+
+ /* Draw the raster onto the temporary bitmap using the
+ foreground color scaled by the alpha map. */
+
+ for (raster_y = 0; raster_y < rasters[i]->height; ++raster_y)
+ {
+ row = (unsigned int *) ((unsigned char *) buffer
+ + stride * (raster_y + top));
+ raster_row = &rasters[i]->cells[raster_y * rasters[i]->stride];
+
+ for (x = 0; x < rasters[i]->width; ++x)
+ {
+ prod
+ = sfntfont_android_scale32 (U255TO256 (raster_row[x]),
+ foreground);
+ row[left + x]
+ = sfntfont_android_blend (prod, row[left + x]);
+ }
+ }
+ }
+
+ /* Lock the bitmap. It must be unlocked later. */
+ bitmap_data = android_lock_bitmap (FRAME_ANDROID_DRAWABLE (s->f),
+ &bitmap_info, &bitmap);
+
+ /* If locking the bitmap fails, just discard the data that was
+ allocated. */
+ if (!bitmap_data)
+ return;
+
+ /* Loop over each clip rect in the GC. */
+ eassert (bitmap_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
+
+ if (s->gc->num_clip_rects > 0)
+ {
+ for (i = 0; i < s->gc->num_clip_rects; ++i)
+ {
+ if (!gui_intersect_rectangles (&s->gc->clip_rects[i],
+ &text_rectangle, &rect))
+ /* Outside the clip region. */
+ continue;
+
+ /* Composite the intersection onto the buffer. */
+ sfntfont_android_composite_bitmap ((unsigned char *) buffer,
+ stride, bitmap_data,
+ &bitmap_info,
+ &text_rectangle, &rect);
+ }
+ }
+ else /* gc->num_clip_rects < 0 */
+ sfntfont_android_composite_bitmap ((unsigned char *) buffer,
+ stride, bitmap_data,
+ &bitmap_info,
+ &text_rectangle,
+ &text_rectangle);
+
+ /* Release the bitmap. */
+ AndroidBitmap_unlockPixels (android_java_env, bitmap);
+ ANDROID_DELETE_LOCAL_REF (bitmap);
+
+ /* Damage the window by the text rectangle. */
+ android_damage_window (FRAME_ANDROID_DRAWABLE (s->f),
+ &text_rectangle);
+
+#undef MAX_ALLOCA
+}
+
+
+
+/* Shrink the scanline buffer after a window update. If
+ max_scanline_buffer_size is not zero, and is less than
+ scanline_buffer.buffer_size / 2, then resize the scanline buffer to
+ max_scanline_buffer_size. */
+
+void
+sfntfont_android_shrink_scanline_buffer (void)
+{
+ if (!max_scanline_buffer_size)
+ return;
+
+ if (max_scanline_buffer_size
+ < scanline_buffer.buffer_size / 2)
+ {
+ scanline_buffer.buffer_size
+ = max_scanline_buffer_size;
+ scanline_buffer.buffer_data
+ = xrealloc (scanline_buffer.buffer_data,
+ max_scanline_buffer_size);
+ }
+
+ max_scanline_buffer_size = 0;
+}
+
+
+
+/* Font driver definition. */
+
+/* Return the font cache for this font driver. F is ignored. */
+
+static Lisp_Object
+sfntfont_android_get_cache (struct frame *f)
+{
+ return font_cache;
+}
+
+/* The Android sfntfont driver. */
+const struct font_driver android_sfntfont_driver =
+ {
+ .type = LISPSYM_INITIALLY (Qsfnt_android),
+ .case_sensitive = true,
+ .get_cache = sfntfont_android_get_cache,
+ .list = sfntfont_list,
+ .match = sfntfont_match,
+ .draw = sfntfont_draw,
+ .open_font = sfntfont_open,
+ .close_font = sfntfont_close,
+ .encode_char = sfntfont_encode_char,
+ .text_extents = sfntfont_text_extents,
+ .list_family = sfntfont_list_family,
+ .get_variation_glyphs = sfntfont_get_variation_glyphs,
+
+#ifdef HAVE_HARFBUZZ
+ /* HarfBuzz support is enabled transparently on Android without
+ using a separate font driver. */
+ .begin_hb_font = sfntfont_begin_hb_font,
+ .combining_capability = hbfont_combining_capability,
+ .shape = hbfont_shape,
+ .otf_capability = hbfont_otf_capability,
+#endif /* HAVE_HARFBUZZ */
+ };
+
+
+
+/* This is an ugly hack that should go away, but I can't think of
+ how. */
+
+DEFUN ("android-enumerate-fonts", Fandroid_enumerate_fonts,
+ Sandroid_enumerate_fonts, 0, 0, 0,
+ doc: /* Enumerate fonts present on the system.
+
+Signal an error if fonts have already been enumerated. This would
+normally have been done in C, but reading fonts require Lisp to be
+loaded before character sets are made available. */)
+ (void)
+{
+ DIR *dir;
+ int i;
+ struct dirent *dirent;
+ char name[PATH_MAX * 2];
+ static bool enumerated;
+
+ if (enumerated)
+ error ("Fonts have already been enumerated");
+ enumerated = true;
+
+ block_input ();
+
+ /* Scan through each of the system font directories. Enumerate each
+ font that looks like a TrueType font. */
+ for (i = 0; i < ARRAYELTS (system_font_directories); ++i)
+ {
+ dir = opendir (system_font_directories[i]);
+
+ __android_log_print (ANDROID_LOG_VERBOSE, __func__,
+ "Loading fonts from: %s",
+ system_font_directories[i]);
+
+ if (!dir)
+ continue;
+
+ while ((dirent = readdir (dir)))
+ {
+ /* If it contains (not ends with!) with .ttf or .ttc, then
+ enumerate it. */
+
+ if ((strstr (dirent->d_name, ".ttf")
+ || strstr (dirent->d_name, ".ttc"))
+ /* Ignore the non-variable Roboto font. */
+ && (i != 0 || strcmp (dirent->d_name,
+ "RobotoStatic-Regular.ttf")))
+ {
+ sprintf (name, "%s/%s", system_font_directories[i],
+ dirent->d_name);
+ sfnt_enum_font (name);
+ }
+ }
+
+ closedir (dir);
+ }
+
+ unblock_input ();
+
+ return Qnil;
+}
+
+
+
+static void
+syms_of_sfntfont_android_for_pdumper (void)
+{
+ init_sfntfont_vendor (Qsfnt_android, &android_sfntfont_driver,
+ sfntfont_android_put_glyphs);
+ register_font_driver (&android_sfntfont_driver, NULL);
+}
+
+void
+init_sfntfont_android (void)
+{
+ if (!android_init_gui)
+ return;
+
+ /* Make sure to pick the right Sans Serif font depending on what
+ version of Android the device is running. */
+ if (android_get_current_api_level () >= 15)
+ Vsfnt_default_family_alist
+ = list3 (Fcons (build_string ("Monospace"),
+ build_string ("Droid Sans Mono")),
+ /* Android doesn't come with a Monospace Serif font, so
+ this will have to do. */
+ Fcons (build_string ("Monospace Serif"),
+ build_string ("Droid Sans Mono")),
+ Fcons (build_string ("Sans Serif"),
+ build_string ("Roboto")));
+ else
+ Vsfnt_default_family_alist
+ = list3 (Fcons (build_string ("Monospace"),
+ build_string ("Droid Sans Mono")),
+ Fcons (build_string ("Monospace Serif"),
+ build_string ("Droid Sans Mono")),
+ Fcons (build_string ("Sans Serif"),
+ build_string ("Droid Sans")));
+
+ /* Set up the user fonts directory. This directory is ``fonts'' in
+ the Emacs files directory. */
+ snprintf (system_font_directories[2], PATH_MAX, "%s/fonts",
+ android_get_home_directory ());
+}
+
+void
+syms_of_sfntfont_android (void)
+{
+ DEFSYM (Qsfnt_android, "sfnt-android");
+ DEFSYM (Qandroid_enumerate_fonts, "android-enumerate-fonts");
+ Fput (Qandroid, Qfont_driver_superseded_by, Qsfnt_android);
+
+ font_cache = list (Qnil);
+ staticpro (&font_cache);
+
+ defsubr (&Sandroid_enumerate_fonts);
+
+ pdumper_do_now_and_after_load (syms_of_sfntfont_android_for_pdumper);
+}
diff --git a/src/sfntfont.c b/src/sfntfont.c
new file mode 100644
index 00000000000..71399b890d2
--- /dev/null
+++ b/src/sfntfont.c
@@ -0,0 +1,3874 @@
+/* sfnt format font driver for GNU Emacs.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#include <config.h>
+
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "lisp.h"
+
+#include "blockinput.h"
+#include "charset.h"
+#include "coding.h"
+#include "font.h"
+#include "frame.h"
+#include "math.h"
+#include "sfnt.h"
+#include "sfntfont.h"
+
+#ifdef HAVE_HARFBUZZ
+#include <hb.h>
+#include <hb-ot.h>
+#endif /* HAVE_HARFBUZZ */
+
+/* For FRAME_FONT. */
+#include TERM_HEADER
+
+/* Generic font driver for sfnt-based fonts (currently TrueType, but
+ it would be easy to add CFF support in the future with a PostScript
+ renderer.)
+
+ This is not a complete font driver. Hooks must be supplied by the
+ platform implementer to draw glyphs. */
+
+
+
+/* Tables associated with each font, be it distortable or not. This
+ allows different font objects sharing the same underlying font file
+ to share tables. */
+
+struct sfnt_font_tables
+{
+ /* Various tables required to use the font. */
+ struct sfnt_cmap_table *cmap;
+ struct sfnt_hhea_table *hhea;
+ struct sfnt_maxp_table *maxp;
+ struct sfnt_head_table *head;
+ struct sfnt_hmtx_table *hmtx;
+ struct sfnt_glyf_table *glyf;
+ struct sfnt_loca_table_short *loca_short;
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_prep_table *prep;
+ struct sfnt_fpgm_table *fpgm;
+ struct sfnt_cvt_table *cvt;
+ struct sfnt_fvar_table *fvar;
+ struct sfnt_avar_table *avar;
+ struct sfnt_gvar_table *gvar;
+ struct sfnt_cvar_table *cvar;
+
+ /* The selected character map. */
+ struct sfnt_cmap_encoding_subtable_data *cmap_data;
+
+ /* Data identifying that character map. */
+ struct sfnt_cmap_encoding_subtable cmap_subtable;
+
+ /* The UVS context. */
+ struct sfnt_uvs_context *uvs;
+
+#ifdef HAVE_MMAP
+ /* Whether or not the glyph table has been mmapped. */
+ bool glyf_table_mapped;
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+ /* File descriptor associated with this font. */
+ int fd;
+
+ /* The table directory of the font file. */
+ struct sfnt_offset_subtable *directory;
+#endif /* HAVE_HARFBUZZ */
+};
+
+/* Description of a font that hasn't been opened. */
+
+struct sfnt_font_desc
+{
+ /* Next font in this list. */
+ struct sfnt_font_desc *next;
+
+ /* Family name of the font. */
+ Lisp_Object family;
+
+ /* Style name of the font. */
+ Lisp_Object style;
+
+ /* 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;
+
+ /* Font registry that this font supports. */
+ Lisp_Object registry;
+
+ /* Vector of instances. Each element is another of the instance's
+ `style', `adstyle', and numeric width, weight, and slant. May be
+ nil. */
+ Lisp_Object instances;
+
+ /* Numeric width, weight, slant and spacing. */
+ int width, weight, slant, spacing;
+
+ /* Path to the font file. */
+ char *path;
+
+ /* char table consisting of characters already known to be
+ present in the font. */
+ Lisp_Object char_cache;
+
+ /* Whether or not the character map can't be used by Emacs. */
+ bool cmap_invalid;
+
+ /* The header of the cmap being used. May be invalid, in which case
+ platform_id will be 500. */
+ struct sfnt_cmap_encoding_subtable subtable;
+
+ /* The offset of the table directory within PATH. */
+ off_t offset;
+
+ /* The number of glyphs in this font. Used to catch invalid cmap
+ tables. This is actually the number of glyphs - 1. */
+ int num_glyphs;
+
+ /* The number of references to the font tables below. */
+ int refcount;
+
+ /* List of font tables. */
+ struct sfnt_font_tables *tables;
+};
+
+/* List of fonts. */
+
+static struct sfnt_font_desc *system_fonts;
+
+/* Font enumeration and matching. The sfnt driver assumes it can read
+ data from each font at startup. It then reads the head, meta and
+ name tables to determine font data, and records the font in a list
+ of system fonts that is then matched against. */
+
+/* Set up the coding system CODING to decode string data from the
+ given platform id ID and platform specific id PLATFORM_SPECIFIC_ID.
+ Value is 0 upon success, 1 upon failure. */
+
+static int
+sfnt_setup_coding_system (enum sfnt_platform_id id, int platform_specific_id,
+ struct coding_system *coding)
+{
+ Lisp_Object system;
+
+ system = Qnil;
+
+ /* Figure out what coding system to use. */
+
+ switch (id)
+ {
+ case SFNT_PLATFORM_UNICODE:
+ system = Qutf_16be;
+ break;
+
+ case SFNT_PLATFORM_MACINTOSH:
+
+ if (platform_specific_id == SFNT_MACINTOSH_ROMAN)
+ system = Qmac_roman;
+ else
+ /* MULE doesn't support the rest... */
+ system = Qnil;
+
+ break;
+
+ case SFNT_PLATFORM_MICROSOFT:
+ system = Qutf_16be;
+
+ /* Not sure if this is right. */
+ if (platform_specific_id == SFNT_MICROSOFT_BIG_FIVE)
+ system = Qchinese_big5;
+
+ break;
+
+ default:
+ system = Qnil;
+ }
+
+ if (NILP (system))
+ return 1;
+
+ setup_coding_system (system, coding);
+ return 0;
+}
+
+/* Globals used to communicate inside the condition-case wrapper. */
+static struct coding_system *sfnt_font_coding;
+
+/* The src_object being encoded from. This should be on the stack as
+ well, or it will get garbage collected. */
+static Lisp_Object sfnt_font_src_object;
+
+/* From-position. */
+static ptrdiff_t sfnt_font_from, sfnt_font_from_byte;
+
+/* To-position. */
+static ptrdiff_t sfnt_font_to, sfnt_font_to_byte;
+
+/* Destination object. Once again, this should also be on the
+ stack. */
+static Lisp_Object sfnt_font_dst_object;
+
+/* Error flag. Set to true if a signal was caught. */
+static bool sfnt_font_signal;
+
+static Lisp_Object
+sfnt_safe_decode_coding_object_1 (void)
+{
+ decode_coding_object (sfnt_font_coding,
+ sfnt_font_src_object,
+ sfnt_font_from,
+ sfnt_font_from_byte,
+ sfnt_font_to,
+ sfnt_font_to_byte,
+ sfnt_font_dst_object);
+ return Qnil;
+}
+
+static Lisp_Object
+sfnt_safe_decode_coding_object_2 (Lisp_Object error)
+{
+ sfnt_font_signal = true;
+
+ return Qnil;
+}
+
+/* Like decode_coding_object, but return 1 if a signal happens. Value
+ is otherwise 0. */
+
+static int
+sfnt_safe_decode_coding_object (struct coding_system *coding,
+ Lisp_Object src_object,
+ ptrdiff_t from, ptrdiff_t from_byte,
+ ptrdiff_t to, ptrdiff_t to_byte,
+ Lisp_Object dst_object)
+{
+ sfnt_font_coding = coding;
+ sfnt_font_src_object = src_object;
+ sfnt_font_from = from;
+ sfnt_font_from_byte = from_byte;
+ sfnt_font_to = to;
+ sfnt_font_to_byte = to_byte;
+ sfnt_font_dst_object = dst_object;
+ sfnt_font_signal = false;
+
+ internal_condition_case (sfnt_safe_decode_coding_object_1,
+ Qt,
+ sfnt_safe_decode_coding_object_2);
+
+ return (int) sfnt_font_signal;
+}
+
+/* Decode the specified string DATA. The encoding is determined based
+ on PLATFORM_ID, PLATFORM_SPECIFIC_ID and LANGUAGE_ID. Consult
+ sfnt.h and the TrueType Reference Manual for more details. LENGTH
+ is the length of DATA in bytes.
+
+ Value is nil upon failure, else the decoded string. */
+
+static Lisp_Object
+sfnt_decode_font_string (unsigned char *data, enum sfnt_platform_id id,
+ int platform_specific_id, int language_id,
+ size_t length)
+{
+ struct coding_system coding;
+
+ memset (&coding, 0, sizeof coding);
+ sfnt_setup_coding_system (id, platform_specific_id, &coding);
+ coding.mode |= CODING_MODE_SAFE_ENCODING;
+ coding.mode |= CODING_MODE_LAST_BLOCK;
+ /* Suppress producing escape sequences for composition. */
+ coding.common_flags &= ~CODING_ANNOTATION_MASK;
+ coding.source = data;
+
+ if (sfnt_safe_decode_coding_object (&coding, Qnil, 0, 0,
+ length, length, Qt))
+ return Qnil;
+
+ return coding.dst_object;
+}
+
+/* Decode the family and style names from the name table NAME. Return
+ 0 and the family and style names upon success, else 1. */
+
+static int
+sfnt_decode_family_style (struct sfnt_name_table *name,
+ Lisp_Object *family, Lisp_Object *style)
+{
+ struct sfnt_name_record family_rec, style_rec;
+ unsigned char *family_data, *style_data;
+
+ family_data = sfnt_find_name (name, SFNT_NAME_FONT_FAMILY,
+ &family_rec);
+ style_data = sfnt_find_name (name, SFNT_NAME_FONT_SUBFAMILY,
+ &style_rec);
+
+ if (!family_data || !style_data)
+ return 1;
+
+ /* Now decode the data. */
+ *family = sfnt_decode_font_string (family_data,
+ family_rec.platform_id,
+ family_rec.platform_specific_id,
+ family_rec.language_id,
+ family_rec.length);
+ *style = sfnt_decode_font_string (style_data,
+ style_rec.platform_id,
+ style_rec.platform_specific_id,
+ style_rec.language_id,
+ style_rec.length);
+
+ /* Return whether or not it was successful. */
+ return (!NILP (*family) && !NILP (*style)) ? 0 : 1;
+}
+
+/* Decode the foundry names from the name table NAME. Return the
+ foundry name, or nil upon failure. */
+
+static Lisp_Object
+sfnt_decode_foundry_name (struct sfnt_name_table *name)
+{
+ struct sfnt_name_record designer_rec;
+ unsigned char *designer_data;
+
+ designer_data = sfnt_find_name (name, SFNT_NAME_DESIGNER,
+ &designer_rec);
+
+ if (!designer_data)
+ return Qnil;
+
+ return sfnt_decode_font_string (designer_data,
+ designer_rec.platform_id,
+ designer_rec.platform_specific_id,
+ designer_rec.language_id,
+ designer_rec.length);
+}
+
+/* Decode the name of the specified font INSTANCE using the given NAME
+ table. Return the name of that instance, or nil upon failure. */
+
+static Lisp_Object
+sfnt_decode_instance_name (struct sfnt_instance *instance,
+ struct sfnt_name_table *name)
+{
+ struct sfnt_name_record name_rec;
+ unsigned char *name_data;
+
+ name_data = sfnt_find_name (name, instance->name_id,
+ &name_rec);
+
+ if (!name_data)
+ return Qnil;
+
+ return sfnt_decode_font_string (name_data,
+ name_rec.platform_id,
+ name_rec.platform_specific_id,
+ name_rec.language_id,
+ name_rec.length);
+}
+
+struct sfnt_style_desc
+{
+ /* The C string to match against. */
+ const char *c_string;
+
+ /* The value of the style field. */
+ int value;
+};
+
+/* Array of style descriptions describing weight. */
+static struct sfnt_style_desc sfnt_weight_descriptions[] =
+ {
+ { "thin", 0, },
+ { "extralight", 40, },
+ { "ultralight", 40, },
+ { "light", 50, },
+ { "demilight", 55, },
+ { "semilight", 55, },
+ { "book", 75, },
+ { "medium", 100, },
+ { "demibold", 180, },
+ { "semibold", 180, },
+ { "bold", 200, },
+ { "extrabold", 205, },
+ { "ultrabold", 205, },
+ { "black", 210, },
+ { "heavy", 210, },
+ { "extrablack", 215, },
+ { "ultrablack", 215, },
+ };
+
+/* Array of style descriptions describing slant. */
+static struct sfnt_style_desc sfnt_slant_descriptions[] =
+ {
+ { "italic", 200, },
+ { "oblique", 210, },
+ };
+
+/* Array of style descriptions describing width. */
+static struct sfnt_style_desc sfnt_width_descriptions[] =
+ {
+ { "ultracondensed", 50, },
+ { "extracondensed", 63, },
+ { "condensed", 75, },
+ { "semicondensed", 87, },
+ { "semiexpanded", 113, },
+ { "expanded", 125, },
+ { "extraexpanded", 150, },
+ { "ultraexpanded", 200, },
+ };
+
+/* Figure out DESC->width, DESC->weight, DESC->slant and DESC->spacing
+ 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)
+{
+ char *style, *single, *saveptr;
+ int i;
+
+ /* Fill in default values. slant seems to not be consistent with
+ Fontconfig. */
+ desc->weight = 80;
+ desc->slant = 100;
+ desc->width = 100;
+
+ /* Split the style into spaces. As long as no weight, slant, or
+ width is encountered, look in the corresponding descriptions
+ array. GC must not happen inside this block. */
+ style = SSDATA (Fdowncase (style_name));
+ saveptr = NULL;
+
+ while ((single = strtok_r (style, " ", &saveptr)))
+ {
+ style = NULL;
+
+ if (desc->weight == 80)
+ {
+ /* Weight hasn't been found yet. Scan through the weight
+ table. */
+ for (i = 0; i < ARRAYELTS (sfnt_weight_descriptions); ++i)
+ {
+ if (!strcmp (sfnt_weight_descriptions[i].c_string,
+ single))
+ {
+ /* Weight found. Continue on reading the slant and
+ width. */
+ desc->weight = sfnt_weight_descriptions[i].value;
+ goto next;
+ }
+ }
+ }
+
+ if (desc->slant == 100)
+ {
+ /* Slant hasn't been found yet. Scan through the slant
+ table. */
+ for (i = 0; i < ARRAYELTS (sfnt_slant_descriptions); ++i)
+ {
+ if (!strcmp (sfnt_slant_descriptions[i].c_string,
+ single))
+ {
+ /* Slant found. Continue on reading the weight and
+ width. */
+ desc->slant = sfnt_slant_descriptions[i].value;
+ goto next;
+ }
+ }
+ }
+
+ if (desc->width == 100)
+ {
+ /* Width hasn't been found yet. Scan through the width
+ table. */
+ for (i = 0; i < ARRAYELTS (sfnt_width_descriptions); ++i)
+ {
+ if (!strcmp (sfnt_width_descriptions[i].c_string,
+ single))
+ {
+ /* Width found. Continue on reading the slant and
+ weight. */
+ desc->width = sfnt_width_descriptions[i].value;
+ goto next;
+ }
+ }
+ }
+
+ /* This token is extraneous or was not recognized. Capitalize
+ the first letter and set it as the adstyle. */
+
+ 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)
+ {
+ /* Fall back to the supported languages metadata. */
+ data = sfnt_find_metadata (meta, SFNT_META_DATA_TAG_SLNG,
+ &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 ();
+}
+
+/* Return the font registry corresponding to the encoding subtable
+ SUBTABLE.
+
+ Under X, the font registry is an atom registered with the Open
+ Group uniquely identifying the organization which defines the
+ font's character set.
+
+ In practice, the registry overlaps with the character set itself.
+ So Emacs just uses the ``registry'' field of each font object and
+ entity to represent both instead. */
+
+static Lisp_Object
+sfnt_registry_for_subtable (struct sfnt_cmap_encoding_subtable *subtable)
+{
+ switch (subtable->platform_id)
+ {
+ case SFNT_PLATFORM_UNICODE:
+ /* Reject variation selector and last resort tables. */
+ if ((subtable->platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ || (subtable->platform_specific_id
+ == SFNT_UNICODE_LAST_RESORT))
+ return Qnil;
+
+ return Qiso10646_1;
+
+ case SFNT_PLATFORM_MACINTOSH:
+
+ switch (subtable->platform_specific_id)
+ {
+ case SFNT_MACINTOSH_ROMAN:
+ /* X calls mac-roman ``apple-roman''. */
+ return Qapple_roman;
+
+ default:
+ /* Some other Macintosh charset not supported by Emacs. */
+ return Qnil;
+ }
+
+ case SFNT_PLATFORM_MICROSOFT:
+
+ /* Microsoft specific encodings. */
+
+ switch (subtable->platform_specific_id)
+ {
+ case SFNT_MICROSOFT_SYMBOL:
+ case SFNT_MICROSOFT_UNICODE_BMP:
+ /* Symbols in the Unicode PUA are still Unicode. */
+ return Qiso10646_1;
+
+ case SFNT_MICROSOFT_SHIFT_JIS:
+ return Qjisx0208_1983_0;
+
+ case SFNT_MICROSOFT_PRC:
+ return Qgbk;
+
+ case SFNT_MICROSOFT_JOHAB:
+ return Qksc5601_1987_0;
+
+ case SFNT_MICROSOFT_UNICODE_UCS_4:
+ return Qiso10646_1;
+ }
+
+ default:
+ return Qnil;
+ }
+}
+
+/* Return the type of characters that the cmap subtable SUBTABLE maps
+ from. Value is:
+
+ 2 if SUBTABLE maps from Unicode characters, including those outside
+ the Unicode Basic Multilingual Plane (BMP).
+
+ 1 if SUBTABLE maps from Unicode characters within the BMP.
+
+ 0 if SUBTABLE maps from some other character set that Emacs knows
+ about.
+
+ 3 if SUBTABLE cannot be used by Emacs. */
+
+static int
+sfntfont_identify_cmap (struct sfnt_cmap_encoding_subtable subtable)
+{
+ switch (subtable.platform_id)
+ {
+ case SFNT_PLATFORM_UNICODE:
+
+ /* Reject variation selector and last resort tables. */
+ if ((subtable.platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ || (subtable.platform_specific_id
+ == SFNT_UNICODE_LAST_RESORT))
+ return 3;
+
+ /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within
+ the BMP. */
+ if (subtable.platform_specific_id < SFNT_UNICODE_2_0)
+ return 1;
+
+ return 2;
+
+ case SFNT_PLATFORM_MACINTOSH:
+
+ switch (subtable.platform_specific_id)
+ {
+ case SFNT_MACINTOSH_ROMAN:
+ /* mac-roman */
+ return 0;
+
+ default:
+ /* Some other Macintosh charset not supported by Emacs. */
+ return 3;
+ }
+
+ case SFNT_PLATFORM_MICROSOFT:
+
+ /* Microsoft specific encodings. */
+
+ switch (subtable.platform_specific_id)
+ {
+ case SFNT_MICROSOFT_SYMBOL:
+ /* Symbols in the Unicode PUA are still Unicode. */
+ return 1;
+
+ case SFNT_MICROSOFT_UNICODE_BMP:
+ return 1;
+
+ case SFNT_MICROSOFT_SHIFT_JIS:
+ /* PCK aka japanese-jisx0208. */
+ return 0;
+
+ case SFNT_MICROSOFT_PRC:
+ /* GBK, GB2312 or GB18030. */
+ return 0;
+
+ case SFNT_MICROSOFT_JOHAB:
+ /* KS C 5601-1992, aka korean-ksc5601. */
+ return 0;
+
+ case SFNT_MICROSOFT_UNICODE_UCS_4:
+ /* Unicode past the BMP. */
+ return 2;
+ }
+
+ default:
+ return 3;
+ }
+}
+
+/* Figure out which registry DESC, backed by FD, whose table directory
+ is SUBTABLE, is likely to support.
+
+ Read the header of each subtable in the character map and compute
+ the registry to use; then, set DESC->registry to that value. */
+
+static void
+sfnt_grok_registry (int fd, struct sfnt_font_desc *desc,
+ struct sfnt_offset_subtable *subtable)
+{
+ struct sfnt_cmap_table *cmap;
+ struct sfnt_cmap_encoding_subtable *subtables;
+ int i;
+
+ cmap = sfnt_read_cmap_table (fd, subtable, &subtables, NULL);
+
+ if (!cmap)
+ return;
+
+ /* Now pick the ``best'' character map the same way as sfntfont_open
+ does. The caveat is that since the subtable data has not been
+ read, Emacs cannot determine whether or not the encoding subtable
+ is valid.
+
+ Once platform_id is set, that value becomes much more
+ reliable. */
+
+ /* First look for a non-BMP Unicode cmap. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (sfntfont_identify_cmap (subtables[i]) == 2)
+ {
+ desc->registry
+ = sfnt_registry_for_subtable (&subtables[i]);
+ goto done;
+ }
+ }
+
+ /* Next, look for a BMP only Unicode cmap. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (sfntfont_identify_cmap (subtables[i]) == 1)
+ {
+ desc->registry
+ = sfnt_registry_for_subtable (&subtables[i]);
+ goto done;
+ }
+ }
+
+ /* Finally, use the first cmap that appears and can be
+ identified. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (sfntfont_identify_cmap (subtables[i]) == 0)
+ {
+ desc->registry
+ = sfnt_registry_for_subtable (&subtables[i]);
+ goto done;
+ }
+ }
+
+ /* There are no cmaps available to Emacs. */
+ done:
+ xfree (cmap);
+ xfree (subtables);
+}
+
+/* Return whether or not the font description PREV conflicts with the
+ newer font description DESC, and should be removed from the list of
+ system fonts.
+
+ If PREV is a variable font, potentially adjust its list of
+ instances. */
+
+static bool
+sfnt_replace_fonts_p (struct sfnt_font_desc *prev,
+ struct sfnt_font_desc *desc)
+{
+ int i, width, weight, slant, count_instance;
+ Lisp_Object tem;
+ bool family_equal_p;
+
+ family_equal_p = !NILP (Fstring_equal (prev->family,
+ desc->family));
+
+ if ((!NILP (desc->instances)
+ || !NILP (Fstring_equal (prev->style, desc->style)))
+ && family_equal_p)
+ return true;
+
+ if (NILP (prev->instances) || !family_equal_p)
+ return false;
+
+ /* Look through instances in PREV to see if DESC provides the same
+ thing. */
+
+ count_instance = 0;
+ for (i = 0; i < ASIZE (prev->instances); ++i)
+ {
+ tem = AREF (prev->instances, i);
+
+ if (NILP (tem))
+ continue;
+
+ width = XFIXNUM (AREF (tem, 2));
+ weight = XFIXNUM (AREF (tem, 3));
+ slant = XFIXNUM (AREF (tem, 4));
+
+ if (desc->width == width
+ && desc->weight == weight
+ && desc->slant == slant)
+ {
+ /* Remove this instance. */
+ ASET (prev->instances, i, Qnil);
+ continue;
+ }
+
+ count_instance++;
+ }
+
+ /* Remove this desc if there are no more instances. */
+ return count_instance < 1;
+}
+
+/* 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
+ failure, else 0. */
+
+static int
+sfnt_enum_font_1 (int fd, const char *file,
+ struct sfnt_offset_subtable *subtables,
+ off_t offset)
+{
+ struct sfnt_font_desc *desc, **next, *prev;
+ struct sfnt_head_table *head;
+ struct sfnt_name_table *name;
+ struct sfnt_meta_table *meta;
+ struct sfnt_maxp_table *maxp;
+ struct sfnt_fvar_table *fvar;
+ struct sfnt_font_desc temp;
+ Lisp_Object family, style, instance, style1;
+ int i;
+
+ /* Create the font desc and copy in the file name. */
+ desc = xzalloc (sizeof *desc + strlen (file) + 1);
+ desc->path = (char *) (desc + 1);
+ memcpy (desc->path, file, strlen (file) + 1);
+ desc->offset = offset;
+
+ /* Check that this is a TrueType font. */
+ if (subtables->scaler_type != SFNT_SCALER_TRUE
+ && subtables->scaler_type != SFNT_SCALER_VER1)
+ goto bail1;
+
+ /* Read required tables. */
+ head = sfnt_read_head_table (fd, subtables);
+ if (!head)
+ goto bail1;
+
+ name = sfnt_read_name_table (fd, subtables);
+ if (!name)
+ goto bail2;
+
+ maxp = sfnt_read_maxp_table (fd, subtables);
+ if (!maxp)
+ goto bail3;
+
+ /* meta is not required, nor present on many non-Apple fonts. */
+ meta = sfnt_read_meta_table (fd, subtables);
+
+ /* Decode the family and style from the name table. */
+ if (sfnt_decode_family_style (name, &family, &style))
+ goto bail4;
+
+ /* See if this is a distortable/variable/multiple master font (all
+ three terms mean the same time.) */
+ fvar = sfnt_read_fvar_table (fd, subtables);
+
+ /* Set the family. */
+ desc->family = family;
+ desc->designer = sfnt_decode_foundry_name (name);
+ desc->char_cache = Qnil;
+ desc->subtable.platform_id = 500;
+
+ /* Set the largest glyph identifier. */
+ desc->num_glyphs = maxp->num_glyphs;
+
+ /* 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),
+ build_string ("mono"),
+ Qnil)))
+ desc->spacing = 100; /* FC_MONO */
+
+ /* Finally add mac-style flags. Allow them to override styles that
+ have not been found. */
+
+ if (head->mac_style & 01 && desc->weight == 80) /* Bold */
+ desc->weight = 200;
+
+ if (head->mac_style & 02 && desc->slant == 0) /* Italic */
+ desc->slant = 100;
+
+ /* Figure out what registry this font is likely to support. */
+ sfnt_grok_registry (fd, desc, subtables);
+
+ if (fvar && fvar->instance_count)
+ {
+ /* If there is an fvar table with instances, then this is a font
+ which defines different axes along which the points in each
+ glyph can be changed.
+
+ Instead of enumerating the font itself, enumerate each
+ instance within, which specifies how to configure each axis
+ to achieve a specified style. */
+
+ desc->instances = make_vector (fvar->instance_count, Qnil);
+
+ for (i = 0; i < fvar->instance_count; ++i)
+ {
+ style1 = sfnt_decode_instance_name (&fvar->instance[i],
+ name);
+
+ if (!style1)
+ continue;
+
+ /* Now parse the style. */
+ temp.adstyle = Qnil;
+ sfnt_parse_style (style1, &temp);
+
+ /* Set each field of the vector. */
+ instance = make_vector (5, Qnil);
+ ASET (instance, 0, style1);
+ ASET (instance, 1, temp.adstyle);
+ ASET (instance, 2, make_fixnum (temp.width));
+ ASET (instance, 3, make_fixnum (temp.weight));
+ ASET (instance, 4, make_fixnum (temp.slant));
+
+ /* Place the vector in desc->instances. */
+ ASET (desc->instances, i, instance);
+ }
+ }
+
+ /* Set the style, link the desc onto system_fonts and return. */
+ desc->style = style;
+ desc->next = system_fonts;
+ system_fonts = desc;
+
+ /* Remove any fonts which have the same style as this one. For
+ distortable fonts, only remove overlapping styles, unless this is
+ also a distortable font. */
+
+ next = &system_fonts->next;
+ prev = *next;
+ for (; *next; prev = *next)
+ {
+ if (sfnt_replace_fonts_p (prev, desc))
+ {
+ *next = prev->next;
+ xfree (prev);
+ }
+ else
+ next = &prev->next;
+ }
+
+ xfree (fvar);
+ xfree (meta);
+ xfree (maxp);
+ xfree (name);
+ xfree (head);
+ return 0;
+
+ bail4:
+ xfree (meta);
+ xfree (maxp);
+ bail3:
+ xfree (name);
+ bail2:
+ xfree (head);
+ bail1:
+ xfree (desc);
+ return 1;
+}
+
+/* Enumerate the font FILE into the list of system fonts. Return 1 if
+ it could not be enumerated, 0 otherwise.
+
+ Remove any font whose family and style is a duplicate of this one.
+
+ FILE can either be a TrueType collection file containing TrueType
+ fonts, or a TrueType font itself. */
+
+int
+sfnt_enum_font (const char *file)
+{
+ int fd, rc;
+ struct sfnt_offset_subtable *subtables;
+ struct sfnt_ttc_header *ttc;
+ size_t i;
+
+ /* Now open the font for reading. */
+ fd = emacs_open (file, O_RDONLY, 0);
+
+ if (fd == -1)
+ goto bail;
+
+ /* Read the table directory. */
+ subtables = sfnt_read_table_directory (fd);
+
+ if (subtables == (struct sfnt_offset_subtable *) -1)
+ {
+ /* This is actually a TrueType container file. Go back to the
+ beginning and read the TTC header. */
+
+ if (lseek (fd, 0, SEEK_SET))
+ goto bail0;
+
+ ttc = sfnt_read_ttc_header (fd);
+
+ if (!ttc)
+ goto bail0;
+
+ /* Enumerate each of the fonts in the collection. */
+
+ for (i = 0; i < ttc->num_fonts; ++i)
+ {
+ if (lseek (fd, ttc->offset_table[i], SEEK_SET)
+ != ttc->offset_table[i])
+ continue;
+
+ subtables = sfnt_read_table_directory (fd);
+
+ if (!subtables)
+ continue;
+
+ sfnt_enum_font_1 (fd, file, subtables,
+ ttc->offset_table[i]);
+ xfree (subtables);
+ }
+
+ /* Always treat reading containers as having been
+ successful. */
+
+ emacs_close (fd);
+ xfree (ttc);
+ return 0;
+ }
+
+ if (!subtables)
+ goto bail0;
+
+ /* Now actually enumerate this font. */
+ rc = sfnt_enum_font_1 (fd, file, subtables, 0);
+ xfree (subtables);
+ emacs_close (fd);
+ return rc;
+
+ bail0:
+ emacs_close (fd);
+ bail:
+ return 1;
+}
+
+
+
+/* Font discovery and matching. */
+
+static struct charset *
+sfntfont_charset_for_name (Lisp_Object symbol)
+{
+ ptrdiff_t idx;
+ int id;
+
+ idx = CHARSET_SYMBOL_HASH_INDEX (symbol);
+
+ if (idx == -1)
+ return NULL;
+
+ /* Vcharset_hash_table is not a real variable, so Lisp programs
+ can't clobber it. */
+ id = XFIXNUM (AREF (HASH_VALUE (XHASH_TABLE (Vcharset_hash_table),
+ idx),
+ charset_id));
+
+ return CHARSET_FROM_ID (id);
+}
+
+/* Return the character set corresponding to a cmap subtable SUBTABLE.
+ Value is NULL if the subtable is not supported. */
+
+static struct charset *
+sfntfont_charset_for_cmap (struct sfnt_cmap_encoding_subtable subtable)
+{
+ switch (subtable.platform_id)
+ {
+ case SFNT_PLATFORM_UNICODE:
+ /* Reject variation selector and last resort tables. */
+ if ((subtable.platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ || (subtable.platform_specific_id
+ == SFNT_UNICODE_LAST_RESORT))
+ return NULL;
+
+ /* 1.0, 1.1, ISO-10646-1993, and 2.0_BMP tables are all within
+ the BMP. */
+ if (subtable.platform_specific_id < SFNT_UNICODE_2_0)
+ return sfntfont_charset_for_name (Qunicode_bmp);
+
+ return sfntfont_charset_for_name (Qunicode);
+
+ case SFNT_PLATFORM_MACINTOSH:
+
+ switch (subtable.platform_specific_id)
+ {
+ case SFNT_MACINTOSH_ROMAN:
+ return sfntfont_charset_for_name (Qmac_roman);
+
+ default:
+ /* Some other Macintosh charset not supported by Emacs. */
+ return NULL;
+ }
+
+ case SFNT_PLATFORM_MICROSOFT:
+
+ /* Microsoft specific encodings. */
+
+ switch (subtable.platform_specific_id)
+ {
+ case SFNT_MICROSOFT_SYMBOL:
+ /* Symbols in the Unicode PUA are still Unicode. */
+ return sfntfont_charset_for_name (Qunicode);
+
+ case SFNT_MICROSOFT_UNICODE_BMP:
+ return sfntfont_charset_for_name (Qunicode_bmp);
+
+ case SFNT_MICROSOFT_SHIFT_JIS:
+ /* PCK aka japanese-jisx0208. */
+ return sfntfont_charset_for_name (Qjapanese_jisx0208);
+
+ case SFNT_MICROSOFT_PRC:
+ /* GBK, GB2312 or GB18030. */
+ return sfntfont_charset_for_name (Qgbk);
+
+ case SFNT_MICROSOFT_JOHAB:
+ /* KS C 5601-1992, aka korean-ksc5601. */
+ return sfntfont_charset_for_name (Qkorean_ksc5601);
+
+ case SFNT_MICROSOFT_UNICODE_UCS_4:
+ /* Unicode past the BMP. */
+ return sfntfont_charset_for_name (Qucs);
+ }
+
+ default:
+ return NULL;
+ }
+}
+
+/* Pick the best character map in the cmap table CMAP. Use the
+ subtables in SUBTABLES and DATA. Return the subtable data and the
+ subtable in *SUBTABLE upon success, NULL otherwise.
+
+ If FORMAT14 is non-NULL, return any associated format 14 variation
+ selection context in *FORMAT14 should the selected charcter map be
+ a Unicode character map. */
+
+static struct sfnt_cmap_encoding_subtable_data *
+sfntfont_select_cmap (struct sfnt_cmap_table *cmap,
+ struct sfnt_cmap_encoding_subtable *subtables,
+ struct sfnt_cmap_encoding_subtable_data **data,
+ struct sfnt_cmap_encoding_subtable *subtable,
+ struct sfnt_cmap_format_14 **format14)
+{
+ int i, j;
+
+ /* First look for a non-BMP Unicode cmap. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (data[i] && sfntfont_identify_cmap (subtables[i]) == 2)
+ {
+ *subtable = subtables[i];
+
+ if (!format14)
+ return data[i];
+
+ /* Search for a correspoinding format 14 character map.
+ This is used in conjunction with the selected character
+ map to map variation sequences. */
+
+ for (j = 0; j < cmap->num_subtables; ++j)
+ {
+ if (data[j]
+ && subtables[j].platform_id == SFNT_PLATFORM_UNICODE
+ && (subtables[j].platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ && data[j]->format == 14)
+ *format14 = (struct sfnt_cmap_format_14 *) data[j];
+ }
+
+ return data[i];
+ }
+ }
+
+ /* Next, look for a BMP only Unicode cmap. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (data[i] && sfntfont_identify_cmap (subtables[i]) == 1)
+ {
+ *subtable = subtables[i];
+
+ if (!format14)
+ return data[i];
+
+ /* Search for a correspoinding format 14 character map.
+ This is used in conjunction with the selected character
+ map to map variation sequences. */
+
+ for (j = 0; j < cmap->num_subtables; ++j)
+ {
+ if (data[j]
+ && subtables[j].platform_id == SFNT_PLATFORM_UNICODE
+ && (subtables[j].platform_specific_id
+ == SFNT_UNICODE_VARIATION_SEQUENCES)
+ && data[j]->format == 14)
+ *format14 = (struct sfnt_cmap_format_14 *) data[j];
+ }
+
+ return data[i];
+ }
+ }
+
+ /* Finally, use the first cmap that appears and can be
+ identified. */
+
+ for (i = 0; i < cmap->num_subtables; ++i)
+ {
+ if (data[i] && sfntfont_identify_cmap (subtables[i]) == 0)
+ {
+ *subtable = subtables[i];
+ return data[i];
+ }
+ }
+
+ /* There are no cmaps available to Emacs. */
+ return NULL;
+}
+
+/* Read the cmap from the font descriptor DESC, and place it in CMAP.
+ Keep *CMAP untouched if opening the cmap fails. Set SUBTABLE to
+ the cmap's header upon success. */
+
+static void
+sfntfont_read_cmap (struct sfnt_font_desc *desc,
+ struct sfnt_cmap_encoding_subtable_data **cmap,
+ struct sfnt_cmap_encoding_subtable *subtable)
+{
+ struct sfnt_offset_subtable *font;
+ struct sfnt_cmap_encoding_subtable *subtables;
+ struct sfnt_cmap_encoding_subtable_data **data;
+ struct sfnt_cmap_table *table;
+ int fd, i;
+
+ /* Pick a character map and place it in *CMAP. */
+ fd = emacs_open (desc->path, O_RDONLY, 0);
+
+ if (fd < 0)
+ return;
+
+ font = sfnt_read_table_directory (fd);
+
+ if (!font)
+ {
+ emacs_close (fd);
+ return;
+ }
+
+ table = sfnt_read_cmap_table (fd, font, &subtables,
+ &data);
+ xfree (font);
+
+ if (!table)
+ {
+ emacs_close (fd);
+ return;
+ }
+
+ /* Now pick the best character map. */
+
+ *cmap = sfntfont_select_cmap (table, subtables, data,
+ subtable, NULL);
+
+ /* Free the cmap data. */
+
+ for (i = 0; i < table->num_subtables; ++i)
+ {
+ if (data[i] != *cmap)
+ xfree (data[i]);
+ }
+
+ xfree (data);
+ xfree (subtables);
+ xfree (table);
+ emacs_close (fd);
+}
+
+/* Return whether or not CHARACTER has an associated mapping in CMAP,
+ and the mapping points to a valid glyph. DESC is the font
+ descriptor associated with the font. */
+
+static bool
+sfntfont_glyph_valid (struct sfnt_font_desc *desc,
+ sfnt_char font_character,
+ struct sfnt_cmap_encoding_subtable_data *cmap)
+{
+ sfnt_glyph glyph;
+
+ glyph = sfnt_lookup_glyph (font_character, cmap);
+
+ if (!glyph)
+ return false;
+
+ return glyph <= desc->num_glyphs;
+}
+
+/* Look up a character CHARACTER in the font description DESC. Cache
+ the results. Return true if the character exists, false otherwise.
+
+ If *CMAP is NULL, select a character map for the font and save it
+ there. Otherwise, use the character map in *CMAP. Save data
+ associated with the character map in *SUBTABLE. */
+
+static bool
+sfntfont_lookup_char (struct sfnt_font_desc *desc, Lisp_Object character,
+ struct sfnt_cmap_encoding_subtable_data **cmap,
+ struct sfnt_cmap_encoding_subtable *subtable)
+{
+ Lisp_Object cached;
+ sfnt_char font_character;
+ struct charset *charset;
+ bool present;
+
+ /* Return false for characters that don't fit in a char table. */
+ if (XFIXNUM (character) > INT_MAX || XFIXNUM (character) < 0)
+ return false;
+
+ if (!NILP (desc->char_cache))
+ {
+ cached = char_table_ref (desc->char_cache,
+ XFIXNUM (character));
+ if (!NILP (cached))
+ return (EQ (cached, Qlambda) ? false : true);
+ }
+
+ if (!*cmap && !desc->cmap_invalid)
+ sfntfont_read_cmap (desc, cmap, subtable);
+
+ /* Check that a cmap is now present. */
+ if (!*cmap)
+ {
+ /* Opening the cmap failed. Set desc->cmap_invalid to avoid
+ opening it again. */
+ desc->cmap_invalid = true;
+ return false;
+ }
+
+ /* Otherwise, encode the character. */
+
+ charset = sfntfont_charset_for_cmap (*subtable);
+ if (!charset)
+ /* Emacs missing charsets? */
+ return false;
+
+ font_character = ENCODE_CHAR (charset, (int) XFIXNUM (character));
+
+ if (font_character == CHARSET_INVALID_CODE (charset))
+ return false;
+
+ /* Now return whether or not the glyph is present. Noto Sans
+ Georgian comes with a corrupt format 4 cmap table that somehow
+ tries to express glyphs greater than 65565. */
+ present = sfntfont_glyph_valid (desc, font_character, *cmap);
+
+ /* Cache the result. Store Qlambda when not present, Qt
+ otherwise. */
+
+ if (NILP (desc->char_cache))
+ desc->char_cache = Fmake_char_table (Qfont_lookup_cache,
+ Qnil);
+
+ Fset_char_table_range (desc->char_cache, character,
+ present ? Qt : Qlambda);
+ return present;
+}
+
+/* Return whether or not the specified registry A is ``compatible''
+ with registry B.
+
+ Compatibility does not refer to whether or not the font registries
+ have an identical character set or repertory of characters.
+
+ Instead, it refers to whether or not Emacs expects looking for A to
+ result in fonts used with B. */
+
+static bool
+sfntfont_registries_compatible_p (Lisp_Object a, Lisp_Object b)
+{
+ if (EQ (a, Qiso8859_1) && EQ (b, Qiso10646_1))
+ return true;
+
+ return EQ (a, b);
+}
+
+/* Return whether or not the font description DESC satisfactorily
+ matches the font specification FONT_SPEC.
+
+ Value is 0 if there is no match, -1 if there is a match against
+ DESC itself, and the number of matching instances if the style
+ matches one or more instances defined in in DESC. Return the index
+ of each matching instance in INSTANCES; it should be SIZE big. */
+
+static int
+sfntfont_list_1 (struct sfnt_font_desc *desc, Lisp_Object spec,
+ int *instances, int size)
+{
+ Lisp_Object tem, extra, tail;
+ struct sfnt_cmap_encoding_subtable_data *cmap;
+ size_t i;
+ struct sfnt_cmap_encoding_subtable subtable;
+ int instance, num_instance;
+ Lisp_Object item;
+
+ /* cmap and subtable are caches for sfntfont_lookup_char. */
+
+ /* Check that the family name in SPEC matches DESC->family if it is
+ specified. */
+
+ tem = AREF (spec, FONT_FAMILY_INDEX);
+
+ /* If TEM is a family listed in Vsfnt_default_family_alist,
+ then use that instead. */
+
+ if (SYMBOLP (tem) && CONSP (Vsfnt_default_family_alist))
+ {
+ tail = Vsfnt_default_family_alist;
+ FOR_EACH_TAIL_SAFE (tail)
+ {
+ if (!CONSP (XCAR (tail)))
+ continue;
+
+ if (STRINGP (XCAR (XCAR (tail)))
+ && STRINGP (XCDR (XCAR (tail)))
+ && Fstring_equal (SYMBOL_NAME (tem),
+ XCAR (XCAR (tail))))
+ {
+ /* Special family found. */
+ tem = Fintern (XCDR (XCAR (tail)), Qnil);
+ break;
+ }
+ }
+ }
+
+ if (!NILP (tem) && NILP (Fstring_equal (SYMBOL_NAME (tem),
+ desc->family)))
+ return 0;
+
+ instance = -1;
+
+ /* If a registry is set and wrong, then reject the font desc
+ immediately. This detects 50% of mismatches from fontset.c.
+
+ If DESC->registry is nil, then the registry couldn't be
+ determined beforehand. */
+
+ tem = AREF (spec, FONT_REGISTRY_INDEX);
+ if (!NILP (tem) && !NILP (desc->registry)
+ && !sfntfont_registries_compatible_p (tem, desc->registry))
+ return 0;
+
+ /* Check the style. If DESC is a fixed font, just check once.
+ Otherwise, check each instance. */
+
+ if (NILP (desc->instances))
+ {
+ tem = AREF (spec, FONT_ADSTYLE_INDEX);
+ if (!NILP (tem) && NILP (Fequal (tem, desc->adstyle)))
+ return 0;
+
+ if (FONT_WIDTH_NUMERIC (spec) != -1
+ && FONT_WIDTH_NUMERIC (spec) != desc->width)
+ return 0;
+
+ if (FONT_WEIGHT_NUMERIC (spec) != -1
+ && FONT_WEIGHT_NUMERIC (spec) != desc->weight)
+ return 0;
+
+ if (FONT_SLANT_NUMERIC (spec) != -1
+ && FONT_SLANT_NUMERIC (spec) != desc->slant)
+ return 0;
+ }
+ else
+ {
+ num_instance = 0;
+
+ /* Find the indices of instances in this distortable font which
+ match the given font spec. */
+
+ for (i = 0; i < ASIZE (desc->instances); ++i)
+ {
+ item = AREF (desc->instances, i);
+
+ if (NILP (item))
+ continue;
+
+ /* Check that the adstyle specified matches. */
+
+ tem = AREF (spec, FONT_ADSTYLE_INDEX);
+ if (!NILP (tem) && NILP (Fequal (tem, AREF (item, 1))))
+ continue;
+
+ /* Check the style. */
+
+ if (FONT_WIDTH_NUMERIC (spec) != -1
+ && (FONT_WIDTH_NUMERIC (spec)
+ != XFIXNUM (AREF (item, 2))))
+ continue;
+
+ if (FONT_WEIGHT_NUMERIC (spec) != -1
+ && (FONT_WEIGHT_NUMERIC (spec)
+ != XFIXNUM (AREF (item, 3))))
+ continue;
+
+ if (FONT_SLANT_NUMERIC (spec) != -1
+ && (FONT_SLANT_NUMERIC (spec)
+ != XFIXNUM (AREF (item, 4))))
+ continue;
+
+ if (num_instance == size)
+ break;
+
+ /* A matching instance has been found. Set its index, then
+ go back to the rest of the font matching. */
+ instances[num_instance++] = i;
+ }
+
+ instance = num_instance;
+ }
+
+ /* Handle extras. */
+ extra = AREF (spec, FONT_EXTRA_INDEX);
+
+ if (NILP (extra))
+ return instance;
+
+ tem = assq_no_quit (QCscript, extra);
+ cmap = NULL;
+
+ if (!NILP (tem))
+ {
+ /* If a script has been specified, look up its representative
+ characters and see if they are present in the font. This
+ requires reading the cmap. */
+ tem = assq_no_quit (XCDR (tem), Vscript_representative_chars);
+
+ if (CONSP (tem) && VECTORP (XCDR (tem)))
+ {
+ tem = XCDR (tem);
+
+ /* The vector contains characters, of which one must be
+ present in the font. */
+ for (i = 0; i < ASIZE (tem); ++i)
+ {
+ if (FIXNUMP (AREF (tem, i)))
+ {
+ if (!sfntfont_lookup_char (desc, AREF (tem, i),
+ &cmap, &subtable))
+ goto fail;
+
+ /* One character is enough to pass a font. Don't
+ look at too many. */
+ break;
+ }
+ }
+ }
+ else if (CONSP (tem) && CONSP (XCDR (tem)))
+ {
+ tem = XCDR (tem);
+
+ /* tem is a list of each characters, all of which must be
+ present in the font. */
+ FOR_EACH_TAIL_SAFE (tem)
+ {
+ if (FIXNUMP (XCAR (tem))
+ && !sfntfont_lookup_char (desc, XCAR (tem), &cmap,
+ &subtable))
+ goto fail;
+ }
+
+ /* One or more characters are missing. */
+ if (!NILP (tem))
+ goto fail;
+ }
+ /* Fail if there are no matching fonts at all. */
+ else if (NILP (tem))
+ goto fail;
+ }
+
+ /* 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;
+
+ xfree (cmap);
+ return instance;
+
+ fail:
+ /* The cmap might've been read in and require deallocation. */
+ xfree (cmap);
+ return 0;
+}
+
+/* Type of font entities and font objects created. */
+static Lisp_Object sfnt_vendor_name;
+
+/* Font driver used in font objects created. */
+static const struct font_driver *sfnt_font_driver;
+
+/* Return the font registry corresponding to the font descriptor DESC.
+ Under X, the font registry is an atom registered with the Open
+ Group uniquely identifying the organization which defines the
+ font's character set.
+
+ In practice, the registry overlaps with the character set itself.
+ So Emacs just uses the ``registry'' field to represent both
+ instead. */
+
+static Lisp_Object
+sfntfont_registry_for_desc (struct sfnt_font_desc *desc)
+{
+ struct sfnt_cmap_encoding_subtable_data *cmap;
+
+ cmap = NULL;
+
+ if (desc->cmap_invalid)
+ return Qnil;
+
+ if (desc->subtable.platform_id == 500)
+ {
+ /* Read in the cmap to determine the registry. */
+ sfntfont_read_cmap (desc, &cmap, &desc->subtable);
+
+ if (!cmap)
+ {
+ desc->cmap_invalid = true;
+ return Qnil;
+ }
+ }
+
+ xfree (cmap);
+
+ if (desc->subtable.platform_id != 500)
+ /* desc->subtable.platform_id is now set. CMAP is already free,
+ because it is not actually used. */
+ return sfnt_registry_for_subtable (&desc->subtable);
+
+ return Qnil;
+}
+
+/* Return a font-entity that represents the font descriptor (unopened
+ font) DESC. If INSTANCE is more than or equal to 1, then it is the
+ index of the instance in DESC that should be opened plus 1; in that
+ case, DESC must be a distortable font. */
+
+static Lisp_Object
+sfntfont_desc_to_entity (struct sfnt_font_desc *desc, int instance)
+{
+ Lisp_Object entity, vector;
+
+ entity = font_make_entity ();
+
+ ASET (entity, FONT_TYPE_INDEX, sfnt_vendor_name);
+
+ if (!NILP (desc->designer))
+ ASET (entity, FONT_FOUNDRY_INDEX,
+ Fintern (desc->designer, Qnil));
+
+ ASET (entity, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil));
+ ASET (entity, FONT_ADSTYLE_INDEX, Qnil);
+ ASET (entity, FONT_REGISTRY_INDEX,
+ sfntfont_registry_for_desc (desc));
+
+ /* Size of 0 means the font is scalable. */
+ ASET (entity, FONT_SIZE_INDEX, make_fixnum (0));
+ ASET (entity, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+ ASET (entity, FONT_SPACING_INDEX,
+ make_fixnum (desc->spacing));
+
+ if (instance >= 1)
+ {
+ if (NILP (desc->instances)
+ || instance > ASIZE (desc->instances))
+ emacs_abort ();
+
+ vector = AREF (desc->instances, instance - 1);
+ FONT_SET_STYLE (entity, FONT_WIDTH_INDEX,
+ AREF (vector, 2));
+ FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX,
+ AREF (vector, 3));
+ FONT_SET_STYLE (entity, FONT_SLANT_INDEX,
+ AREF (vector, 4));
+ ASET (entity, FONT_ADSTYLE_INDEX, AREF (vector, 1));
+ }
+ else
+ {
+ FONT_SET_STYLE (entity, FONT_WIDTH_INDEX,
+ make_fixnum (desc->width));
+ FONT_SET_STYLE (entity, FONT_WEIGHT_INDEX,
+ make_fixnum (desc->weight));
+ FONT_SET_STYLE (entity, FONT_SLANT_INDEX,
+ make_fixnum (desc->slant));
+ ASET (entity, FONT_ADSTYLE_INDEX, desc->adstyle);
+ }
+
+ /* Set FONT_EXTRA_INDEX to a pointer to the font description. Font
+ descriptions are never supposed to be freed. */
+
+ ASET (entity, FONT_EXTRA_INDEX,
+ (instance >= 1
+ ? list2 (Fcons (Qfont_entity, make_mint_ptr (desc)),
+ Fcons (Qfont_instance, make_fixnum (instance - 1)))
+ : list1 (Fcons (Qfont_entity, make_mint_ptr (desc)))));
+
+ return entity;
+}
+
+/* Return a list of font-entities matching the specified
+ FONT_SPEC. */
+
+Lisp_Object
+sfntfont_list (struct frame *f, Lisp_Object font_spec)
+{
+ Lisp_Object matching, tem;
+ struct sfnt_font_desc *desc;
+ int i, rc, instances[100];
+
+ matching = Qnil;
+
+ block_input ();
+ /* Returning irrelevant results on receiving an OTF form will cause
+ fontset.c to loop over and over, making displaying some
+ characters very slow. */
+ tem = assq_no_quit (QCotf, AREF (font_spec, FONT_EXTRA_INDEX));
+ if (CONSP (tem) && !NILP (XCDR (tem)))
+ {
+ unblock_input ();
+ return Qnil;
+ }
+
+ /* Loop through known system fonts and add them one-by-one. */
+
+ for (desc = system_fonts; desc; desc = desc->next)
+ {
+ rc = sfntfont_list_1 (desc, font_spec, instances,
+ ARRAYELTS (instances));
+
+ if (rc < 0)
+ matching = Fcons (sfntfont_desc_to_entity (desc, 0),
+ matching);
+ else if (rc)
+ {
+ /* Add each matching instance. */
+
+ for (i = 0; i < rc; ++i)
+ matching = Fcons (sfntfont_desc_to_entity (desc,
+ instances[i] + 1),
+ matching);
+ }
+ }
+
+ unblock_input ();
+
+ return matching;
+}
+
+/* Return the first font-entity matching the specified FONT_SPEC. */
+
+Lisp_Object
+sfntfont_match (struct frame *f, Lisp_Object font_spec)
+{
+ Lisp_Object matches;
+
+ matches = sfntfont_list (f, font_spec);
+
+ if (!NILP (matches))
+ return XCAR (matches);
+
+ return Qnil;
+}
+
+
+
+enum
+ {
+ SFNT_OUTLINE_CACHE_SIZE = 256,
+ SFNT_RASTER_CACHE_SIZE = 128,
+ };
+
+/* Caching subsystem. Generating outlines from glyphs is expensive,
+ and so is rasterizing them, so two caches are maintained for both
+ glyph outlines and rasters.
+
+ Computing metrics also requires some expensive processing if the
+ glyph has instructions or distortions. */
+
+struct sfnt_outline_cache
+{
+ /* Next and last cache buckets. */
+ struct sfnt_outline_cache *next, *last;
+
+ /* Pointer to outline. */
+ struct sfnt_glyph_outline *outline;
+
+ /* Reference to glyph metrics. */
+ struct sfnt_glyph_metrics metrics;
+
+ /* What glyph this caches. */
+ sfnt_glyph glyph;
+};
+
+struct sfnt_raster_cache
+{
+ /* Next and last cache buckets. */
+ struct sfnt_raster_cache *next, *last;
+
+ /* Pointer to raster. */
+ struct sfnt_raster *raster;
+
+ /* What glyph this caches. */
+ sfnt_glyph glyph;
+};
+
+struct sfntfont_get_glyph_outline_dcontext
+{
+ /* Long and short loca tables. */
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_loca_table_short *loca_short;
+
+ /* glyf table. */
+ struct sfnt_glyf_table *glyf;
+
+ /* Variation settings, or NULL. */
+ struct sfnt_blend *blend;
+};
+
+/* Return the glyph identified by GLYPH_ID from the glyf and loca
+ table specified in DCONTEXT. Set *NEED_FREE to true. */
+
+static struct sfnt_glyph *
+sfntfont_get_glyph (sfnt_glyph glyph_id, void *dcontext,
+ bool *need_free)
+{
+ struct sfntfont_get_glyph_outline_dcontext *tables;
+ struct sfnt_glyph *glyph;
+ struct sfnt_metrics_distortion distortion;
+
+ tables = dcontext;
+ *need_free = true;
+
+ glyph = sfnt_read_glyph (glyph_id, tables->glyf,
+ tables->loca_short,
+ tables->loca_long);
+
+ if (tables->blend && glyph)
+ {
+ if (glyph->simple)
+ sfnt_vary_simple_glyph (tables->blend, glyph_id, glyph,
+ &distortion);
+ else
+ sfnt_vary_compound_glyph (tables->blend, glyph_id, glyph,
+ &distortion);
+ }
+
+ /* Note that the distortion is not relevant for compound glyphs. */
+ return glyph;
+}
+
+/* Free the glyph identified by GLYPH. */
+
+static void
+sfntfont_free_glyph (struct sfnt_glyph *glyph, void *dcontext)
+{
+ sfnt_free_glyph (glyph);
+}
+
+/* Dereference the outline OUTLINE. Free it once refcount reaches
+ 0. */
+
+static void
+sfntfont_dereference_outline (struct sfnt_glyph_outline *outline)
+{
+ eassert (outline->refcount > 0);
+
+ if (--outline->refcount)
+ return;
+
+ xfree (outline);
+}
+
+/* Get the outline corresponding to the specified GLYPH_CODE in CACHE.
+ Use the scale factor SCALE, the glyf table GLYF, and the head table
+ HEAD. Keep *CACHE_SIZE updated with the number of elements in the
+ cache.
+
+ Distort the glyph using BLEND if INDEX is not -1.
+
+ Use the offset information in the long or short loca tables
+ LOCA_LONG and LOCA_SHORT, whichever is set.
+
+ Use the specified HMTX, HEAD, HHEA and MAXP tables when instructing
+ compound glyphs.
+
+ If INTERPRETER is non-NULL, then possibly use it and the
+ interpreter graphics STATE to instruct the glyph.
+
+ If METRICS is non-NULL, return the scaled glyph metrics after
+ variation and instructing.
+
+ Return the outline with an incremented reference count and enter
+ the generated outline into CACHE upon success, possibly discarding
+ any older outlines, or NULL on failure. */
+
+static struct sfnt_glyph_outline *
+sfntfont_get_glyph_outline (sfnt_glyph glyph_code,
+ struct sfnt_outline_cache *cache,
+ sfnt_fixed scale, int *cache_size,
+ struct sfnt_blend *blend,
+ int index,
+ struct sfnt_glyf_table *glyf,
+ struct sfnt_head_table *head,
+ struct sfnt_hmtx_table *hmtx,
+ struct sfnt_hhea_table *hhea,
+ struct sfnt_maxp_table *maxp,
+ struct sfnt_loca_table_short *loca_short,
+ struct sfnt_loca_table_long *loca_long,
+ struct sfnt_interpreter *interpreter,
+ struct sfnt_glyph_metrics *metrics,
+ struct sfnt_graphics_state *state)
+{
+ struct sfnt_outline_cache *start;
+ struct sfnt_glyph_outline *outline;
+ struct sfnt_glyph *glyph;
+ struct sfntfont_get_glyph_outline_dcontext dcontext;
+ struct sfnt_instructed_outline *value;
+ const char *error;
+ struct sfnt_glyph_metrics temp;
+ struct sfnt_metrics_distortion distortion;
+
+ start = cache->next;
+ distortion.advance = 0;
+
+ /* See if the outline is already cached. */
+ for (; start != cache; start = start->next)
+ {
+ if (start->glyph == glyph_code)
+ {
+ /* Move start to the start of the ring. Then increase
+ start->outline->refcount and return it. */
+
+ start->last->next = start->next;
+ start->next->last = start->last;
+
+ start->next = cache->next;
+ start->last = cache;
+ start->next->last = start;
+ start->last->next = start;
+ start->outline->refcount++;
+
+ if (metrics)
+ *metrics = start->metrics;
+
+ return start->outline;
+ }
+ }
+
+ /* Not already cached. Get the glyph. */
+ glyph = sfnt_read_glyph (glyph_code, glyf,
+ loca_short, loca_long);
+
+ if (!glyph)
+ return NULL;
+
+ /* Distort the glyph if necessary. */
+
+ if (index != -1)
+ {
+ if (glyph->simple)
+ {
+ if (sfnt_vary_simple_glyph (blend, glyph_code,
+ glyph, &distortion))
+ {
+ sfnt_free_glyph (glyph);
+ return NULL;
+ }
+ }
+ else if (sfnt_vary_compound_glyph (blend, glyph_code,
+ glyph, &distortion))
+ {
+ sfnt_free_glyph (glyph);
+ return NULL;
+ }
+ }
+
+ /* Try to instruct the glyph if INTERPRETER is specified. */
+
+ outline = NULL;
+
+ dcontext.loca_long = loca_long;
+ dcontext.loca_short = loca_short;
+ dcontext.glyf = glyf;
+ dcontext.blend = (index != -1 ? blend : NULL);
+
+ /* Now load the glyph's unscaled metrics into TEMP. */
+
+ if (sfnt_lookup_glyph_metrics (glyph_code, -1, &temp, hmtx, hhea,
+ head, maxp))
+ goto fail;
+
+ /* Add the advance width distortion. */
+ temp.advance += distortion.advance;
+
+ if (interpreter)
+ {
+ if (glyph->simple)
+ {
+ /* Restore the interpreter state from the snapshot taken
+ after loading the preprogram. */
+ interpreter->state = *state;
+
+ error = sfnt_interpret_simple_glyph (glyph, interpreter,
+ &temp, &value);
+ }
+ else
+ /* Restoring the interpreter state is done by
+ sfnt_interpret_compound_glyph; all that must be done here
+ is to give the graphics state to that function. */
+ error = sfnt_interpret_compound_glyph (glyph, interpreter,
+ state,
+ sfntfont_get_glyph,
+ sfntfont_free_glyph,
+ hmtx, hhea, maxp,
+ &temp, &dcontext,
+ &value);
+
+ if (!error)
+ {
+ outline = sfnt_build_instructed_outline (value);
+ xfree (value);
+ }
+ }
+
+ if (!outline)
+ {
+ if (!interpreter)
+ outline = sfnt_build_glyph_outline (glyph, scale,
+ &temp,
+ sfntfont_get_glyph,
+ sfntfont_free_glyph,
+ &dcontext);
+ else
+ outline = sfnt_build_glyph_outline (glyph, scale,
+ &temp,
+ sfntfont_get_glyph,
+ sfntfont_free_glyph,
+ &dcontext);
+ }
+
+ /* At this point, the glyph metrics are unscaled. Scale them up.
+ If INTERPRETER is set, use the scale placed within. */
+
+ sfnt_scale_metrics (&temp, scale);
+
+ fail:
+
+ xfree (glyph);
+
+ if (!outline)
+ return NULL;
+
+ if (index != -1)
+ /* Finally, adjust the left side bearing of the glyph metrics by
+ the origin point of the outline, should a distortion have been
+ applied. The left side bearing is the distance from the origin
+ point to the left most point on the X axis. */
+ temp.lbearing = outline->xmin - outline->origin;
+
+ start = xmalloc (sizeof *start);
+ start->glyph = glyph_code;
+ start->outline = outline;
+ start->metrics = temp;
+
+ /* One reference goes to the cache. The second reference goes to
+ the caller. */
+ outline->refcount = 2;
+
+ /* Link start onto the cache. */
+ start->next = cache->next;
+ start->last = cache;
+ start->next->last = start;
+ start->last->next = start;
+
+ /* Update the cache size. */
+ (*cache_size)++;
+
+ /* Figure out if the least recently used element has to be
+ evicted. */
+ if (*cache_size > SFNT_OUTLINE_CACHE_SIZE)
+ {
+ start = cache->last;
+ eassert (start != cache);
+
+ /* Free the least recently used entry in the cache. */
+ start->last->next = start->next;
+ start->next->last = start->last;
+ sfntfont_dereference_outline (start->outline);
+ xfree (start);
+
+ (*cache_size)--;
+ }
+
+ /* Return the cached outline and metrics. */
+
+ if (metrics)
+ *metrics = temp;
+
+ return outline;
+}
+
+/* Free the outline cache referred to by CACHE. Dereference each
+ outline contained therein. */
+
+static void
+sfntfont_free_outline_cache (struct sfnt_outline_cache *cache)
+{
+ struct sfnt_outline_cache *next, *last;
+
+ /* Handle partly initialized fonts. */
+ if (!cache->next)
+ return;
+
+ for (next = cache->next; next != cache;)
+ {
+ last = next;
+ next = next->next;
+
+ sfntfont_dereference_outline (last->outline);
+ xfree (last);
+ }
+
+ cache->next = cache;
+ cache->last = cache;
+}
+
+/* Dereference the raster RASTER. Free it once refcount reaches
+ 0. */
+
+static void
+sfntfont_dereference_raster (struct sfnt_raster *raster)
+{
+ eassert (raster->refcount > 0);
+
+ if (--raster->refcount)
+ return;
+
+ xfree (raster);
+}
+
+/* Get the raster corresponding to the specified GLYPH_CODE in CACHE.
+ Use the outline named OUTLINE. Keep *CACHE_SIZE updated with the
+ number of elements in the cache. */
+
+static struct sfnt_raster *
+sfntfont_get_glyph_raster (sfnt_glyph glyph_code,
+ struct sfnt_raster_cache *cache,
+ struct sfnt_glyph_outline *outline,
+ int *cache_size)
+{
+ struct sfnt_raster_cache *start;
+ struct sfnt_raster *raster;
+
+ /* See if the raster is already cached. */
+ start = cache->next;
+
+ for (; start != cache; start = start->next)
+ {
+ if (start->glyph == glyph_code)
+ {
+ /* Move start to the start of the ring. Them, increase
+ start->raster->refcount and return it. */
+
+ start->last->next = start->next;
+ start->next->last = start->last;
+
+ start->next = cache->next;
+ start->last = cache;
+ start->next->last = start;
+ start->last->next = start;
+ start->raster->refcount++;
+
+ return start->raster;
+ }
+ }
+
+ /* Not already cached. Raster the outline. */
+ raster = sfnt_raster_glyph_outline (outline);
+
+ if (!raster)
+ return NULL;
+
+ start = xmalloc (sizeof *start);
+ start->glyph = glyph_code;
+ start->raster = raster;
+
+ /* One reference goes to the cache. The second reference goes to
+ the caller. */
+ raster->refcount = 2;
+
+ /* Link start onto the cache. */
+ start->next = cache->next;
+ start->last = cache;
+ start->next->last = start;
+ start->last->next = start;
+
+ /* Update the cache size. */
+ (*cache_size)++;
+
+ /* Figure out if the least recently used element has to be
+ evicted. */
+ if (*cache_size > SFNT_OUTLINE_CACHE_SIZE)
+ {
+ start = cache->last;
+ eassert (start != cache);
+
+ /* Free the least recently used entry in the cache. */
+ start->last->next = start->next;
+ start->next->last = start->last;
+ sfntfont_dereference_raster (start->raster);
+ xfree (start);
+
+ (*cache_size)--;
+ }
+
+ /* Return the cached raster. */
+ return raster;
+}
+
+/* Free the raster cache referred to by CACHE. Dereference each
+ raster contained therein. */
+
+static void
+sfntfont_free_raster_cache (struct sfnt_raster_cache *cache)
+{
+ struct sfnt_raster_cache *next, *last;
+
+ /* Handle partly initialized fonts. */
+ if (!cache->next)
+ return;
+
+ for (next = cache->next; next != cache;)
+ {
+ last = next;
+ next = next->next;
+
+ sfntfont_dereference_raster (last->raster);
+ xfree (last);
+ }
+
+ cache->next = cache;
+ cache->last = cache;
+}
+
+
+
+/* Opening fonts. */
+
+struct sfnt_font_info
+{
+ /* Parent font structure. */
+ struct font font;
+
+#ifdef HAVE_MMAP
+ /* The next font in this chain. */
+ struct sfnt_font_info *next;
+#endif /* HAVE_MMAP */
+
+ /* The font description used to create this font. Used to
+ dereference tables associated with this font. */
+ struct sfnt_font_desc *desc;
+
+ /* Various tables required to use the font. */
+ struct sfnt_cmap_table *cmap;
+ struct sfnt_hhea_table *hhea;
+ struct sfnt_maxp_table *maxp;
+ struct sfnt_head_table *head;
+ struct sfnt_hmtx_table *hmtx;
+ struct sfnt_glyf_table *glyf;
+ struct sfnt_loca_table_short *loca_short;
+ struct sfnt_loca_table_long *loca_long;
+ struct sfnt_prep_table *prep;
+ struct sfnt_fpgm_table *fpgm;
+ struct sfnt_cvt_table *cvt;
+
+ /* The selected character map. */
+ struct sfnt_cmap_encoding_subtable_data *cmap_data;
+
+ /* Data identifying that character map. */
+ struct sfnt_cmap_encoding_subtable cmap_subtable;
+
+ /* The UVS context. */
+ struct sfnt_uvs_context *uvs;
+
+ /* Outline cache. */
+ struct sfnt_outline_cache outline_cache;
+
+ /* Number of elements in the outline cache. */
+ int outline_cache_size;
+
+ /* Raster cache. */
+ struct sfnt_raster_cache raster_cache;
+
+ /* Number of elements in the raster cache. */
+ int raster_cache_size;
+
+ /* Interpreter for grid fitting (if enabled). */
+ struct sfnt_interpreter *interpreter;
+
+ /* Graphics state after the execution of the font and control value
+ programs. */
+ struct sfnt_graphics_state state;
+
+ /* Factor used to convert from em space to pixel space. */
+ sfnt_fixed scale;
+
+ /* The blend (configuration of this multiple master font). */
+ struct sfnt_blend blend;
+
+ /* The index of the named instance used to initialize BLEND.
+ -1 if BLEND is not initialized. */
+ int instance;
+
+#ifdef HAVE_MMAP
+ /* Whether or not the glyph table has been mmapped. */
+ bool glyf_table_mapped;
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+ /* HarfBuzz font object. */
+ hb_font_t *hb_font;
+
+ /* File descriptor associated with this font. */
+ int fd;
+
+ /* The table directory of the font file. */
+ struct sfnt_offset_subtable *directory;
+#endif /* HAVE_HARFBUZZ */
+};
+
+#ifdef HAVE_MMAP
+
+/* List of all open fonts. */
+
+static struct sfnt_font_info *open_fonts;
+
+#endif /* HAVE_MMAP */
+
+/* Look up the glyph corresponding to the character C in FONT. Return
+ 0 upon failure, and the glyph otherwise. */
+
+static sfnt_glyph
+sfntfont_lookup_glyph (struct sfnt_font_info *font_info, int c)
+{
+ struct charset *charset;
+ sfnt_char character;
+ sfnt_glyph glyph;
+
+ charset = CHARSET_FROM_ID (font_info->font.encoding_charset);
+
+ if (!charset)
+ return 0;
+
+ character = ENCODE_CHAR (charset, c);
+
+ if (character == CHARSET_INVALID_CODE (charset))
+ return 0;
+
+ /* Do the actual lookup with the encoded character. */
+ glyph = sfnt_lookup_glyph (character, font_info->cmap_data);
+
+ return glyph;
+}
+
+/* Probe and set FONT_INFO->font.average_width,
+ FONT_INFO->font.space_width, and FONT_INFO->font.min_width
+ according to the tables contained therein. */
+
+static void
+sfntfont_probe_widths (struct sfnt_font_info *font_info)
+{
+ int i, num_characters, total_width;
+ sfnt_glyph glyph;
+ struct sfnt_glyph_metrics metrics;
+
+ num_characters = 0;
+ total_width = 0;
+
+ /* First set some reasonable default values. */
+ font_info->font.average_width = font_info->font.pixel_size;
+ font_info->font.space_width = font_info->font.pixel_size;
+ font_info->font.min_width = 1;
+
+ /* Next, loop through the common ASCII characters. Tally up their
+ advance widths and set space_width if necessary. */
+ for (i = 0; i < 127; ++i)
+ {
+ glyph = sfntfont_lookup_glyph (font_info, i);
+
+ if (!glyph)
+ continue;
+
+ /* Now look up the metrics of this glyph. */
+ if (sfnt_lookup_glyph_metrics (glyph, font_info->font.pixel_size,
+ &metrics, font_info->hmtx,
+ font_info->hhea, font_info->head,
+ font_info->maxp))
+ continue;
+
+ /* Increase the number of characters. */
+ num_characters++;
+
+ /* Add the advance to total_width. */
+ total_width += SFNT_CEIL_FIXED (metrics.advance) / 65536;
+
+ /* Update min_width if it hasn't been set yet or is wider. */
+ if (font_info->font.min_width == 1
+ || font_info->font.min_width > metrics.advance / 65536)
+ font_info->font.min_width = metrics.advance / 65536;
+
+ /* If i is the space character, set the space width. Make sure
+ to round this up. */
+ if (i == 32)
+ font_info->font.space_width
+ = SFNT_CEIL_FIXED (metrics.advance) / 65536;
+ }
+
+ /* Now, if characters were found, set average_width. */
+ if (num_characters)
+ font_info->font.average_width = total_width / num_characters;
+}
+
+/* Initialize the instruction interpreter for INFO. Load the font and
+ preprogram for the pixel size in INFO and its corresponding point
+ size POINT_SIZE. Use the FVAR table in DESC.
+
+ The font tables in INFO must already have been initialized.
+
+ Set INFO->interpreter upon success, and leave that field intact
+ otherwise. */
+
+static void
+sfntfont_setup_interpreter (struct sfnt_font_info *info,
+ struct sfnt_font_desc *desc,
+ int point_size)
+{
+ struct sfnt_cvt_table *cvt;
+ struct sfnt_fpgm_table *fpgm;
+ struct sfnt_prep_table *prep;
+ struct sfnt_interpreter *interpreter;
+ const char *error;
+ struct sfnt_graphics_state state;
+
+ /* Load the cvt, fpgm and prep already read. */
+
+ cvt = info->cvt ;
+ fpgm = info->fpgm;
+ prep = info->prep;
+
+ /* If both fpgm and prep are NULL, this font likely has no
+ instructions, so don't bother setting up the interpreter. */
+
+ if (!fpgm && !prep)
+ goto bail;
+
+ /* If the interpreter does not use the operand stack at all, it is
+ useless. In addition, some broken fonts specify some unnecessary
+ instructions in prep and set head->max_stack_elements to 0.
+
+ Don't create the interpreter in that case. */
+
+ if (!info->maxp->max_stack_elements)
+ goto bail;
+
+ /* Now, create the interpreter using the limits in info->maxp and
+ info->head. CVT can be NULL. */
+
+ interpreter = sfnt_make_interpreter (info->maxp, cvt, info->head,
+ desc->tables->fvar,
+ info->font.pixel_size,
+ point_size);
+
+ /* Bail if the interpreter couldn't be created. */
+ if (!interpreter)
+ goto bail;
+
+ if (fpgm)
+ {
+ /* Otherwise, evaluate the font and cvt programs.
+
+ FIXME: make sure infinite loops inside these programs
+ cannot lock up Emacs. */
+
+ error = sfnt_interpret_font_program (interpreter, fpgm);
+
+ if (error)
+ {
+ /* If an error occurs, log it to the *Messages* buffer. */
+ message_with_string ("While interpreting font program: %s",
+ build_string (error), true);
+ goto bail1;
+ }
+
+ /* Save the graphics state. */
+ state = interpreter->state;
+ }
+
+ if (prep)
+ {
+ /* This will overwrite state if the instruction control is set
+ appropriately. */
+ error = sfnt_interpret_control_value_program (interpreter, prep,
+ &state);
+
+ if (error)
+ {
+ /* If an error occurs, log it to the *Messages* buffer. */
+ message_with_string ("While interpreting preprogram: %s",
+ build_string (error), true);
+ goto bail1;
+ }
+ }
+
+ /* The interpreter has been properly set up. */
+ info->fpgm = fpgm;
+ info->prep = prep;
+ info->cvt = cvt;
+ info->state = state;
+ info->interpreter = interpreter;
+
+ return;
+
+ bail1:
+ xfree (interpreter);
+ bail:
+ return;
+}
+
+/* Free each of the tables opened by `sfnt_open_tables', and possibly
+ file descriptors as well. Then, free TABLES itself. */
+
+static void
+sfnt_close_tables (struct sfnt_font_tables *tables)
+{
+ int rc;
+
+ xfree (tables->cmap);
+ xfree (tables->hhea);
+ xfree (tables->maxp);
+ xfree (tables->head);
+ xfree (tables->hmtx);
+#ifdef HAVE_MMAP
+ if (tables->glyf_table_mapped)
+ {
+ rc = sfnt_unmap_glyf_table (tables->glyf);
+
+ if (rc)
+ emacs_abort ();
+ }
+ else
+#endif /* HAVE_MMAP */
+ xfree (tables->glyf);
+ xfree (tables->loca_short);
+ xfree (tables->loca_long);
+ xfree (tables->prep);
+ xfree (tables->fpgm);
+ xfree (tables->cvt);
+ xfree (tables->fvar);
+ xfree (tables->avar);
+ xfree (tables->gvar);
+ xfree (tables->cvar);
+ xfree (tables->cmap_data);
+
+ if (tables->uvs)
+ sfnt_free_uvs_context (tables->uvs);
+
+#ifdef HAVE_HARFBUZZ
+ /* Close the font file. */
+
+ if (tables->fd != -1)
+ {
+ emacs_close (tables->fd);
+ tables->fd = -1;
+ }
+
+ /* Free its table directory. */
+ xfree (tables->directory);
+ tables->directory = NULL;
+#endif
+}
+
+/* Open font tables associated with the specified font description
+ DESC. Return the font tables, or NULL upon failure. */
+
+static struct sfnt_font_tables *
+sfnt_open_tables (struct sfnt_font_desc *desc)
+{
+ struct sfnt_font_tables *tables;
+ struct sfnt_offset_subtable *subtable;
+ int fd, i, rc;
+ struct sfnt_cmap_encoding_subtable *subtables;
+ struct sfnt_cmap_encoding_subtable_data **data;
+ struct sfnt_cmap_format_14 *format14;
+
+ tables = xzalloc (sizeof *tables);
+
+ /* Open the font. */
+ fd = emacs_open (desc->path, O_RDONLY, 0);
+
+ if (fd == -1)
+ goto bail;
+
+ /* Seek to the offset specified to the table directory. */
+
+ if (desc->offset
+ && lseek (fd, desc->offset, SEEK_SET) != desc->offset)
+ goto bail;
+
+ /* Read the offset subtable. */
+ subtable = sfnt_read_table_directory (fd);
+
+ if (!subtable)
+ goto bail1;
+
+ /* Read required tables. This font backend is supposed to be used
+ mostly on devices with flash memory, so the order in which they
+ are read is insignificant. */
+
+ tables->cmap = sfnt_read_cmap_table (fd, subtable, &subtables,
+ &data);
+ if (!tables->cmap)
+ goto bail2;
+
+ format14 = NULL;
+ tables->cmap_data
+ = sfntfont_select_cmap (tables->cmap,
+ subtables, data,
+ &tables->cmap_subtable,
+ &format14);
+
+ if (format14)
+ {
+ /* Build a UVS context from this format 14 mapping table. A UVS
+ context contains each variation selector supported by the
+ font, and a list of ``non-default'' mappings between base
+ characters and variation glyph IDs. */
+
+ tables->uvs = sfnt_create_uvs_context (format14, fd);
+ xfree (format14);
+ }
+
+ for (i = 0; i < tables->cmap->num_subtables; ++i)
+ {
+ if (data[i] != tables->cmap_data
+ /* format14 has already been freed. */
+ && data[i] != (struct sfnt_cmap_encoding_subtable_data *) format14)
+ xfree (data[i]);
+ }
+
+ xfree (subtables);
+ xfree (data);
+
+ if (!tables->cmap_data)
+ goto bail3;
+
+ /* Read the hhea, maxp, glyf, and head tables. */
+ tables->hhea = sfnt_read_hhea_table (fd, subtable);
+ tables->maxp = sfnt_read_maxp_table (fd, subtable);
+
+#ifdef HAVE_MMAP
+
+ /* First try to map the glyf table. If that fails, then read the
+ glyf table. */
+
+ tables->glyf = sfnt_map_glyf_table (fd, subtable);
+
+ /* Next, if this fails, read the glyf table. */
+
+ if (!tables->glyf)
+#endif /* HAVE_MMAP */
+ tables->glyf = sfnt_read_glyf_table (fd, subtable);
+#ifdef HAVE_MMAP
+ else
+ tables->glyf_table_mapped = true;
+#endif /* HAVE_MMAP */
+
+ tables->head = sfnt_read_head_table (fd, subtable);
+
+ /* If any of those tables couldn't be read, bail. */
+ if (!tables->hhea || !tables->maxp || !tables->glyf
+ || !tables->head)
+ goto bail4;
+
+ /* Now figure out which kind of loca table must be read based on
+ head->index_to_loc_format. */
+
+ if (tables->head->index_to_loc_format)
+ {
+ tables->loca_long
+ = sfnt_read_loca_table_long (fd, subtable);
+
+ if (!tables->loca_long)
+ goto bail4;
+ }
+ else
+ {
+ tables->loca_short
+ = sfnt_read_loca_table_short (fd, subtable);
+
+ if (!tables->loca_short)
+ goto bail4;
+ }
+
+ /* Read the horizontal metrics table. */
+ tables->hmtx = sfnt_read_hmtx_table (fd, subtable,
+ tables->hhea,
+ tables->maxp);
+ if (!tables->hmtx)
+ goto bail5;
+
+ /* Read instruction related font tables. These might not be
+ present, which is OK, since instructing fonts is optional. */
+ tables->prep = sfnt_read_prep_table (fd, subtable);
+ tables->fpgm = sfnt_read_fpgm_table (fd, subtable);
+ tables->cvt = sfnt_read_cvt_table (fd, subtable);
+
+ /* Read distortion related tables. These might not be present. */
+ tables->fvar = sfnt_read_fvar_table (fd, subtable);
+ tables->avar = sfnt_read_avar_table (fd, subtable);
+ tables->gvar = sfnt_read_gvar_table (fd, subtable);
+
+ if (tables->cvt && tables->fvar)
+ tables->cvar = sfnt_read_cvar_table (fd, subtable, tables->fvar,
+ tables->cvt);
+
+#ifdef HAVE_HARFBUZZ
+ /* Now copy over the subtable if necessary, as it is needed to read
+ extra font tables required by HarfBuzz. */
+ tables->directory = subtable;
+ tables->fd = fd;
+#else /* !HAVE_HARFBUZZ */
+ /* Otherwise, close the fd and free the table directory. */
+ xfree (subtable);
+ emacs_close (fd);
+#endif /* HAVE_HARFBUZZ */
+
+ return tables;
+
+ bail5:
+ xfree (tables->loca_long);
+ xfree (tables->loca_short);
+ bail4:
+ xfree (tables->hhea);
+ xfree (tables->maxp);
+
+#ifdef HAVE_MMAP
+ if (tables->glyf_table_mapped)
+ {
+ rc = sfnt_unmap_glyf_table (tables->glyf);
+
+ if (rc)
+ emacs_abort ();
+ }
+ else
+#endif /* HAVE_MMAP */
+ xfree (tables->glyf);
+
+ xfree (tables->head);
+
+ /* This comes under bail4 due to a peculiarity of how the four
+ tables above are validated. */
+ xfree (tables->cmap_data);
+ bail3:
+ if (tables->uvs)
+ sfnt_free_uvs_context (tables->uvs);
+
+ xfree (tables->cmap);
+ bail2:
+ xfree (subtable);
+ bail1:
+ emacs_close (fd);
+ bail:
+ xfree (tables);
+ return NULL;
+}
+
+/* Open or reference font tables corresponding to the specified font
+ DESC. Return NULL upon failure. */
+
+static struct sfnt_font_tables *
+sfnt_reference_font_tables (struct sfnt_font_desc *desc)
+{
+ if (desc->refcount)
+ {
+ desc->refcount++;
+ return desc->tables;
+ }
+
+ desc->tables = sfnt_open_tables (desc);
+
+ if (!desc->tables)
+ return NULL;
+
+ desc->refcount++;
+ return desc->tables;
+}
+
+/* Dereference font tables corresponding to the specified font
+ DESC. */
+
+static void
+sfnt_dereference_font_tables (struct sfnt_font_desc *desc)
+{
+ if (!desc->refcount)
+ emacs_abort ();
+
+ if (--desc->refcount)
+ return;
+
+ sfnt_close_tables (desc->tables);
+ desc->tables = NULL;
+ return;
+}
+
+/* Open the font corresponding to the font-entity FONT_ENTITY. Return
+ nil upon failure, else the opened font-object. */
+
+Lisp_Object
+sfntfont_open (struct frame *f, Lisp_Object font_entity,
+ int pixel_size)
+{
+ struct sfnt_font_info *font_info;
+ struct font *font;
+ struct sfnt_font_desc *desc;
+ Lisp_Object font_object;
+ struct charset *charset;
+ int point_size, instance, i;
+ Display_Info *dpyinfo;
+ struct sfnt_font_tables *tables;
+ Lisp_Object tem;
+
+ if (XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX)) != 0)
+ pixel_size = XFIXNUM (AREF (font_entity, FONT_SIZE_INDEX));
+ else if (pixel_size == 0)
+ {
+ /* This bit was copied from xfont.c. The values might need
+ adjustment. */
+
+ if (FRAME_FONT (f))
+ pixel_size = FRAME_FONT (f)->pixel_size;
+ else
+ pixel_size = 12;
+ }
+
+ /* Now find the font description corresponding to FONT_ENTITY. */
+
+ tem = AREF (font_entity, FONT_EXTRA_INDEX);
+ if (NILP (tem))
+ return Qnil;
+
+ desc = xmint_pointer (XCDR (XCAR (tem)));
+
+ /* Finally, see if a specific instance is associated with
+ FONT_ENTITY. */
+
+ instance = -1;
+ if (!NILP (XCDR (tem)))
+ instance = XFIXNUM (XCDR (XCAR (XCDR (tem))));
+
+ /* Build the font object. */
+ font_object = font_make_object (VECSIZE (struct sfnt_font_info),
+ font_entity, pixel_size);
+ font_info = (struct sfnt_font_info *) XFONT_OBJECT (font_object);
+
+ block_input ();
+
+ /* Initialize all the font driver specific data. */
+
+ font_info->cmap = NULL;
+ font_info->hhea = NULL;
+ font_info->maxp = NULL;
+ font_info->head = NULL;
+ font_info->glyf = NULL;
+ font_info->hmtx = NULL;
+ font_info->loca_short = NULL;
+ font_info->loca_long = NULL;
+ font_info->cmap_data = NULL;
+ font_info->prep = NULL;
+ font_info->fpgm = NULL;
+ font_info->cvt = NULL;
+ font_info->uvs = NULL;
+
+ font_info->outline_cache.next = &font_info->outline_cache;
+ font_info->outline_cache.last = &font_info->outline_cache;
+ font_info->outline_cache_size = 0;
+ font_info->raster_cache.next = &font_info->raster_cache;
+ font_info->raster_cache.last = &font_info->raster_cache;
+ font_info->raster_cache_size = 0;
+ font_info->interpreter = NULL;
+ font_info->scale = 0;
+ font_info->instance = -1;
+ font_info->blend.coords = NULL;
+#ifdef HAVE_MMAP
+ font_info->glyf_table_mapped = false;
+#endif /* HAVE_MMAP */
+#ifdef HAVE_HARFBUZZ
+ font_info->hb_font = NULL;
+ font_info->fd = -1;
+ font_info->directory = NULL;
+#endif /* HAVE_HARFBUZZ */
+
+ /* Read required tables. This font backend is supposed to be used
+ mostly on devices with flash memory, so the order in which they
+ are read is insignificant. */
+
+ tables = sfnt_reference_font_tables (desc);
+
+ if (!tables)
+ goto bail;
+
+ /* Copy fields from the table structure to the font for fast
+ access. */
+ font_info->cmap = tables->cmap;
+ font_info->hhea = tables->hhea;
+ font_info->maxp = tables->maxp;
+ font_info->head = tables->head;
+ font_info->hmtx = tables->hmtx;
+ font_info->glyf = tables->glyf;
+ font_info->loca_short = tables->loca_short;
+ font_info->loca_long = tables->loca_long;
+ font_info->prep = tables->prep;
+ font_info->fpgm = tables->fpgm;
+ font_info->cvt = tables->cvt ;
+ font_info->cmap_data = tables->cmap_data;
+ font_info->cmap_subtable = tables->cmap_subtable;
+ font_info->uvs = tables->uvs;
+
+ /* Calculate the font's scaling factor. */
+ font_info->scale = sfnt_get_scale (font_info->head, pixel_size);
+
+ /* Fill in font data. */
+ font = &font_info->font;
+ font->pixel_size = pixel_size;
+ font->driver = sfnt_font_driver;
+ font->encoding_charset = font->repertory_charset = -1;
+
+ /* Figure out which character set to use. */
+ charset = sfntfont_charset_for_cmap (font_info->cmap_subtable);
+
+ if (!charset)
+ goto bail6;
+
+ /* Set the character set IDs. */
+ font->encoding_charset = charset->id;
+ font->repertory_charset = charset->id;
+
+ /* Figure out the font ascent and descent. */
+ font->ascent
+ = ceil (font_info->hhea->ascent
+ * pixel_size * 1.0 / font_info->head->units_per_em);
+ font->descent
+ = -floor (font_info->hhea->descent
+ * pixel_size * 1.0 / font_info->head->units_per_em);
+ font->height = font->ascent + font->descent;
+
+ /* Set font->max_width to the maximum advance width. */
+ font->max_width = (font_info->hhea->advance_width_max
+ * pixel_size * 1.0 / font_info->head->units_per_em);
+
+ /* Set generic attributes such as type and style. */
+ ASET (font_object, FONT_TYPE_INDEX, sfnt_vendor_name);
+
+ if (!NILP (desc->designer))
+ ASET (font_object, FONT_FOUNDRY_INDEX,
+ Fintern (desc->designer, Qnil));
+
+ ASET (font_object, FONT_FAMILY_INDEX, Fintern (desc->family, Qnil));
+ ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);
+ ASET (font_object, FONT_REGISTRY_INDEX,
+ sfntfont_registry_for_desc (desc));
+
+ /* Size of 0 means the font is scalable. */
+ ASET (font_object, FONT_SIZE_INDEX, make_fixnum (0));
+ ASET (font_object, FONT_AVGWIDTH_INDEX, make_fixnum (0));
+ ASET (font_object, FONT_SPACING_INDEX,
+ make_fixnum (desc->spacing));
+
+ /* Set the font style. */
+
+ FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX,
+ make_fixnum (desc->width));
+ FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX,
+ make_fixnum (desc->weight));
+ FONT_SET_STYLE (font_object, FONT_SLANT_INDEX,
+ make_fixnum (desc->slant));
+
+ ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);
+
+ /* Find out the minimum, maximum and average widths. */
+ sfntfont_probe_widths (font_info);
+
+ /* Clear various offsets. */
+ font_info->font.baseline_offset = 0;
+ font_info->font.relative_compose = 0;
+ font_info->font.default_ascent = 0;
+ font_info->font.vertical_centering = 0;
+ font_info->font.underline_position = -1;
+ font_info->font.underline_thickness = 0;
+
+ /* Now try to set up grid fitting for this font. */
+ dpyinfo = FRAME_DISPLAY_INFO (f);
+ point_size = PIXEL_TO_POINT (pixel_size, (dpyinfo->resx
+ * dpyinfo->resy
+ / 2));
+ sfntfont_setup_interpreter (font_info, desc, point_size);
+
+ /* If an instance was specified and the font is distortable, set up
+ the blend. */
+
+ if (instance != -1
+ && desc->tables->fvar && desc->tables->gvar
+ /* Make sure the instance is within range. */
+ && instance < desc->tables->fvar->instance_count)
+ {
+ tem = AREF (desc->instances, instance);
+
+ if (!NILP (tem))
+ {
+ sfnt_init_blend (&font_info->blend, desc->tables->fvar,
+ desc->tables->gvar, desc->tables->avar,
+ desc->tables->cvar);
+
+ /* Copy over the coordinates. */
+ for (i = 0; i < desc->tables->fvar->axis_count; ++i)
+ font_info->blend.coords[i]
+ = desc->tables->fvar->instance[instance].coords[i];
+
+ sfnt_normalize_blend (&font_info->blend);
+
+ /* Test whether or not the instance is actually redundant,
+ as all of its axis are at their default values. If so,
+ free the instance. */
+
+ for (i = 0; i < desc->tables->fvar->axis_count; ++i)
+ {
+ if (font_info->blend.norm_coords[i])
+ break;
+ }
+
+ if (i == desc->tables->fvar->axis_count)
+ {
+ sfnt_free_blend (&font_info->blend);
+ goto cancel_blend;
+ }
+
+ /* If an interpreter was specified, distort it now. */
+
+ if (font_info->interpreter)
+ sfnt_vary_interpreter (font_info->interpreter,
+ &font_info->blend);
+
+ font_info->instance = instance;
+
+ /* Replace the style information with that of the
+ instance. */
+
+ FONT_SET_STYLE (font_object, FONT_WIDTH_INDEX,
+ AREF (tem, 2));
+ FONT_SET_STYLE (font_object, FONT_WEIGHT_INDEX,
+ AREF (tem, 3));
+ FONT_SET_STYLE (font_object, FONT_SLANT_INDEX,
+ AREF (tem, 4));
+ ASET (font_object, FONT_ADSTYLE_INDEX, Qnil);
+ }
+ }
+
+ cancel_blend:
+ /* Calculate the xfld name. */
+ font->props[FONT_NAME_INDEX] = Ffont_xlfd_name (font_object, Qnil);
+
+#ifdef HAVE_HARFBUZZ
+ /* HarfBuzz will potentially read font tables after the font has
+ been opened by Emacs. Keep the font open, and record its offset
+ subtable. */
+ font_info->fd = tables->fd;
+ font_info->directory = tables->directory;
+#endif /* HAVE_HARFBUZZ */
+
+ /* Set font->desc so that font tables can be dereferenced if
+ anything goes wrong. */
+ font_info->desc = desc;
+
+#ifdef HAVE_MMAP
+ /* Link the font onto the font table. */
+ font_info->next = open_fonts;
+ open_fonts = font_info;
+#endif /* HAVE_MMAP */
+
+ /* All done. */
+ unblock_input ();
+ return font_object;
+
+ bail6:
+ sfnt_dereference_font_tables (desc);
+ font_info->desc = NULL;
+ bail:
+ unblock_input ();
+ return Qnil;
+}
+
+
+
+/* Metrics computation and other similar font backend functions. */
+
+/* Return the glyph code corresponding to C inside the font-object
+ FONT. Value is the glyph code upon success, else
+ FONT_INVALID_CODE. */
+
+unsigned int
+sfntfont_encode_char (struct font *font, int c)
+{
+ sfnt_glyph glyph;
+
+ /* Now look up the glyph. */
+ glyph = sfntfont_lookup_glyph ((struct sfnt_font_info *) font, c);
+
+ if (!glyph)
+ return FONT_INVALID_CODE;
+
+ return glyph;
+}
+
+/* Measure the single glyph GLYPH in the font FONT and return its
+ metrics in *PCM.
+
+ Instruct the glyph if possible.
+
+ Value is 0 upon success, 1 otherwise. */
+
+static int
+sfntfont_measure_pcm (struct sfnt_font_info *font, sfnt_glyph glyph,
+ struct font_metrics *pcm)
+{
+ struct sfnt_glyph_metrics metrics;
+ struct sfnt_glyph_outline *outline;
+
+ /* Now get the glyph outline, which is required to obtain the rsb,
+ ascent and descent. */
+ outline = sfntfont_get_glyph_outline (glyph, &font->outline_cache,
+ font->scale,
+ &font->outline_cache_size,
+ &font->blend,
+ font->instance,
+ font->glyf, font->head,
+ font->hmtx, font->hhea,
+ font->maxp,
+ font->loca_short,
+ font->loca_long,
+ font->interpreter, &metrics,
+ &font->state);
+
+ if (!outline)
+ return 1;
+
+ /* Round the left side bearing downwards. */
+ pcm->lbearing = SFNT_FLOOR_FIXED (metrics.lbearing) / 65536;
+ pcm->rbearing = SFNT_CEIL_FIXED (outline->xmax) / 65536;
+
+ /* Round the advance, ascent and descent upwards. */
+ pcm->width = SFNT_CEIL_FIXED (metrics.advance) / 65536;
+ pcm->ascent = SFNT_CEIL_FIXED (outline->ymax) / 65536;
+ pcm->descent = SFNT_CEIL_FIXED (-outline->ymin) / 65536;
+
+ sfntfont_dereference_outline (outline);
+ return 0;
+}
+
+/* Return the total text extents of NGLYPHS glyphs given as CODE in
+ the single font metrics array METRICS. */
+
+void
+sfntfont_text_extents (struct font *font, const unsigned int *code,
+ int nglyphs, struct font_metrics *metrics)
+{
+ int i, total_width;
+ struct font_metrics pcm;
+
+ total_width = 0;
+
+ /* First clear the metrics array. */
+ memset (metrics, 0, sizeof *metrics);
+
+ /* Get the metrcs one by one, then sum them up. */
+ for (i = 0; i < nglyphs; ++i)
+ {
+ if (!sfntfont_measure_pcm ((struct sfnt_font_info *) font,
+ code[i], &pcm))
+ {
+ /* Add the per-char metric (PCM) to the metrics in
+ METRICS. */
+
+ if (total_width + pcm.lbearing < metrics->lbearing)
+ metrics->lbearing = total_width + pcm.lbearing;
+
+ if (total_width + pcm.rbearing > metrics->rbearing)
+ metrics->rbearing = total_width + pcm.rbearing;
+
+ if (pcm.ascent > metrics->ascent)
+ metrics->ascent = pcm.ascent;
+
+ if (pcm.descent > metrics->descent)
+ metrics->descent = pcm.descent;
+
+ total_width += pcm.width;
+ }
+ }
+
+ metrics->width = total_width;
+}
+
+/* Close the font FONT, discarding all tables inside it and
+ dereferencing all cached outlines and rasters. */
+
+void
+sfntfont_close (struct font *font)
+{
+ struct sfnt_font_info *info;
+#ifdef HAVE_MMAP
+ struct sfnt_font_info **next;
+#endif /* HAVE_MMAP */
+
+ info = (struct sfnt_font_info *) font;
+
+ /* If info->desc is still set, dereference the font tables. */
+ if (info->desc)
+ sfnt_dereference_font_tables (info->desc);
+ info->desc = NULL;
+
+ /* Free the interpreter, which is created on a per font basis. */
+ xfree (info->interpreter);
+
+ /* Clear these fields. It seems that close can be called twice,
+ once during font driver destruction, and once during GC. */
+
+ info->cmap = NULL;
+ info->hhea = NULL;
+ info->maxp = NULL;
+ info->head = NULL;
+ info->hhea = NULL;
+ info->glyf = NULL;
+ info->loca_short = NULL;
+ info->loca_long = NULL;
+ info->cmap_data = NULL;
+ info->prep = NULL;
+ info->fpgm = NULL;
+ info->cvt = NULL;
+ info->interpreter = NULL;
+ info->uvs = NULL;
+
+ /* Deinitialize the blend. */
+ if (info->instance != -1 && info->blend.coords)
+ sfnt_free_blend (&info->blend);
+ info->instance = -1;
+
+#ifdef HAVE_MMAP
+
+ /* Unlink INFO. */
+
+ next = &open_fonts;
+ while (*next && (*next) != info)
+ next = &(*next)->next;
+
+ if (*next)
+ *next = info->next;
+ info->next = NULL;
+
+#endif /* HAVE_MMAP */
+
+#ifdef HAVE_HARFBUZZ
+ /* These fields will be freed or closed by
+ sfnt_dereference_font_tables, but clear them here for good
+ measure. */
+ info->directory = NULL;
+ info->fd = -1;
+
+ /* Free any hb_font created. */
+
+ if (info->hb_font)
+ {
+ hb_font_destroy (info->hb_font);
+ info->hb_font = NULL;
+ }
+#endif
+
+ sfntfont_free_outline_cache (&info->outline_cache);
+ sfntfont_free_raster_cache (&info->raster_cache);
+}
+
+
+
+/* Glyph display. */
+
+/* Function called to actually draw rasters to the glass. */
+static sfntfont_put_glyph_proc sfnt_put_glyphs;
+
+/* Draw glyphs in S->char2b starting from FROM to TO, with the origin
+ at X and baseline at Y. Fill the background from X, Y +
+ FONT_DESCENT to X + S->background_width, Y - FONT_ASCENT with the
+ background color if necessary. Use the foreground and background
+ colors in S->gc. */
+
+int
+sfntfont_draw (struct glyph_string *s, int from, int to,
+ int x, int y, bool with_background)
+{
+ int length;
+ struct sfnt_raster **rasters;
+ int *x_coords, current_x, i;
+ struct sfnt_glyph_outline *outline;
+ struct font *font;
+ struct sfnt_font_info *info;
+ struct sfnt_glyph_metrics metrics;
+
+ length = to - from;
+ font = s->font;
+ info = (struct sfnt_font_info *) font;
+
+ rasters = alloca (length * sizeof *rasters);
+ x_coords = alloca (length * sizeof *x_coords);
+ current_x = x;
+
+ /* Get rasters and outlines for them. */
+ for (i = from; i < to; ++i)
+ {
+ /* Look up the outline. */
+ outline = sfntfont_get_glyph_outline (s->char2b[i],
+ &info->outline_cache,
+ info->scale,
+ &info->outline_cache_size,
+ &info->blend,
+ info->instance,
+ info->glyf, info->head,
+ info->hmtx, info->hhea,
+ info->maxp,
+ info->loca_short,
+ info->loca_long,
+ info->interpreter,
+ &metrics,
+ &info->state);
+ x_coords[i - from] = 0;
+
+ if (!outline)
+ {
+ rasters[i - from] = NULL;
+ continue;
+ }
+
+ /* Rasterize the outline. */
+ rasters[i - from] = sfntfont_get_glyph_raster (s->char2b[i],
+ &info->raster_cache,
+ outline,
+ &info->raster_cache_size);
+ sfntfont_dereference_outline (outline);
+
+ if (!rasters[i - from])
+ continue;
+
+ /* Now work out where to put the outline. */
+ x_coords[i - from] = current_x;
+
+ if (s->padding_p)
+ current_x += 1;
+ else
+ current_x += SFNT_CEIL_FIXED (metrics.advance) / 65536;
+ }
+
+ /* Call the window system function to put the glyphs to the
+ frame. */
+ sfnt_put_glyphs (s, from, to, x, y, with_background,
+ rasters, x_coords);
+
+ /* Dereference all the rasters. */
+ for (i = 0; i < from - to; ++i)
+ {
+ if (rasters[i])
+ sfntfont_dereference_raster (rasters[i]);
+ }
+
+ return 1;
+}
+
+
+
+/* Other callbacks. */
+
+/* Return a list of each font family known to Emacs. F is supposed to
+ be a frame but is ignored. */
+
+Lisp_Object
+sfntfont_list_family (struct frame *f)
+{
+ Lisp_Object families;
+ struct sfnt_font_desc *desc;
+
+ families = Qnil;
+
+ for (desc = system_fonts; desc; desc = desc->next)
+ /* Add desc->family to the list. */
+ families = Fcons (desc->family, families);
+
+ /* Not sure if deleting duplicates is worth it. Is this ever
+ called? */
+ return families;
+}
+
+
+
+/* Unicode Variation Selector (UVS) support. This is typically
+ required for Harfbuzz. */
+
+/* Given a FONT object, a character C, and VARIATIONS, return the
+ number of non-default variation glyphs, and their glyph ids in
+ VARIATIONS.
+
+ For each variation selector character K with a non-default glyph in
+ the variation selector range 0xFE00 to 0xFE0F, set variations[K -
+ 0xFE0] to its ID.
+
+ For each variation selector character K with a non-default glyph in
+ the variation selector range 0xE0100 to 0xE01EF, set variations[K -
+ 0xE0100 + 16] to its ID.
+
+ If value is more than 0, set all other members of VARIATIONS to 0.
+ Else, the contents of VARIATIONS are undefined. */
+
+int
+sfntfont_get_variation_glyphs (struct font *font, int c,
+ unsigned variations[256])
+{
+ struct sfnt_font_info *info;
+ size_t i;
+ int n;
+ struct sfnt_mapped_variation_selector_record *record;
+
+ info = (struct sfnt_font_info *) font;
+ n = 0;
+
+ /* Return 0 if there is no UVS mapping table. */
+
+ if (!info->uvs)
+ return 0;
+
+ /* Clear the variations array. */
+
+ memset (variations, 0, sizeof *variations * 256);
+
+ /* Find the first 0xFExx selector. */
+
+ i = 0;
+ while (i < info->uvs->num_records
+ && info->uvs->records[i].selector < 0xfe00)
+ ++i;
+
+ /* Fill in selectors 0 to 15. */
+
+ while (i < info->uvs->num_records
+ && info->uvs->records[i].selector <= 0xfe0f)
+ {
+ record = &info->uvs->records[i];
+
+ /* If record has no non-default mappings, continue on to the
+ next selector. */
+
+ if (!record->nondefault_uvs)
+ goto next_selector;
+
+ /* Handle invalid unsorted tables. */
+
+ if (record->selector < 0xfe00)
+ return 0;
+
+ /* Find the glyph ID associated with C and put it in
+ VARIATIONS. */
+
+ variations[info->uvs->records[i].selector - 0xfe00]
+ = sfnt_variation_glyph_for_char (record->nondefault_uvs, c);
+
+ if (variations[info->uvs->records[i].selector - 0xfe00])
+ ++n;
+
+ next_selector:
+ ++i;
+ }
+
+ /* Find the first 0xE0100 selector. */
+
+ i = 0;
+ while (i < info->uvs->num_records
+ && info->uvs->records[i].selector < 0xe0100)
+ ++i;
+
+ /* Fill in selectors 16 to 255. */
+
+ while (i < info->uvs->num_records
+ && info->uvs->records[i].selector <= 0xe01ef)
+ {
+ record = &info->uvs->records[i];
+
+ /* If record has no non-default mappings, continue on to the
+ next selector. */
+
+ if (!record->nondefault_uvs)
+ goto next_selector_1;
+
+ /* Handle invalid unsorted tables. */
+
+ if (record->selector < 0xe0100)
+ return 0;
+
+ /* Find the glyph ID associated with C and put it in
+ VARIATIONS. */
+
+ variations[info->uvs->records[i].selector - 0xe0100 + 16]
+ = sfnt_variation_glyph_for_char (record->nondefault_uvs, c);
+
+ if (variations[info->uvs->records[i].selector - 0xe0100 + 16])
+ ++n;
+
+ next_selector_1:
+ ++i;
+ }
+
+ return n;
+}
+
+
+
+/* mmap specific stuff. */
+
+#ifdef HAVE_MMAP
+
+/* Return whether or not ADDR lies in a mapped glyph, and bus faults
+ should be ignored. */
+
+bool
+sfntfont_detect_sigbus (void *addr)
+{
+ struct sfnt_font_info *info;
+
+ for (info = open_fonts; info; info = info->next)
+ {
+ if (info->glyf_table_mapped
+ && (unsigned char *) addr >= info->glyf->glyphs
+ && (unsigned char *) addr < (info->glyf->glyphs
+ + info->glyf->size))
+ return true;
+ }
+
+ return false;
+}
+
+#endif
+
+
+
+/* Harfbuzz font support. */
+
+#ifdef HAVE_HARFBUZZ
+
+#ifdef HAVE_MMAP
+
+/* Unmap the specified table. */
+
+static void
+sfntfont_unmap_blob (void *ptr)
+{
+ if (sfnt_unmap_table (ptr))
+ emacs_abort ();
+
+ xfree (ptr);
+}
+
+#endif /* HAVE_MMAP */
+
+/* Given a font DATA and a tag TAG, return the data of the
+ corresponding font table as a HarfBuzz blob. */
+
+static hb_blob_t *
+sfntfont_get_font_table (hb_face_t *face, hb_tag_t tag, void *data)
+{
+ size_t size;
+ struct sfnt_font_info *info;
+#ifdef HAVE_MMAP
+ struct sfnt_mapped_table *table;
+ hb_blob_t *blob;
+
+ info = data;
+ table = xmalloc (sizeof *table);
+
+ if (!sfnt_map_table (info->fd, info->directory, tag,
+ table))
+ {
+ /* Create an hb_blob_t and return it.
+ TODO: record this mapping properly so that SIGBUS can
+ be handled. */
+
+ blob = hb_blob_create (table->data, table->length,
+ HB_MEMORY_MODE_READONLY,
+ table, sfntfont_unmap_blob);
+
+ /* Note that sfntfont_unmap_blob will be called if the empty
+ blob is returned. */
+ return blob;
+ }
+
+ xfree (table);
+#else /* !HAVE_MMAP */
+
+ /* Try to read the table conventionally. */
+ info = data;
+#endif /* HAVE_MMAP */
+
+ data = sfnt_read_table (info->fd, info->directory, tag,
+ &size);
+
+ if (!data)
+ return NULL;
+
+ return hb_blob_create (data, size, HB_MEMORY_MODE_WRITABLE,
+ data, xfree);
+}
+
+/* Create or return a HarfBuzz font object corresponding to the
+ specified FONT. Return the scale to convert between fwords and
+ pixels in POSITION_UNIT. */
+
+hb_font_t *
+sfntfont_begin_hb_font (struct font *font, double *position_unit)
+{
+ struct sfnt_font_info *info;
+ hb_face_t *face;
+ int factor;
+
+ info = (struct sfnt_font_info *) font;
+
+ if (info->hb_font)
+ {
+ /* Calculate the scale factor. */
+ *position_unit = 1.0 / 64.0;
+ return info->hb_font;
+ }
+
+ /* Create a face and then a font. */
+ face = hb_face_create_for_tables (sfntfont_get_font_table, font,
+ NULL);
+
+ if (hb_face_get_glyph_count (face) > 0)
+ {
+ info->hb_font = hb_font_create (face);
+ if (!info->hb_font)
+ goto bail;
+
+ factor = font->pixel_size;
+
+ /* Set the scale and PPEM values. */
+ hb_font_set_scale (info->hb_font, factor * 64, factor * 64);
+ hb_font_set_ppem (info->hb_font, factor, factor);
+
+#ifdef HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE
+ /* Set the instance if this is a distortable font. */
+ if (info->instance != -1)
+ hb_font_set_var_named_instance (info->hb_font,
+ info->instance);
+#endif /* HAVE_HB_FONT_SET_VAR_NAMED_INSTANCE */
+
+ /* This is needed for HarfBuzz before 2.0.0; it is the default
+ in later versions. */
+ hb_ot_font_set_funcs (info->hb_font);
+ }
+
+ bail:
+ hb_face_destroy (face);
+
+ /* Calculate the scale factor. */
+ *position_unit = 1.0 / 64.0;
+ return info->hb_font;
+}
+
+#endif /* HAVE_HARFBUZZ */
+
+
+
+void
+syms_of_sfntfont (void)
+{
+ DEFSYM (Qutf_16be, "utf-16be");
+ DEFSYM (Qmac_roman, "mac-roman");
+ DEFSYM (Qchinese_big5, "chinese-big5");
+ DEFSYM (Qunicode_bmp, "unicode-bmp");
+ DEFSYM (Qucs, "ucs");
+ DEFSYM (Qjapanese_jisx0208, "japanese-jisx0208");
+ DEFSYM (Qgbk, "gbk");
+ DEFSYM (Qkorean_ksc5601, "korean-ksc5601");
+ 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");
+ DEFSYM (Qfont_instance, "font-instance");
+
+ /* Char-table purpose. */
+ DEFSYM (Qfont_lookup_cache, "font-lookup-cache");
+
+ /* Set up staticpros. */
+ sfnt_vendor_name = Qnil;
+ staticpro (&sfnt_vendor_name);
+
+ /* This variable is supposed to be set by the platform specific part
+ of the font backend. */
+ DEFVAR_LISP ("sfnt-default-family-alist", Vsfnt_default_family_alist,
+ doc: /* Alist between "emulated" and actual font family names.
+
+Much Emacs code assumes that font families named "Monospace" and "Sans
+Serif" exist, and map to the default monospace and Sans Serif fonts on
+a system. When the `sfnt' font driver is asked to look for a font
+with one of the families in this alist, it uses its value instead. */);
+ Vsfnt_default_family_alist = Qnil;
+}
+
+void
+mark_sfntfont (void)
+{
+ struct sfnt_font_desc *desc;
+
+ /* Mark each font desc. */
+ for (desc = system_fonts; desc; desc = desc->next)
+ {
+ mark_object (desc->family);
+ mark_object (desc->style);
+ mark_object (desc->adstyle);
+ mark_object (desc->instances);
+ mark_object (desc->languages);
+ mark_object (desc->registry);
+ mark_object (desc->char_cache);
+ mark_object (desc->designer);
+ }
+}
+
+void
+init_sfntfont (void)
+{
+
+}
+
+
+
+/* Initialize the sfntfont font driver. VENDOR_TYPE is the type of
+ all font entities created. DRIVER is the font driver that is saved
+ in font objects. PUT_GLYPHS is a function that is called with 8
+ arguments, S, FROM, TO, X, Y, WITH_BACKGROUND, RASTERS, and
+ X_COORDS, and should draw all the rasters in RASTERS to S->f,
+ originating at X_COORDS[i], Y, along with filling the background if
+ WITH_BACKGROUND is specified. */
+
+void
+init_sfntfont_vendor (Lisp_Object vendor_name,
+ const struct font_driver *driver,
+ sfntfont_put_glyph_proc put_glyphs)
+{
+ sfnt_vendor_name = vendor_name;
+ sfnt_font_driver = driver;
+ sfnt_put_glyphs = put_glyphs;
+}
diff --git a/src/sfntfont.h b/src/sfntfont.h
new file mode 100644
index 00000000000..df387512d0d
--- /dev/null
+++ b/src/sfntfont.h
@@ -0,0 +1,79 @@
+/* sfnt format font driver for GNU Emacs.
+
+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, write to the Free Software Foundation,
+Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+
+#ifndef _SFNTFONT_H_
+#define _SFNTFONT_H_
+
+#include "lisp.h"
+#include "frame.h"
+#include "font.h"
+#include "sfnt.h"
+
+extern int sfnt_enum_font (const char *);
+
+
+/* Font driver callbacks. */
+
+extern Lisp_Object sfntfont_list (struct frame *, Lisp_Object);
+extern Lisp_Object sfntfont_match (struct frame *, Lisp_Object);
+extern Lisp_Object sfntfont_open (struct frame *, Lisp_Object, int);
+
+extern unsigned int sfntfont_encode_char (struct font *, int);
+extern void sfntfont_text_extents (struct font *, const unsigned int *,
+ int, struct font_metrics *);
+extern void sfntfont_close (struct font *);
+extern int sfntfont_draw (struct glyph_string *, int, int,
+ int, int, bool);
+extern Lisp_Object sfntfont_list_family (struct frame *);
+extern int sfntfont_get_variation_glyphs (struct font *, int, unsigned[256]);
+
+
+/* Initialization functions. */
+
+typedef void (*sfntfont_put_glyph_proc) (struct glyph_string *, int, int,
+ int, int, bool, struct sfnt_raster **,
+ int *);
+
+extern void syms_of_sfntfont (void);
+extern void init_sfntfont (void);
+extern void mark_sfntfont (void);
+extern void init_sfntfont_vendor (Lisp_Object, const struct font_driver *,
+ sfntfont_put_glyph_proc);
+
+
+/* mmap specific functions. */
+
+#ifdef HAVE_MMAP
+
+extern bool sfntfont_detect_sigbus (void *);
+
+#endif /* HAVE_MMAP */
+
+
+
+/* HarfBuzz specific functions. */
+
+#ifdef HAVE_HARFBUZZ
+
+extern hb_font_t *sfntfont_begin_hb_font (struct font *, double *);
+
+#endif /* HAVE_HARFBUZZ */
+
+#endif /* _SFNTFONT_H_ */
diff --git a/src/sound.c b/src/sound.c
index 145100cd433..a51cdb6d97a 100644
--- a/src/sound.c
+++ b/src/sound.c
@@ -1384,7 +1384,7 @@ Internal use only, use `play-sound' instead. */)
/* Open the sound file. */
current_sound->fd =
openp (list1 (Vdata_directory), attrs[SOUND_FILE], Qnil, &file, Qnil,
- false, false);
+ false, false, NULL);
if (current_sound->fd < 0)
sound_perror ("Could not open sound file");
diff --git a/src/sysdep.c b/src/sysdep.c
index 443602a2d6d..bec2c00d3e5 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -134,6 +134,14 @@ int _cdecl _spawnlp (int, const char *, const char *, ...);
# include <sys/socket.h>
#endif
+#ifdef HAVE_ANDROID
+#include "android.h"
+#endif
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+#include "sfntfont.h"
+#endif
+
/* Declare here, including term.h is problematic on some systems. */
extern void tputs (const char *, int, int (*)(int));
@@ -790,6 +798,7 @@ init_sigio (int fd)
#endif
}
+#ifndef HAVE_ANDROID
#ifndef DOS_NT
#ifdef F_SETOWN
static void
@@ -801,6 +810,7 @@ reset_sigio (int fd)
}
#endif /* F_SETOWN */
#endif
+#endif
void
request_sigio (void)
@@ -972,6 +982,8 @@ narrow_foreground_group (int fd)
tcsetpgrp_without_stopping (fd, getpid ());
}
+#ifndef HAVE_ANDROID
+
/* Set the tty to our original foreground group. */
static void
widen_foreground_group (int fd)
@@ -979,6 +991,9 @@ widen_foreground_group (int fd)
if (inherited_pgroup && setpgid (0, inherited_pgroup) == 0)
tcsetpgrp_without_stopping (fd, inherited_pgroup);
}
+
+#endif
+
/* Getting and setting emacs_tty structures. */
@@ -1496,6 +1511,8 @@ reset_sys_modes (struct tty_display_info *tty_out)
fflush (stdout);
return;
}
+
+#ifndef HAVE_ANDROID
if (!tty_out->term_initted)
return;
@@ -1552,6 +1569,7 @@ reset_sys_modes (struct tty_display_info *tty_out)
#endif
widen_foreground_group (fileno (tty_out->input));
+#endif
}
#ifdef HAVE_PTYS
@@ -1802,7 +1820,45 @@ handle_arith_signal (int sig)
xsignal0 (Qarith_error);
}
-#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && defined HAVE_MMAP
+
+static void
+handle_sigbus (int sig, siginfo_t *siginfo, void *arg)
+{
+ /* If this arrives during sfntfont_open, then Emacs may be
+ screwed. */
+
+ if (sfntfont_detect_sigbus (siginfo->si_addr))
+ return;
+
+ handle_fatal_signal (sig);
+}
+
+/* Try to set up SIGBUS handling for the sfnt font driver.
+ Value is 1 upon failure, 0 otherwise. */
+
+static int
+init_sigbus (void)
+{
+ struct sigaction sa;
+
+ sigfillset (&sa.sa_mask);
+ sa.sa_sigaction = handle_sigbus;
+ sa.sa_flags = SA_SIGINFO;
+
+ if (sigaction (SIGBUS, &sa, NULL))
+ return 1;
+
+ return 0;
+}
+
+#endif
+
+/* This does not work on Android and interferes with the system
+ tombstone generation. */
+
+#if defined HAVE_STACK_OVERFLOW_HANDLING && !defined WINDOWSNT \
+ && (!defined HAVE_ANDROID || defined ANDROID_STUBIFY)
/* Alternate stack used by SIGSEGV handler below. */
@@ -1914,12 +1970,16 @@ init_sigsegv (void)
#else /* not HAVE_STACK_OVERFLOW_HANDLING or WINDOWSNT */
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
+
static bool
init_sigsegv (void)
{
return 0;
}
+#endif
+
#endif /* HAVE_STACK_OVERFLOW_HANDLING && !WINDOWSNT */
static void
@@ -2027,12 +2087,17 @@ init_signals (void)
#endif /* __vax__ */
}
+ /* SIGUSR1 and SIGUSR2 are used internally by the android_select
+ function. */
+#if !defined HAVE_ANDROID
#ifdef SIGUSR1
add_user_signal (SIGUSR1, "sigusr1");
#endif
#ifdef SIGUSR2
add_user_signal (SIGUSR2, "sigusr2");
#endif
+#endif
+
sigaction (SIGABRT, &thread_fatal_action, 0);
#ifdef SIGPRE
sigaction (SIGPRE, &thread_fatal_action, 0);
@@ -2056,10 +2121,15 @@ init_signals (void)
sigaction (SIGEMT, &thread_fatal_action, 0);
#endif
#ifdef SIGBUS
- sigaction (SIGBUS, &thread_fatal_action, 0);
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY && defined HAVE_MMAP
+ if (init_sigbus ())
#endif
+ sigaction (SIGBUS, &thread_fatal_action, 0);
+#endif
+#if !defined HAVE_ANDROID || defined ANDROID_STUBIFY
if (!init_sigsegv ())
sigaction (SIGSEGV, &thread_fatal_action, 0);
+#endif
#ifdef SIGSYS
sigaction (SIGSYS, &thread_fatal_action, 0);
#endif
@@ -2313,7 +2383,8 @@ emacs_backtrace (int backtrace_limit)
}
}
-#ifndef HAVE_NTGUI
+#if !defined HAVE_NTGUI && !(defined HAVE_ANDROID \
+ && !defined ANDROID_STUBIFY)
void
emacs_abort (void)
{
@@ -2335,11 +2406,20 @@ int
emacs_fstatat (int dirfd, char const *filename, void *st, int flags)
{
int r;
- while ((r = fstatat (dirfd, filename, st, flags)) != 0 && errno == EINTR)
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ while ((r = fstatat (dirfd, filename, st, flags)) != 0
+ && errno == EINTR)
maybe_quit ();
+#else
+ while ((r = android_fstatat (dirfd, filename, st, flags)) != 0
+ && errno == EINTR)
+ maybe_quit ();
+#endif
return r;
}
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+
static int
sys_openat (int dirfd, char const *file, int oflags, int mode)
{
@@ -2354,6 +2434,28 @@ sys_openat (int dirfd, char const *file, int oflags, int mode)
#endif
}
+#endif
+
+int
+sys_fstat (int fd, struct stat *statb)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ return fstat (fd, statb);
+#else
+ return android_fstat (fd, statb);
+#endif
+}
+
+int
+sys_faccessat (int fd, const char *pathname, int mode, int flags)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ return faccessat (fd, pathname, mode, flags);
+#else
+ return android_faccessat (fd, pathname, mode, flags);
+#endif
+}
+
/* Assuming the directory DIRFD, open FILE for Emacs use,
using open flags OFLAGS and mode MODE.
Use binary I/O on systems that care about text vs binary I/O.
@@ -2362,6 +2464,8 @@ sys_openat (int dirfd, char const *file, int oflags, int mode)
Do not fail merely because the open was interrupted by a signal.
Allow the user to quit. */
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+
int
emacs_openat (int dirfd, char const *file, int oflags, int mode)
{
@@ -2374,10 +2478,23 @@ emacs_openat (int dirfd, char const *file, int oflags, int mode)
return fd;
}
+#endif
+
int
emacs_open (char const *file, int oflags, int mode)
{
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ int fd;
+#endif
+
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
return emacs_openat (AT_FDCWD, file, oflags, mode);
+#else
+ while ((fd = android_open (file, oflags, mode)) < 0 && errno == EINTR)
+ maybe_quit ();
+
+ return fd;
+#endif
}
/* Same as above, but doesn't allow the user to quit. */
@@ -2389,9 +2506,15 @@ emacs_open_noquit (char const *file, int oflags, int mode)
if (! (oflags & O_TEXT))
oflags |= O_BINARY;
oflags |= O_CLOEXEC;
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
do
fd = open (file, oflags, mode);
while (fd < 0 && errno == EINTR);
+#else
+ do
+ fd = android_open (file, oflags, mode);
+ while (fd < 0 && errno == EINTR);
+#endif
return fd;
}
@@ -2441,6 +2564,8 @@ emacs_pipe (int fd[2])
For the background behind this mess, please see Austin Group defect 529
<https://austingroupbugs.net/view.php?id=529>. */
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+
#ifndef POSIX_CLOSE_RESTART
# define POSIX_CLOSE_RESTART 1
static int
@@ -2467,6 +2592,8 @@ posix_close (int fd, int flag)
}
#endif
+#endif
+
/* Close FD, retrying if interrupted. If successful, return 0;
otherwise, return -1 and set errno to a non-EINTR value. Consider
an EINPROGRESS error to be successful, as that's merely a signal
@@ -2479,9 +2606,17 @@ posix_close (int fd, int flag)
int
emacs_close (int fd)
{
+ int r;
+
while (1)
{
- int r = posix_close (fd, POSIX_CLOSE_RESTART);
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ r = posix_close (fd, POSIX_CLOSE_RESTART);
+#else
+ r = android_close (fd) == 0 || errno == EINTR ? 0 : -1;
+#define POSIX_CLOSE_RESTART 1
+#endif
+
if (r == 0)
return r;
if (!POSIX_CLOSE_RESTART || errno != EINTR)
@@ -2492,6 +2627,20 @@ emacs_close (int fd)
}
}
+/* Wrapper around fclose. On Android, this calls `android_fclose' to
+ clear information associated with the FILE's file descriptor if
+ necessary. */
+
+int
+emacs_fclose (FILE *stream)
+{
+#if !(defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
+ return fclose (stream);
+#else
+ return android_fclose (stream);
+#endif
+}
+
/* Maximum number of bytes to read or write in a single system call.
This works around a serious bug in Linux kernels before 2.6.16; see
<https://bugzilla.redhat.com/show_bug.cgi?format=multiple&id=612839>.
@@ -2736,6 +2885,15 @@ errwrite (void const *buf, ptrdiff_t nbuf)
void
close_output_streams (void)
{
+ /* Android comes with some kind of ``file descriptor sanitizer''
+ that aborts when stdout or stderr is closed. */
+
+#if defined HAVE_ANDROID && !defined ANDROID_STUBIFY
+ fflush (stderr);
+ fflush (stdout);
+ return;
+#endif
+
if (close_stream (stdout) != 0)
{
emacs_perror ("Write error to standard output");
diff --git a/src/term.c b/src/term.c
index 2b28d3303e8..44f6c5918b9 100644
--- a/src/term.c
+++ b/src/term.c
@@ -62,6 +62,8 @@ static int been_here = -1;
#include "w32term.h"
#endif
+#ifndef HAVE_ANDROID
+
static void tty_set_scroll_region (struct frame *f, int start, int stop);
static void turn_on_face (struct frame *, int face_id);
static void turn_off_face (struct frame *, int face_id);
@@ -73,11 +75,15 @@ static void clear_tty_hooks (struct terminal *terminal);
static void set_tty_hooks (struct terminal *terminal);
static void dissociate_if_controlling_tty (int fd);
static void delete_tty (struct terminal *);
+
+#endif
+
static AVOID maybe_fatal (bool, struct terminal *, const char *, const char *,
...)
ATTRIBUTE_FORMAT_PRINTF (3, 5) ATTRIBUTE_FORMAT_PRINTF (4, 5);
static AVOID vfatal (const char *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0);
+#ifndef HAVE_ANDROID
#define OUTPUT(tty, a) \
emacs_tputs ((tty), a, \
@@ -95,6 +101,8 @@ static AVOID vfatal (const char *, va_list) ATTRIBUTE_FORMAT_PRINTF (1, 0);
#define OUTPUT1_IF(tty, a) do { if (a) emacs_tputs ((tty), a, 1, cmputc); } while (0)
+#endif
+
/* Display space properties. */
/* Chain of all tty device parameters. */
@@ -117,10 +125,14 @@ enum no_color_bit
/* internal state */
+#ifndef HAVE_ANDROID
+
/* The largest frame width in any call to calculate_costs. */
static int max_frame_cols;
+#endif
+
#ifdef HAVE_GPM
@@ -133,6 +145,8 @@ struct tty_display_info *gpm_tty = NULL;
static int last_mouse_x, last_mouse_y;
#endif /* HAVE_GPM */
+#ifndef HAVE_ANDROID
+
/* Ring the bell on a tty. */
static void
@@ -718,7 +732,20 @@ encode_terminal_code (struct glyph *src, int src_len,
return (encode_terminal_dst);
}
+#else /* !HAVE_ANDROID */
+unsigned char *
+encode_terminal_code (struct glyph *src, int src_len,
+ struct coding_system *coding)
+{
+ /* Text terminals are simply not supported on Android. */
+ coding->produced = 0;
+ return NULL;
+}
+
+#endif /* HAVE_ANDROID */
+
+#ifndef HAVE_ANDROID
/* An implementation of write_glyphs for termcap frames. */
@@ -1046,8 +1073,10 @@ int
string_cost (const char *str)
{
cost = 0;
+#ifndef HAVE_ANDROID
if (str)
tputs (str, 0, evalcost);
+#endif
return cost;
}
@@ -1058,8 +1087,10 @@ static int
string_cost_one_line (const char *str)
{
cost = 0;
+#ifndef HAVE_ANDROID
if (str)
tputs (str, 1, evalcost);
+#endif
return cost;
}
@@ -1070,11 +1101,13 @@ int
per_line_cost (const char *str)
{
cost = 0;
+#ifndef HAVE_ANDROID
if (str)
tputs (str, 0, evalcost);
cost = - cost;
if (str)
tputs (str, 10, evalcost);
+#endif
return cost;
}
@@ -1147,11 +1180,14 @@ calculate_ins_del_char_costs (struct frame *f)
*p++ = (ins_startup_cost += ins_cost_per_char);
}
+#endif
+
void
calculate_costs (struct frame *frame)
{
FRAME_COST_BAUD_RATE (frame) = baud_rate;
+#ifndef HAVE_ANDROID
if (FRAME_TERMCAP_P (frame))
{
struct tty_display_info *tty = FRAME_TTY (frame);
@@ -1206,13 +1242,15 @@ calculate_costs (struct frame *frame)
cmcostinit (FRAME_TTY (frame)); /* set up cursor motion costs */
}
+#endif
}
-struct fkey_table {
+struct fkey_table
+{
const char *cap, *name;
};
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
/* Termcap capability names that correspond directly to X keysyms.
Some of these (marked "terminfo") aren't supplied by old-style
(Berkeley) termcap entries. They're listed in X keysym order;
@@ -1443,6 +1481,9 @@ term_get_fkeys_1 (void)
#endif /* not DOS_NT */
+
+#ifndef HAVE_ANDROID
+
/***********************************************************************
Character Display Information
***********************************************************************/
@@ -1519,14 +1560,17 @@ append_glyph (struct it *it)
}
}
+#endif
+
/* For external use. */
void
tty_append_glyph (struct it *it)
{
+#ifndef HAVE_ANDROID
append_glyph (it);
+#endif
}
-
/* Produce glyphs for the display element described by IT. *IT
specifies what we want to produce a glyph for (character, image, ...),
and where in the glyph matrix we currently are (glyph row and hpos).
@@ -1549,6 +1593,7 @@ tty_append_glyph (struct it *it)
void
produce_glyphs (struct it *it)
{
+#ifndef HAVE_ANDROID
/* If a hook is installed, let it do the work. */
/* Nothing but characters are supported on terminal frames. */
@@ -1661,8 +1706,11 @@ produce_glyphs (struct it *it)
it->current_x += it->pixel_width;
it->ascent = it->max_ascent = it->phys_ascent = it->max_phys_ascent = 0;
it->descent = it->max_descent = it->phys_descent = it->max_phys_descent = 1;
+#endif
}
+#ifndef HAVE_ANDROID
+
/* Append glyphs to IT's glyph_row for the composition IT->cmp_id.
Called from produce_composite_glyph for terminal frames if
IT->glyph_row != NULL. IT->face_id contains the character's
@@ -2020,6 +2068,7 @@ turn_off_face (struct frame *f, int face_id)
OUTPUT1_IF (tty, tty->TS_orig_pair);
}
+#endif /* !HAVE_ANDROID */
/* Return true if the terminal on frame F supports all of the
capabilities in CAPS simultaneously. */
@@ -2027,8 +2076,9 @@ turn_off_face (struct frame *f, int face_id)
bool
tty_capable_p (struct tty_display_info *tty, unsigned int caps)
{
+#ifndef HAVE_ANDROID
#define TTY_CAPABLE_P_TRY(tty, cap, TS, NC_bit) \
- if ((caps & (cap)) && (!(TS) || !MAY_USE_WITH_COLORS_P(tty, NC_bit))) \
+ if ((caps & (cap)) && (!(TS) || !MAY_USE_WITH_COLORS_P (tty, NC_bit))) \
return 0;
TTY_CAPABLE_P_TRY (tty,
@@ -2048,6 +2098,9 @@ tty_capable_p (struct tty_display_info *tty, unsigned int caps)
/* We can do it! */
return 1;
+#else
+ return false;
+#endif
}
/* Return non-zero if the terminal is capable to display colors. */
@@ -2081,7 +2134,7 @@ TERMINAL does not refer to a text terminal. */)
return make_fixnum (t ? t->display_info.tty->TN_max_colors : 0);
}
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
/* Declare here rather than in the function, as in the rest of Emacs,
to work around an HPUX compiler bug (?). See
@@ -2186,7 +2239,7 @@ set_tty_color_mode (struct tty_display_info *tty, struct frame *f)
}
}
-#endif /* !DOS_NT */
+#endif /* !DOS_NT && !HAVE_ANDROID */
char *
tty_type_name (Lisp_Object terminal)
@@ -2278,6 +2331,7 @@ suspended.
A suspended tty may be resumed by calling `resume-tty' on it. */)
(Lisp_Object tty)
{
+#ifndef HAVE_ANDROID
struct terminal *t = decode_tty_terminal (tty);
FILE *f;
@@ -2300,8 +2354,8 @@ A suspended tty may be resumed by calling `resume-tty' on it. */)
#ifndef MSDOS
if (f != t->display_info.tty->output)
- fclose (t->display_info.tty->output);
- fclose (f);
+ emacs_fclose (t->display_info.tty->output);
+ emacs_fclose (f);
#endif
t->display_info.tty->input = 0;
@@ -2314,6 +2368,10 @@ A suspended tty may be resumed by calling `resume-tty' on it. */)
/* Clear display hooks to prevent further output. */
clear_tty_hooks (t);
+#else
+ /* This will always signal on Android. */
+ decode_tty_terminal (tty);
+#endif
return Qnil;
}
@@ -2337,9 +2395,12 @@ TTY may be a terminal object, a frame, or nil (meaning the selected
frame's terminal). */)
(Lisp_Object tty)
{
- struct terminal *t = decode_tty_terminal (tty);
+#ifndef HAVE_ANDROID
+ struct terminal *t;
int fd;
+ t = decode_tty_terminal (tty);
+
if (!t)
error ("Attempt to resume a non-text terminal device");
@@ -2396,10 +2457,15 @@ frame's terminal). */)
}
set_tty_hooks (t);
+#else
+ decode_tty_terminal (tty);
+#endif
return Qnil;
}
+#ifndef HAVE_ANDROID
+
DEFUN ("tty--set-output-buffer-size", Ftty__set_output_buffer_size,
Stty__set_output_buffer_size, 1, 2, 0, doc:
/* Set the output buffer size for a TTY.
@@ -2438,12 +2504,14 @@ A value of zero means TTY uses the system's default value. */)
error ("Not a tty terminal");
}
+#endif
+
/***********************************************************************
Mouse
***********************************************************************/
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
/* Implementation of draw_row_with_mouse_face for TTY/GPM and macOS. */
void
@@ -2713,7 +2781,7 @@ DEFUN ("gpm-mouse-stop", Fgpm_mouse_stop, Sgpm_mouse_stop,
Menus
***********************************************************************/
-#if !defined (MSDOS)
+#if !defined (MSDOS) && !defined HAVE_ANDROID
/* TTY menu implementation and main ideas are borrowed from msdos.c.
@@ -3813,10 +3881,12 @@ tty_menu_show (struct frame *f, int x, int y, int menuflags,
return SAFE_FREE_UNBIND_TO (specpdl_count, entry);
}
-#endif /* !MSDOS */
+#endif /* !MSDOS && !defined HAVE_ANDROID */
-#ifndef MSDOS
+
+#if !defined MSDOS && !defined HAVE_ANDROID
+
/***********************************************************************
Initialization
***********************************************************************/
@@ -3846,7 +3916,7 @@ tty_free_frame_resources (struct frame *f)
xfree (f->output_data.tty);
}
-#else /* MSDOS */
+#elif defined MSDOS
/* Delete frame F's face cache. */
@@ -3856,8 +3926,13 @@ tty_free_frame_resources (struct frame *f)
eassert (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f));
free_frame_faces (f);
}
-#endif /* MSDOS */
+
+#endif
+
+
+#ifndef HAVE_ANDROID
+
/* Reset the hooks in TERMINAL. */
static void
@@ -3952,6 +4027,8 @@ dissociate_if_controlling_tty (int fd)
}
}
+#endif /* !HAVE_ANDROID */
+
/* Create a termcap display on the tty device with the given name and
type.
@@ -3961,11 +4038,23 @@ dissociate_if_controlling_tty (int fd)
TERMINAL_TYPE is the termcap type of the device, e.g. "vt100".
- If MUST_SUCCEED is true, then all errors are fatal. */
+ If MUST_SUCCEED is true, then all errors are fatal. This function
+ always signals on Android, where text terminals are prohibited by
+ system policy (and the required libraries are usually not
+ available.) */
+
+#ifdef HAVE_ANDROID
+_Noreturn
+#endif
struct terminal *
init_tty (const char *name, const char *terminal_type, bool must_succeed)
{
+#ifdef HAVE_ANDROID
+ maybe_fatal (must_succeed, 0, "Text terminals are not supported"
+ " under Android", "Text terminals are not supported"
+ " under Android");
+#else
struct tty_display_info *tty = NULL;
struct terminal *terminal = NULL;
#ifndef DOS_NT
@@ -4455,6 +4544,7 @@ use the Bourne shell command 'TERM=...; export TERM' (C-shell:\n\
init_sys_modes (tty);
return terminal;
+#endif /* !HAVE_ANDROID */
}
@@ -4479,8 +4569,13 @@ maybe_fatal (bool must_succeed, struct terminal *terminal,
{
va_list ap;
va_start (ap, str2);
+
+#ifndef HAVE_ANDROID
if (terminal)
delete_tty (terminal);
+#else
+ eassert (terminal == NULL);
+#endif
if (must_succeed)
vfatal (str2, ap);
@@ -4498,6 +4593,8 @@ fatal (const char *str, ...)
+#ifndef HAVE_ANDROID
+
/* Delete the given tty terminal, closing all frames on it. */
static void
@@ -4543,25 +4640,27 @@ delete_tty (struct terminal *terminal)
{
delete_keyboard_wait_descriptor (fileno (tty->input));
if (tty->input != stdin)
- fclose (tty->input);
+ emacs_fclose (tty->input);
}
if (tty->output && tty->output != stdout && tty->output != tty->input)
- fclose (tty->output);
+ emacs_fclose (tty->output);
if (tty->termscript)
- fclose (tty->termscript);
+ emacs_fclose (tty->termscript);
xfree (tty->old_tty);
xfree (tty->Wcm);
xfree (tty);
}
+#endif
+
void
syms_of_term (void)
{
DEFVAR_BOOL ("system-uses-terminfo", system_uses_terminfo,
doc: /* Non-nil means the system uses terminfo rather than termcap.
This variable can be used by terminal emulator packages. */);
-#ifdef TERMINFO
+#if defined TERMINFO || (defined HAVE_ANDROID && !defined ANDROID_STUBIFY)
system_uses_terminfo = 1;
#else
system_uses_terminfo = 0;
@@ -4602,21 +4701,25 @@ trigger redisplay. */);
defsubr (&Stty_top_frame);
defsubr (&Ssuspend_tty);
defsubr (&Sresume_tty);
+#ifndef HAVE_ANDROID
defsubr (&Stty__set_output_buffer_size);
defsubr (&Stty__output_buffer_size);
+#endif /* !HAVE_ANDROID */
#ifdef HAVE_GPM
defsubr (&Sgpm_mouse_start);
defsubr (&Sgpm_mouse_stop);
#endif /* HAVE_GPM */
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
default_orig_pair = NULL;
default_set_foreground = NULL;
default_set_background = NULL;
-#endif /* !DOS_NT */
+#endif /* !DOS_NT && !HAVE_ANDROID */
+#ifndef HAVE_ANDROID
encode_terminal_src = NULL;
encode_terminal_dst = NULL;
+#endif
DEFSYM (Qtty_mode_set_strings, "tty-mode-set-strings");
DEFSYM (Qtty_mode_reset_strings, "tty-mode-reset-strings");
diff --git a/src/termhooks.h b/src/termhooks.h
index ba04a6b7759..99f27cd668e 100644
--- a/src/termhooks.h
+++ b/src/termhooks.h
@@ -63,7 +63,8 @@ enum output_method
output_w32,
output_ns,
output_pgtk,
- output_haiku
+ output_haiku,
+ output_android,
};
/* Input queue declarations and hooks. */
@@ -516,12 +517,13 @@ struct terminal
/* Device-type dependent data shared amongst all frames on this terminal. */
union display_info
{
- struct tty_display_info *tty; /* termchar.h */
- struct x_display_info *x; /* xterm.h */
- struct w32_display_info *w32; /* w32term.h */
- struct ns_display_info *ns; /* nsterm.h */
- struct pgtk_display_info *pgtk; /* pgtkterm.h */
- struct haiku_display_info *haiku; /* haikuterm.h */
+ struct tty_display_info *tty; /* termchar.h */
+ struct x_display_info *x; /* xterm.h */
+ struct w32_display_info *w32; /* w32term.h */
+ struct ns_display_info *ns; /* nsterm.h */
+ struct pgtk_display_info *pgtk; /* pgtkterm.h */
+ struct haiku_display_info *haiku; /* haikuterm.h */
+ struct android_display_info *android; /* androidterm.h */
} display_info;
@@ -595,7 +597,8 @@ struct terminal
BGCOLOR. */
void (*query_frame_background_color) (struct frame *f, Emacs_Color *bgcolor);
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) || defined (HAVE_PGTK)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NTGUI) || defined (HAVE_PGTK) \
+ || defined (HAVE_ANDROID)
/* On frame F, translate pixel colors to RGB values for the NCOLORS
colors in COLORS. Use cached information, if available. */
@@ -930,6 +933,9 @@ extern struct terminal *terminal_list;
#elif defined (HAVE_HAIKU)
#define TERMINAL_FONT_CACHE(t) \
(t->type == output_haiku ? t->display_info.haiku->name_list_element : Qnil)
+#elif defined (HAVE_ANDROID)
+#define TERMINAL_FONT_CACHE(t) \
+ (t->type == output_android ? t->display_info.android->name_list_element : Qnil)
#endif
extern struct terminal *decode_live_terminal (Lisp_Object);
diff --git a/src/terminal.c b/src/terminal.c
index d13e3466512..07c37883f0e 100644
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -451,6 +451,8 @@ return values. */)
return Qpgtk;
case output_haiku:
return Qhaiku;
+ case output_android:
+ return Qandroid;
default:
emacs_abort ();
}
diff --git a/src/textconv.c b/src/textconv.c
index d5db6d11717..e1a73e91397 100644
--- a/src/textconv.c
+++ b/src/textconv.c
@@ -25,25 +25,38 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
ability to ``undo'' or ``edit'' previously composed text. This is
most commonly seen in input methods for CJK laguages for X Windows,
and is extensively used throughout Android by input methods for all
- kinds of scripts. */
+ kinds of scripts.
+
+ In addition, these input methods may also need to make detailed
+ edits to the content of a buffer. That is also handled here. */
#include <config.h>
#include "textconv.h"
#include "buffer.h"
#include "syntax.h"
+#include "blockinput.h"
-/* The window system's text conversion interface.
- NULL when the window system has not set up text conversion.
-
- This interface will later be heavily extended on the
- feature/android branch to deal with Android's much less
- straightforward text conversion protocols. */
+/* The window system's text conversion interface. NULL when the
+ window system has not set up text conversion. */
static struct textconv_interface *text_interface;
+/* How many times text conversion has been disabled. */
+
+static int suppress_conversion_count;
+
+/* Flags used to determine what must be sent after a batch edit
+ ends. */
+
+enum textconv_batch_edit_flags
+ {
+ PENDING_POINT_CHANGE = 1,
+ PENDING_COMPOSE_CHANGE = 2,
+ };
+
/* Copy the portion of the current buffer described by BEG, BEG_BYTE,
@@ -77,13 +90,46 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
size = end0 - beg0;
memcpy (buffer, BYTE_POS_ADDR (beg0), size);
if (beg1 != -1)
- memcpy (buffer, BEG_ADDR + beg1, end1 - beg1);
+ memcpy (buffer + size, BEG_ADDR + beg1, end1 - beg1);
}
/* Conversion query. */
+/* Return the position of the active mark, or -1 if there is no mark
+ or it is not active. */
+
+static ptrdiff_t
+get_mark (void)
+{
+ if (!NILP (BVAR (current_buffer, mark_active))
+ && XMARKER (BVAR (current_buffer, mark))->buffer)
+ return marker_position (BVAR (current_buffer,
+ mark));
+
+ return -1;
+}
+
+/* Like Fselect_window. However, if WINDOW is a mini buffer window
+ but not the active minibuffer window, select its frame's selected
+ window instead. */
+
+static void
+select_window (Lisp_Object window, Lisp_Object norecord)
+{
+ struct window *w;
+
+ w = XWINDOW (window);
+
+ if (MINI_WINDOW_P (w)
+ && WINDOW_LIVE_P (window)
+ && !EQ (window, Factive_minibuffer_window ()))
+ window = WINDOW_XFRAME (w)->selected_window;
+
+ Fselect_window (window, norecord);
+}
+
/* Perform the text conversion operation specified in QUERY and return
the results.
@@ -91,19 +137,28 @@ copy_buffer (ptrdiff_t beg, ptrdiff_t beg_byte,
window and QUERY->factor times QUERY->direction from that
position. Return it in QUERY->text.
+ If QUERY->position is TYPE_MINIMUM (EMACS_INT) or EMACS_INT_MAX,
+ start at the window's last point or mark, whichever is greater or
+ smaller.
+
Then, either delete that text from the buffer if QUERY->operation
is TEXTCONV_SUBSTITUTION, or return 0.
+ If FLAGS & TEXTCONV_SKIP_CONVERSION_REGION, then first move PT past
+ the conversion region in the specified direction if it is inside.
+
Value is 0 if QUERY->operation was not TEXTCONV_SUBSTITUTION
or if deleting the text was successful, and 1 otherwise. */
int
-textconv_query (struct frame *f, struct textconv_callback_struct *query)
+textconv_query (struct frame *f, struct textconv_callback_struct *query,
+ int flags)
{
specpdl_ref count;
- ptrdiff_t pos, pos_byte, end, end_byte;
- ptrdiff_t temp, temp1;
+ ptrdiff_t pos, pos_byte, end, end_byte, start;
+ ptrdiff_t temp, temp1, mark;
char *buffer;
+ struct window *w;
/* Save the excursion, as there will be extensive changes to the
selected window. */
@@ -113,14 +168,69 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query)
/* Inhibit quitting. */
specbind (Qinhibit_quit, Qt);
- /* Temporarily switch to F's selected window. */
- Fselect_window (f->selected_window, Qt);
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window ((WINDOW_LIVE_P (f->old_selected_window)
+ ? f->old_selected_window
+ : f->selected_window), Qt);
+ w = XWINDOW (selected_window);
/* Now find the appropriate text bounds for QUERY. First, move
point QUERY->position steps forward or backwards. */
pos = PT;
+ /* If QUERY->position is EMACS_INT_MAX, use the last mark or the
+ ephemeral last point, whichever is greater.
+
+ The opposite applies for EMACS_INT_MIN. */
+
+ mark = get_mark ();
+
+ if (query->position == EMACS_INT_MAX)
+ {
+ pos = (mark == -1
+ ? w->ephemeral_last_point
+ : max (w->ephemeral_last_point, mark));
+ goto escape1;
+ }
+ else if (query->position == TYPE_MINIMUM (EMACS_INT))
+ {
+ pos = (mark == -1
+ ? w->ephemeral_last_point
+ : min (w->ephemeral_last_point, mark));
+ goto escape1;
+ }
+
+ /* Next, if POS lies within the conversion region and the caller
+ asked for it to be moved away, move it away from the conversion
+ region. */
+
+ if (flags & TEXTCONV_SKIP_CONVERSION_REGION
+ && MARKERP (f->conversion.compose_region_start))
+ {
+ start = marker_position (f->conversion.compose_region_start);
+ end = marker_position (f->conversion.compose_region_end);
+
+ if (pos >= start && pos < end)
+ {
+ switch (query->direction)
+ {
+ case TEXTCONV_FORWARD_CHAR:
+ case TEXTCONV_FORWARD_WORD:
+ case TEXTCONV_CARET_DOWN:
+ case TEXTCONV_NEXT_LINE:
+ case TEXTCONV_LINE_START:
+ pos = end;
+ break;
+
+ default:
+ pos = max (BEGV, start - 1);
+ break;
+ }
+ }
+ }
+
/* If pos is outside the accessible part of the buffer or if it
overflows, move back to point or to the extremes of the
accessible region. */
@@ -128,6 +238,8 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query)
if (INT_ADD_WRAPV (pos, query->position, &pos))
pos = PT;
+ escape1:
+
if (pos < BEGV)
pos = BEGV;
@@ -287,6 +399,1216 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query)
return 0;
}
+/* Update the overlay displaying the conversion area on F after a
+ change to the conversion region. */
+
+static void
+sync_overlay (struct frame *f)
+{
+ if (MARKERP (f->conversion.compose_region_start)
+ && !NILP (Vtext_conversion_face))
+ {
+ if (NILP (f->conversion.compose_region_overlay))
+ {
+ f->conversion.compose_region_overlay
+ = Fmake_overlay (f->conversion.compose_region_start,
+ f->conversion.compose_region_end, Qnil,
+ Qt, Qnil);
+ Foverlay_put (f->conversion.compose_region_overlay,
+ Qface, Vtext_conversion_face);
+ }
+
+ Fmove_overlay (f->conversion.compose_region_overlay,
+ f->conversion.compose_region_start,
+ f->conversion.compose_region_end, Qnil);
+ }
+ else if (!NILP (f->conversion.compose_region_overlay))
+ {
+ Fdelete_overlay (f->conversion.compose_region_overlay);
+ f->conversion.compose_region_overlay = Qnil;
+ }
+}
+
+/* Record a change to the current buffer as a result of an
+ asynchronous text conversion operation on F.
+
+ Consult the doc string of `text-conversion-edits' for the meaning
+ of BEG, END, and EPHEMERAL. */
+
+static void
+record_buffer_change (ptrdiff_t beg, ptrdiff_t end,
+ Lisp_Object ephemeral)
+{
+ Lisp_Object buffer, beg_marker, end_marker;
+
+ XSETBUFFER (buffer, current_buffer);
+
+ /* Make markers for both BEG and END. */
+ beg_marker = build_marker (current_buffer, beg,
+ CHAR_TO_BYTE (beg));
+
+ /* If BEG and END are identical, make sure to keep the markers
+ eq. */
+
+ if (beg == end)
+ end_marker = beg_marker;
+ else
+ {
+ end_marker = build_marker (current_buffer, end,
+ CHAR_TO_BYTE (end));
+
+ /* Otherwise, make sure the marker extends past inserted
+ text. */
+ Fset_marker_insertion_type (end_marker, Qt);
+ }
+
+ Vtext_conversion_edits
+ = Fcons (list4 (buffer, beg_marker, end_marker,
+ ephemeral),
+ Vtext_conversion_edits);
+}
+
+/* Reset F's text conversion state. Delete any overlays or
+ markers inside. */
+
+void
+reset_frame_state (struct frame *f)
+{
+ struct text_conversion_action *last, *next;
+
+ /* Make the composition region markers point elsewhere. */
+
+ if (!NILP (f->conversion.compose_region_start))
+ {
+ Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
+ Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
+ f->conversion.compose_region_start = Qnil;
+ f->conversion.compose_region_end = Qnil;
+ }
+
+ /* Delete the composition region overlay. */
+
+ if (!NILP (f->conversion.compose_region_overlay))
+ Fdelete_overlay (f->conversion.compose_region_overlay);
+
+ /* Delete each text conversion action queued up. */
+
+ next = f->conversion.actions;
+ while (next)
+ {
+ last = next;
+ next = next->next;
+
+ /* Say that the conversion is finished. */
+ if (text_interface && text_interface->notify_conversion)
+ text_interface->notify_conversion (last->counter);
+
+ xfree (last);
+ }
+ f->conversion.actions = NULL;
+
+ /* Clear batch edit state. */
+ f->conversion.batch_edit_count = 0;
+ f->conversion.batch_edit_flags = 0;
+}
+
+/* Return whether or not there are pending edits from an input method
+ on any frame. */
+
+bool
+detect_conversion_events (void)
+{
+ Lisp_Object tail, frame;
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ if (XFRAME (frame)->conversion.actions)
+ return true;
+ }
+
+ return false;
+}
+
+/* Restore the selected window WINDOW. */
+
+static void
+restore_selected_window (Lisp_Object window)
+{
+ /* FIXME: not sure what to do if WINDOW has been deleted. */
+ select_window (window, Qt);
+}
+
+/* Commit the given text in the composing region. If there is no
+ composing region, then insert the text after F's selected window's
+ last point instead. Finally, remove the composing region.
+
+ Then, move point to POSITION relative to TEXT. If POSITION is
+ greater than zero, it is relative to the character at the end of
+ TEXT; otherwise, it is relative to the start of TEXT. */
+
+static void
+really_commit_text (struct frame *f, EMACS_INT position,
+ Lisp_Object text)
+{
+ specpdl_ref count;
+ ptrdiff_t wanted, start, end;
+ struct window *w;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+
+ /* Now detect whether or not there is a composing region.
+ If there is, then replace it with TEXT. Don't do that
+ otherwise. */
+
+ if (MARKERP (f->conversion.compose_region_start))
+ {
+ /* Replace its contents. */
+ start = marker_position (f->conversion.compose_region_start);
+ end = marker_position (f->conversion.compose_region_end);
+ del_range (start, end);
+ record_buffer_change (start, start, Qnil);
+ Finsert (1, &text);
+ record_buffer_change (start, PT, text);
+
+ /* Move to a the position specified in POSITION. If POSITION is
+ less than zero, it is relative to the start of the text that
+ was inserted. */
+
+ if (position <= 0)
+ {
+ wanted
+ = marker_position (f->conversion.compose_region_start);
+
+ if (INT_ADD_WRAPV (wanted, position, &wanted)
+ || wanted < BEGV)
+ wanted = BEGV;
+
+ if (wanted > ZV)
+ wanted = ZV;
+
+ set_point (wanted);
+ }
+ else
+ {
+ /* Otherwise, it is relative to the last character in
+ TEXT. */
+
+ wanted
+ = marker_position (f->conversion.compose_region_end);
+
+ if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
+ || wanted > ZV)
+ wanted = ZV;
+
+ if (wanted < BEGV)
+ wanted = BEGV;
+
+ set_point (wanted);
+ }
+
+ /* Make the composition region markers point elsewhere. */
+
+ if (!NILP (f->conversion.compose_region_start))
+ {
+ Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
+ Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
+ f->conversion.compose_region_start = Qnil;
+ f->conversion.compose_region_end = Qnil;
+ }
+
+ /* Delete the composition region overlay. */
+
+ if (!NILP (f->conversion.compose_region_overlay))
+ Fdelete_overlay (f->conversion.compose_region_overlay);
+ }
+ else
+ {
+ /* Otherwise, move the text and point to an appropriate
+ location. */
+ wanted = PT;
+ Finsert (1, &text);
+ record_buffer_change (wanted, PT, text);
+
+ if (position <= 0)
+ {
+ if (INT_ADD_WRAPV (wanted, position, &wanted)
+ || wanted < BEGV)
+ wanted = BEGV;
+
+ if (wanted > ZV)
+ wanted = ZV;
+
+ set_point (wanted);
+ }
+ else
+ {
+ wanted = PT;
+
+ if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
+ || wanted > ZV)
+ wanted = ZV;
+
+ if (wanted < BEGV)
+ wanted = BEGV;
+
+ set_point (wanted);
+ }
+ }
+
+ /* This should deactivate the mark. */
+ call0 (Qdeactivate_mark);
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+ unbind_to (count, Qnil);
+}
+
+/* Remove the composition region on the frame F, while leaving its
+ contents intact. */
+
+static void
+really_finish_composing_text (struct frame *f)
+{
+ if (!NILP (f->conversion.compose_region_start))
+ {
+ Fset_marker (f->conversion.compose_region_start, Qnil, Qnil);
+ Fset_marker (f->conversion.compose_region_end, Qnil, Qnil);
+ f->conversion.compose_region_start = Qnil;
+ f->conversion.compose_region_end = Qnil;
+ }
+
+ /* Delete the composition region overlay. */
+
+ if (!NILP (f->conversion.compose_region_overlay))
+ Fdelete_overlay (f->conversion.compose_region_overlay);
+}
+
+/* Set the composing text on F to TEXT. Then, move point to an
+ appropriate position relative to POSITION, and call
+ `compose_region_changed' in the text conversion interface should
+ point not have been changed relative to F's old selected window's
+ last point. */
+
+static void
+really_set_composing_text (struct frame *f, ptrdiff_t position,
+ Lisp_Object text)
+{
+ specpdl_ref count;
+ ptrdiff_t start, wanted, end;
+ struct window *w;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ w = XWINDOW (f->old_selected_window);
+ select_window (f->old_selected_window, Qt);
+
+ /* Now set up the composition region if necessary. */
+
+ if (!MARKERP (f->conversion.compose_region_start))
+ {
+ f->conversion.compose_region_start
+ = build_marker (current_buffer, PT, PT_BYTE);
+ f->conversion.compose_region_end
+ = build_marker (current_buffer, PT, PT_BYTE);
+
+ Fset_marker_insertion_type (f->conversion.compose_region_end,
+ Qt);
+
+ start = position;
+ }
+ else
+ {
+ /* Delete the text between the start of the composing region and
+ its end. */
+ start = marker_position (f->conversion.compose_region_start);
+ end = marker_position (f->conversion.compose_region_end);
+ del_range (start, end);
+ set_point (start);
+
+ if (start != end)
+ record_buffer_change (start, start, Qnil);
+ }
+
+ /* Insert the new text. */
+ Finsert (1, &text);
+
+ if (start != PT)
+ record_buffer_change (start, PT, Qnil);
+
+ /* Now move point to an appropriate location. */
+ if (position < 0)
+ {
+ wanted = start;
+
+ if (INT_SUBTRACT_WRAPV (wanted, position, &wanted)
+ || wanted < BEGV)
+ wanted = BEGV;
+
+ if (wanted > ZV)
+ wanted = ZV;
+ }
+ else
+ {
+ end = marker_position (f->conversion.compose_region_end);
+ wanted = end;
+
+ /* end should be PT after the edit. */
+ eassert (end == PT);
+
+ if (INT_ADD_WRAPV (wanted, position - 1, &wanted)
+ || wanted > ZV)
+ wanted = ZV;
+
+ if (wanted < BEGV)
+ wanted = BEGV;
+ }
+
+ set_point (wanted);
+
+ /* This should deactivate the mark. */
+ call0 (Qdeactivate_mark);
+
+ /* Move the composition overlay. */
+ sync_overlay (f);
+
+ /* If PT hasn't changed, the conversion region definitely has.
+ Otherwise, redisplay will update the input method instead. */
+
+ if (PT == w->ephemeral_last_point
+ && text_interface
+ && text_interface->compose_region_changed)
+ {
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_COMPOSE_CHANGE;
+ else
+ text_interface->compose_region_changed (f);
+ }
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+
+ unbind_to (count, Qnil);
+}
+
+/* Set the composing region to START by END. Make it that it is not
+ already set. */
+
+static void
+really_set_composing_region (struct frame *f, ptrdiff_t start,
+ ptrdiff_t end)
+{
+ specpdl_ref count;
+ struct window *w;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ /* If MAX (0, start) == end, then this should behave the same as
+ really_finish_composing_text. */
+
+ if (max (0, start) == max (0, end))
+ {
+ really_finish_composing_text (f);
+ return;
+ }
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+
+ /* Now set up the composition region if necessary. */
+
+ if (!MARKERP (f->conversion.compose_region_start))
+ {
+ f->conversion.compose_region_start = Fmake_marker ();
+ f->conversion.compose_region_end = Fmake_marker ();
+ Fset_marker_insertion_type (f->conversion.compose_region_end,
+ Qt);
+ }
+
+ Fset_marker (f->conversion.compose_region_start,
+ make_fixnum (start), Qnil);
+ Fset_marker (f->conversion.compose_region_end,
+ make_fixnum (end), Qnil);
+ sync_overlay (f);
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+
+ unbind_to (count, Qnil);
+}
+
+/* Delete LEFT and RIGHT chars around point or the active mark,
+ whichever is larger, avoiding the composing region if
+ necessary. */
+
+static void
+really_delete_surrounding_text (struct frame *f, ptrdiff_t left,
+ ptrdiff_t right)
+{
+ specpdl_ref count;
+ ptrdiff_t start, end, a, b, a1, b1, lstart, rstart;
+ struct window *w;
+ Lisp_Object text;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+
+ /* Figure out where to start deleting from. */
+
+ a = get_mark ();
+
+ if (a != -1 && a != PT)
+ lstart = rstart = max (a, PT);
+ else
+ lstart = rstart = PT;
+
+ /* Avoid the composing text. This behavior is identical to how
+ Android's BaseInputConnection actually implements avoiding the
+ composing span. */
+
+ if (MARKERP (f->conversion.compose_region_start))
+ {
+ a = marker_position (f->conversion.compose_region_start);
+ b = marker_position (f->conversion.compose_region_end);
+
+ a1 = min (a, b);
+ b1 = max (a, b);
+
+ lstart = min (lstart, min (PT, a1));
+ rstart = max (rstart, max (PT, b1));
+ }
+
+ if (lstart == rstart)
+ {
+ start = max (BEGV, lstart - left);
+ end = min (ZV, rstart + right);
+
+ text = del_range_1 (start, end, false, true);
+ record_buffer_change (start, start, text);
+ }
+ else
+ {
+ /* Don't record a deletion if the text which was deleted lies
+ after point. */
+
+ start = rstart;
+ end = min (ZV, rstart + right);
+ text = del_range_1 (start, end, false, true);
+ record_buffer_change (start, start, Qnil);
+
+ /* Now delete what must be deleted on the left. */
+
+ start = max (BEGV, lstart - left);
+ end = lstart;
+ text = del_range_1 (start, end, false, true);
+ record_buffer_change (start, start, text);
+ }
+
+ /* if the mark is now equal to start, deactivate it. */
+
+ if (get_mark () == PT)
+ call0 (Qdeactivate_mark);
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+
+ unbind_to (count, Qnil);
+}
+
+/* Update the interface with F's new point and mark. If a batch edit
+ is in progress, schedule the update for when it finishes
+ instead. */
+
+static void
+really_request_point_update (struct frame *f)
+{
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else if (text_interface && text_interface->point_changed)
+ text_interface->point_changed (f,
+ XWINDOW (f->old_selected_window),
+ current_buffer);
+}
+
+/* Set point in F to POSITION. If MARK is not POSITION, activate the
+ mark and set MARK to that as well.
+
+ If it has not changed, signal an update through the text input
+ interface, which is necessary for the IME to acknowledge that the
+ change has completed. */
+
+static void
+really_set_point_and_mark (struct frame *f, ptrdiff_t point,
+ ptrdiff_t mark)
+{
+ specpdl_ref count;
+ struct window *w;
+
+ /* If F's old selected window is no longer live, fail. */
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect (restore_selected_window,
+ selected_window);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+
+ if (point == PT)
+ {
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else if (text_interface && text_interface->point_changed)
+ text_interface->point_changed (f,
+ XWINDOW (f->old_selected_window),
+ current_buffer);
+ }
+ else
+ /* Set the point. */
+ Fgoto_char (make_fixnum (point));
+
+ if (mark == point && BVAR (current_buffer, mark_active))
+ call0 (Qdeactivate_mark);
+ else
+ call1 (Qpush_mark, make_fixnum (mark));
+
+ /* Update the ephemeral last point. */
+ w = XWINDOW (selected_window);
+ w->ephemeral_last_point = PT;
+
+ unbind_to (count, Qnil);
+}
+
+/* Complete the edit specified by the counter value inside *TOKEN. */
+
+static void
+complete_edit (void *token)
+{
+ if (text_interface && text_interface->notify_conversion)
+ text_interface->notify_conversion (*(unsigned long *) token);
+}
+
+/* Context for complete_edit_check. */
+
+struct complete_edit_check_context
+{
+ /* The window. */
+ struct window *w;
+
+ /* Whether or not editing was successful. */
+ bool check;
+};
+
+/* If CONTEXT->check is false, then update W's ephemeral last point
+ and give it to the input method, the assumption being that an
+ editing operation signalled. */
+
+static void
+complete_edit_check (void *ptr)
+{
+ struct complete_edit_check_context *context;
+ struct frame *f;
+
+ context = ptr;
+
+ if (!context->check)
+ {
+ /* Figure out the new position of point. */
+ context->w->ephemeral_last_point
+ = window_point (context->w);
+
+ /* See if the frame is still alive. */
+
+ f = WINDOW_XFRAME (context->w);
+
+ if (!FRAME_LIVE_P (f))
+ return;
+
+ if (text_interface && text_interface->point_changed)
+ {
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else
+ text_interface->point_changed (f, context->w, NULL);
+ }
+ }
+}
+
+/* Process and free the text conversion ACTION. F must be the frame
+ on which ACTION will be performed.
+
+ Value is the window which was used, or NULL. */
+
+static struct window *
+handle_pending_conversion_events_1 (struct frame *f,
+ struct text_conversion_action *action)
+{
+ Lisp_Object data;
+ enum text_conversion_operation operation;
+ struct buffer *buffer UNINIT;
+ struct window *w;
+ specpdl_ref count;
+ unsigned long token;
+ struct complete_edit_check_context context;
+
+ /* Next, process this action and free it. */
+
+ data = action->data;
+ operation = action->operation;
+ token = action->counter;
+ xfree (action);
+
+ /* Text conversion events can still arrive immediately after
+ `conversion_disabled_p' becomes true. In that case, process all
+ events, but don't perform any associated actions. */
+
+ if (conversion_disabled_p ())
+ return NULL;
+
+ /* check is a flag used by complete_edit_check to determine whether
+ or not the editing operation completed successfully. */
+ context.check = false;
+
+ /* Make sure completion is signalled. */
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (complete_edit, &token);
+ w = NULL;
+
+ if (WINDOW_LIVE_P (f->old_selected_window))
+ {
+ w = XWINDOW (f->old_selected_window);
+ buffer = XBUFFER (WINDOW_BUFFER (w));
+ context.w = w;
+
+ /* Notify the input method of any editing failures. */
+ record_unwind_protect_ptr (complete_edit_check, &context);
+ }
+
+ switch (operation)
+ {
+ case TEXTCONV_START_BATCH_EDIT:
+ f->conversion.batch_edit_count++;
+ break;
+
+ case TEXTCONV_END_BATCH_EDIT:
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_count--;
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ break;
+
+ if (f->conversion.batch_edit_flags & PENDING_POINT_CHANGE)
+ text_interface->point_changed (f, w, buffer);
+
+ if (f->conversion.batch_edit_flags & PENDING_COMPOSE_CHANGE)
+ text_interface->compose_region_changed (f);
+
+ f->conversion.batch_edit_flags = 0;
+ break;
+
+ case TEXTCONV_COMMIT_TEXT:
+ really_commit_text (f, XFIXNUM (XCAR (data)), XCDR (data));
+ break;
+
+ case TEXTCONV_FINISH_COMPOSING_TEXT:
+ really_finish_composing_text (f);
+ break;
+
+ case TEXTCONV_SET_COMPOSING_TEXT:
+ really_set_composing_text (f, XFIXNUM (XCAR (data)),
+ XCDR (data));
+ break;
+
+ case TEXTCONV_SET_COMPOSING_REGION:
+ really_set_composing_region (f, XFIXNUM (XCAR (data)),
+ XFIXNUM (XCDR (data)));
+ break;
+
+ case TEXTCONV_SET_POINT_AND_MARK:
+ really_set_point_and_mark (f, XFIXNUM (XCAR (data)),
+ XFIXNUM (XCDR (data)));
+ break;
+
+ case TEXTCONV_DELETE_SURROUNDING_TEXT:
+ really_delete_surrounding_text (f, XFIXNUM (XCAR (data)),
+ XFIXNUM (XCDR (data)));
+ break;
+
+ case TEXTCONV_REQUEST_POINT_UPDATE:
+ really_request_point_update (f);
+ break;
+ }
+
+ /* Signal success. */
+ context.check = true;
+ unbind_to (count, Qnil);
+
+ return w;
+}
+
+/* Decrement the variable pointed to by *PTR. */
+
+static void
+decrement_inside (void *ptr)
+{
+ int *i;
+
+ i = ptr;
+ (*i)--;
+}
+
+/* Process any outstanding text conversion events.
+ This may run Lisp or signal. */
+
+void
+handle_pending_conversion_events (void)
+{
+ struct frame *f;
+ Lisp_Object tail, frame;
+ struct text_conversion_action *action, *next;
+ bool handled;
+ static int inside;
+ specpdl_ref count;
+ ptrdiff_t last_point;
+ struct window *w;
+
+ handled = false;
+
+ /* Reset Vtext_conversion_edits. Do not do this if called
+ reentrantly. */
+
+ if (!inside)
+ Vtext_conversion_edits = Qnil;
+
+ inside++;
+ last_point = -1;
+ w = NULL;
+
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_ptr (decrement_inside, &inside);
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+
+ /* Test if F has any outstanding conversion events. Then
+ process them in bottom to up order. */
+ while (true)
+ {
+ /* Update the input method if handled &&
+ w->ephemeral_last_point != last_point. */
+ if (w && (last_point != w->ephemeral_last_point))
+ {
+ if (handled
+ && last_point != -1
+ && text_interface
+ && text_interface->point_changed)
+ {
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else
+ text_interface->point_changed (f, NULL, NULL);
+ }
+
+ last_point = w->ephemeral_last_point;
+ }
+
+ /* Reload action. This needs to be reentrant as buffer
+ modification functions can call `read-char'. */
+ action = f->conversion.actions;
+
+ /* If there are no more actions, break. */
+
+ if (!action)
+ break;
+
+ /* Unlink this action. */
+ next = action->next;
+ f->conversion.actions = next;
+
+ /* Handle and free the action. */
+ w = handle_pending_conversion_events_1 (f, action);
+ handled = true;
+ }
+ }
+
+ unbind_to (count, Qnil);
+}
+
+/* Start a ``batch edit'' in F. During a batch edit, point_changed
+ will not be called until the batch edit ends.
+
+ Process the actual operation in the event loop in keyboard.c; then,
+ call `notify_conversion' in the text conversion interface with
+ COUNTER. */
+
+void
+start_batch_edit (struct frame *f, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_START_BATCH_EDIT;
+ action->data = Qnil;
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* End a ``batch edit''. It is ok to call this function even if a
+ batch edit has not yet started, in which case it does nothing.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+end_batch_edit (struct frame *f, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_END_BATCH_EDIT;
+ action->data = Qnil;
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Insert the specified STRING into F's current buffer's composition
+ region, and set point to POSITION relative to STRING.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+commit_text (struct frame *f, Lisp_Object string,
+ ptrdiff_t position, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_COMMIT_TEXT;
+ action->data = Fcons (make_fixnum (position), string);
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Remove the composition region and its overlay from F's current
+ buffer. Leave the text being composed intact.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+finish_composing_text (struct frame *f, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_FINISH_COMPOSING_TEXT;
+ action->data = Qnil;
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Insert the given STRING and make it the currently active
+ composition.
+
+ If there is currently no composing region, then the new value of
+ point is used as the composing region.
+
+ Then, the composing region is replaced with the text in the
+ specified string.
+
+ Finally, move point to new_point, which is relative to either the
+ start or the end of OBJECT depending on whether or not it is less
+ than zero.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+set_composing_text (struct frame *f, Lisp_Object object,
+ ptrdiff_t new_point, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_SET_COMPOSING_TEXT;
+ action->data = Fcons (make_fixnum (new_point),
+ object);
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Make the region between START and END the currently active
+ ``composing region''.
+
+ The ``composing region'' is a region of text in the buffer that is
+ about to undergo editing by the input method. */
+
+void
+set_composing_region (struct frame *f, ptrdiff_t start,
+ ptrdiff_t end, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ if (start > MOST_POSITIVE_FIXNUM)
+ start = MOST_POSITIVE_FIXNUM;
+
+ if (end > MOST_POSITIVE_FIXNUM)
+ end = MOST_POSITIVE_FIXNUM;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_SET_COMPOSING_REGION;
+ action->data = Fcons (make_fixnum (start),
+ make_fixnum (end));
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Move point in F's selected buffer to POINT and maybe push MARK.
+
+ COUNTER means the same as in `start_batch_edit'. */
+
+void
+textconv_set_point_and_mark (struct frame *f, ptrdiff_t point,
+ ptrdiff_t mark, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ if (point > MOST_POSITIVE_FIXNUM)
+ point = MOST_POSITIVE_FIXNUM;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_SET_POINT_AND_MARK;
+ action->data = Fcons (make_fixnum (point),
+ make_fixnum (mark));
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Delete LEFT and RIGHT characters around point in F's old selected
+ window. */
+
+void
+delete_surrounding_text (struct frame *f, ptrdiff_t left,
+ ptrdiff_t right, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_DELETE_SURROUNDING_TEXT;
+ action->data = Fcons (make_fixnum (left),
+ make_fixnum (right));
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Request an immediate call to INTERFACE->point_changed with the new
+ details of F's region unless a batch edit is in progress. */
+
+void
+request_point_update (struct frame *f, unsigned long counter)
+{
+ struct text_conversion_action *action, **last;
+
+ action = xmalloc (sizeof *action);
+ action->operation = TEXTCONV_REQUEST_POINT_UPDATE;
+ action->data = Qnil;
+ action->next = NULL;
+ action->counter = counter;
+ for (last = &f->conversion.actions; *last; last = &(*last)->next)
+ ;;
+ *last = action;
+ input_pending = true;
+}
+
+/* Return N characters of text around point in F's old selected
+ window.
+
+ If N is -1, return the text between point and mark instead, given
+ that the mark is active.
+
+ Set *N to the actual number of characters returned, *START_RETURN
+ to the position of the first character returned, *OFFSET to the
+ offset of point within that text, *LENGTH to the actual number of
+ characters returned, and *BYTES to the actual number of bytes
+ returned.
+
+ Value is NULL upon failure, and a malloced string upon success. */
+
+char *
+get_extracted_text (struct frame *f, ptrdiff_t n,
+ ptrdiff_t *start_return,
+ ptrdiff_t *offset, ptrdiff_t *length,
+ ptrdiff_t *bytes)
+{
+ specpdl_ref count;
+ ptrdiff_t start, end, start_byte, end_byte;
+ char *buffer;
+
+ if (!WINDOW_LIVE_P (f->old_selected_window))
+ return NULL;
+
+ /* Save the excursion, as there will be extensive changes to the
+ selected window. */
+ count = SPECPDL_INDEX ();
+ record_unwind_protect_excursion ();
+
+ /* Inhibit quitting. */
+ specbind (Qinhibit_quit, Qt);
+
+ /* Temporarily switch to F's selected window at the time of the last
+ redisplay. */
+ select_window (f->old_selected_window, Qt);
+ buffer = NULL;
+
+ /* Figure out the bounds of the text to return. */
+ if (n != -1)
+ {
+ /* Make sure n is at least 4, leaving two characters around
+ PT. */
+ n = max (4, n);
+
+ start = PT - n / 2;
+ end = PT + n - n / 2;
+ }
+ else
+ {
+ if (!NILP (BVAR (current_buffer, mark_active))
+ && XMARKER (BVAR (current_buffer, mark))->buffer)
+ {
+ start = marker_position (BVAR (current_buffer, mark));
+ end = PT;
+
+ /* Sort start and end. start_byte is used to hold a
+ temporary value. */
+
+ if (start > end)
+ {
+ start_byte = end;
+ end = start;
+ start = start_byte;
+ }
+ }
+ else
+ goto finish;
+ }
+
+ start = max (start, BEGV);
+ end = min (end, ZV);
+
+ /* Detect overflow. */
+
+ if (!(start <= PT && PT <= end))
+ goto finish;
+
+ /* Convert the character positions to byte positions. */
+ start_byte = CHAR_TO_BYTE (start);
+ end_byte = CHAR_TO_BYTE (end);
+
+ /* Extract the text from the buffer. */
+ buffer = xmalloc (end_byte - start_byte);
+ copy_buffer (start, start_byte, end, end_byte,
+ buffer);
+
+ /* Return the offsets. */
+ *start_return = start;
+ *offset = PT - start;
+ *length = end - start;
+ *bytes = end_byte - start_byte;
+
+ finish:
+ unbind_to (count, Qnil);
+ return buffer;
+}
+
+/* Return whether or not text conversion is temporarily disabled.
+ `reset' should always call this to determine whether or not to
+ disable the input method. */
+
+bool
+conversion_disabled_p (void)
+{
+ return suppress_conversion_count > 0;
+}
+
/* Window system interface. These are called from the rest of
@@ -298,16 +1620,220 @@ textconv_query (struct frame *f, struct textconv_callback_struct *query)
void
report_selected_window_change (struct frame *f)
{
+ struct window *w;
+
+ reset_frame_state (f);
+
if (!text_interface)
return;
+ /* When called from window.c, F's selected window has already been
+ redisplayed, but w->last_point has not yet been updated. Update
+ it here to avoid race conditions when the IM asks for the initial
+ selection position immediately after. */
+
+ if (WINDOWP (f->selected_window))
+ {
+ w = XWINDOW (f->selected_window);
+ w->ephemeral_last_point = window_point (w);
+ }
+
text_interface->reset (f);
}
+/* Notice that the point in F's selected window's current buffer has
+ changed.
+
+ F is the frame whose selected window was changed, W is the window
+ in question, and BUFFER is that window's current buffer.
+
+ Tell the text conversion interface about the change; it will likely
+ pass the information on to the system input method. */
+
+void
+report_point_change (struct frame *f, struct window *window,
+ struct buffer *buffer)
+{
+ if (!text_interface || !text_interface->point_changed)
+ return;
+
+ if (f->conversion.batch_edit_count > 0)
+ f->conversion.batch_edit_flags |= PENDING_POINT_CHANGE;
+ else
+ text_interface->point_changed (f, window, buffer);
+}
+
+/* Temporarily disable text conversion. Must be paired with a
+ corresponding call to resume_text_conversion. */
+
+void
+disable_text_conversion (void)
+{
+ Lisp_Object tail, frame;
+ struct frame *f;
+
+ suppress_conversion_count++;
+
+ if (!text_interface || suppress_conversion_count > 1)
+ return;
+
+ /* Loop through and reset the input method on each window system
+ frame. It should call conversion_disabled_p and then DTRT. */
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+ reset_frame_state (f);
+
+ if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
+ text_interface->reset (f);
+ }
+}
+
+/* Undo the effect of the last call to `disable_text_conversion'. */
+
+void
+resume_text_conversion (void)
+{
+ Lisp_Object tail, frame;
+ struct frame *f;
+
+ suppress_conversion_count--;
+ eassert (suppress_conversion_count >= 0);
+
+ if (!text_interface || suppress_conversion_count)
+ return;
+
+ /* Loop through and reset the input method on each window system
+ frame. It should call conversion_disabled_p and then DTRT. */
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+ reset_frame_state (f);
+
+ if (FRAME_WINDOW_P (f) && FRAME_VISIBLE_P (f))
+ text_interface->reset (f);
+ }
+}
+
/* Register INTERFACE as the text conversion interface. */
void
-register_texconv_interface (struct textconv_interface *interface)
+register_textconv_interface (struct textconv_interface *interface)
{
text_interface = interface;
}
+
+
+
+/* Lisp interface. */
+
+DEFUN ("set-text-conversion-style", Fset_text_conversion_style,
+ Sset_text_conversion_style, 1, 1, 0,
+ doc: /* Set the text conversion style in the current buffer.
+
+Set `text-conversion-style' to VALUE, then force any input method
+editing frame displaying this buffer to stop itself.
+
+This can lead to a significant amount of time being taken by the input
+method resetting itself, so you should not use this function lightly;
+instead, set `text-conversion-style' before your buffer is displayed,
+and let redisplay manage the input method appropriately. */)
+ (Lisp_Object value)
+{
+ Lisp_Object tail, frame;
+ struct frame *f;
+ Lisp_Object buffer;
+
+ bset_text_conversion_style (current_buffer, value);
+
+ if (!text_interface)
+ return Qnil;
+
+ /* If there are any seleted windows displaying this buffer, reset
+ text conversion on their associated frames. */
+
+ if (buffer_window_count (current_buffer))
+ {
+ buffer = Fcurrent_buffer ();
+
+ FOR_EACH_FRAME (tail, frame)
+ {
+ f = XFRAME (frame);
+
+ if (WINDOW_LIVE_P (f->old_selected_window)
+ && FRAME_WINDOW_P (f)
+ && EQ (XWINDOW (f->old_selected_window)->contents,
+ buffer))
+ {
+ block_input ();
+ reset_frame_state (f);
+ text_interface->reset (f);
+ unblock_input ();
+ }
+ }
+ }
+
+ return Qnil;
+}
+
+
+
+void
+syms_of_textconv (void)
+{
+ DEFSYM (Qaction, "action");
+ DEFSYM (Qtext_conversion, "text-conversion");
+ DEFSYM (Qpush_mark, "push-mark");
+ DEFSYM (Qunderline, "underline");
+ DEFSYM (Qoverriding_text_conversion_style,
+ "overriding-text-conversion-style");
+
+ DEFVAR_LISP ("text-conversion-edits", Vtext_conversion_edits,
+ doc: /* List of buffers that were last edited as a result of text conversion.
+
+This list can be used while handling a `text-conversion' event to
+determine the changes which have taken place.
+
+Each element of the list describes a single edit in a buffer, of the
+form:
+
+ (BUFFER BEG END EPHEMERAL)
+
+If an insertion or a change occured, then BEG and END are markers
+which denote the bounds of the text that was changed or inserted.
+
+If EPHEMERAL is t, then the input method will shortly make more
+changes to the text, so any actions that would otherwise be taken
+(such as indenting or automatically filling text) should not take
+place; otherwise, it is a string describing the text which was
+inserted.
+
+If a deletion occured before point, then BEG and END are the same
+object, and EPHEMERAL is the text which was deleted.
+
+If a deletion occured after point, then BEG and END are also the same
+object, but EPHEMERAL is nil.
+
+The list contents are ordered later edits first, so you must iterate
+through the list in reverse. */);
+ Vtext_conversion_edits = Qnil;
+
+ DEFVAR_LISP ("overriding-text-conversion-style",
+ Voverriding_text_conversion_style,
+ doc: /* Non-buffer local version of `text-conversion-style'.
+
+If this variable is the symbol `lambda', it means to consult the
+buffer local variable `text-conversion-style' to determine whether or
+not to activate the input method. Otherwise, its value is used in
+preference to any buffer local value of `text-conversion-style'. */);
+ Voverriding_text_conversion_style = Qlambda;
+
+ DEFVAR_LISP ("text-conversion-face", Vtext_conversion_face,
+ doc: /* Face in which to display temporary edits by an input method.
+nil means to display no indication of a temporary edit. */);
+ Vtext_conversion_face = Qunderline;
+
+ defsubr (&Sset_text_conversion_style);
+}
diff --git a/src/textconv.h b/src/textconv.h
index f6e7eb7925f..6abca97bc52 100644
--- a/src/textconv.h
+++ b/src/textconv.h
@@ -34,6 +34,21 @@ struct textconv_interface
happen if the window is deleted or switches buffers, or an
unexpected buffer change occurs.) */
void (*reset) (struct frame *);
+
+ /* Notice that point or mark has moved in the specified frame's
+ selected window's selected buffer. The second argument is the
+ window whose point changed, and the third argument is the
+ buffer. */
+ void (*point_changed) (struct frame *, struct window *,
+ struct buffer *);
+
+ /* Notice that the preconversion region has changed without point
+ being moved. */
+ void (*compose_region_changed) (struct frame *);
+
+ /* Notice that an asynch conversion identified by COUNTER has
+ completed. */
+ void (*notify_conversion) (unsigned long);
};
@@ -103,7 +118,30 @@ struct textconv_callback_struct
struct textconv_conversion_text text;
};
-extern int textconv_query (struct frame *, struct textconv_callback_struct *);
-extern void register_texconv_interface (struct textconv_interface *);
+#define TEXTCONV_SKIP_CONVERSION_REGION (1 << 0)
+
+extern int textconv_query (struct frame *, struct textconv_callback_struct *,
+ int);
+extern bool detect_conversion_events (void);
+extern void handle_pending_conversion_events (void);
+extern void start_batch_edit (struct frame *, unsigned long);
+extern void end_batch_edit (struct frame *, unsigned long);
+extern void commit_text (struct frame *, Lisp_Object, ptrdiff_t,
+ unsigned long);
+extern void finish_composing_text (struct frame *, unsigned long);
+extern void set_composing_text (struct frame *, Lisp_Object,
+ ptrdiff_t, unsigned long);
+extern void set_composing_region (struct frame *, ptrdiff_t, ptrdiff_t,
+ unsigned long);
+extern void textconv_set_point_and_mark (struct frame *, ptrdiff_t,
+ ptrdiff_t, unsigned long);
+extern void delete_surrounding_text (struct frame *, ptrdiff_t,
+ ptrdiff_t, unsigned long);
+extern void request_point_update (struct frame *, unsigned long);
+extern char *get_extracted_text (struct frame *, ptrdiff_t, ptrdiff_t *,
+ ptrdiff_t *, ptrdiff_t *, ptrdiff_t *);
+extern bool conversion_disabled_p (void);
+
+extern void register_textconv_interface (struct textconv_interface *);
#endif /* _TEXTCONV_H_ */
diff --git a/src/verbose.mk.in b/src/verbose.mk.in
index a4e2aad9325..97799cee813 100644
--- a/src/verbose.mk.in
+++ b/src/verbose.mk.in
@@ -32,6 +32,11 @@ AM_V_GEN =
AM_V_GLOBALS =
AM_V_NO_PD =
AM_V_RC =
+AM_V_JAVAC =
+AM_V_DX =
+AM_V_AAPT =
+AM_V_ZIPALIGN =
+AM_V_SILENT =
else
# Whether $(info ...) works. This is to work around a bug in GNU Make
@@ -76,4 +81,10 @@ AM_V_GEN = @$(info $ GEN $@)
AM_V_GLOBALS = @$(info $ GEN globals.h)
AM_V_NO_PD = --no-print-directory
AM_V_RC = @$(info $ RC $@)
+
+# These are used for the Android port.
+AM_V_JAVAC = @$(info $ JAVAC $@)
+AM_V_D8 = @$(info $ D8 $@)
+AM_V_AAPT = @$(info $ AAPT $@)
+AM_V_SILENT = @
endif
diff --git a/src/w32.c b/src/w32.c
index a6bc0f4b2ee..c75beb630e5 100644
--- a/src/w32.c
+++ b/src/w32.c
@@ -10335,7 +10335,8 @@ check_windows_init_file (void)
names from UTF-8 to ANSI. */
init_file = build_string ("term/w32-win");
fd =
- openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0);
+ openp (Vload_path, init_file, Fget_load_suffixes (), NULL, Qnil, 0, 0,
+ NULL);
if (fd < 0)
{
Lisp_Object load_path_print = Fprin1_to_string (Vload_path,
diff --git a/src/w32proc.c b/src/w32proc.c
index 77a4ac1ff7e..edc4394b17f 100644
--- a/src/w32proc.c
+++ b/src/w32proc.c
@@ -1956,7 +1956,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
program = build_string (cmdname);
full = Qnil;
openp (Vexec_path, program, Vexec_suffixes, &full, make_fixnum (X_OK),
- 0, 0);
+ 0, 0, NULL);
if (NILP (full))
{
errno = EINVAL;
diff --git a/src/window.c b/src/window.c
index f4e09f49eae..8c42d3cdd0c 100644
--- a/src/window.c
+++ b/src/window.c
@@ -3514,7 +3514,10 @@ window-start value is reasonable when this function is called. */)
void
replace_buffer_in_windows (Lisp_Object buffer)
{
- call1 (Qreplace_buffer_in_windows, buffer);
+ /* When kill-buffer is called early during loadup, this function is
+ undefined. */
+ if (!NILP (Ffboundp (Qreplace_buffer_in_windows)))
+ call1 (Qreplace_buffer_in_windows, buffer);
}
/* If BUFFER is shown in a window, safely replace it with some other
diff --git a/src/window.h b/src/window.h
index 2f793ebe438..6b151efbe60 100644
--- a/src/window.h
+++ b/src/window.h
@@ -286,6 +286,25 @@ struct window
it should be positive. */
ptrdiff_t last_point;
+#ifdef HAVE_TEXT_CONVERSION
+ /* ``ephemeral'' last point position. This is used while
+ processing text conversion events.
+
+ `last_point' is normally used during redisplay to indicate the
+ position of point as seem by the input method. However, it is
+ not updated if consequtive conversions are processed at the
+ same time.
+
+ This `ephemeral_last_point' field is either the last point as
+ set in redisplay or the last point after a text editing
+ operation. */
+ ptrdiff_t ephemeral_last_point;
+#endif
+
+ /* Value of mark in the selected window at the time of the last
+ redisplay. */
+ ptrdiff_t last_mark;
+
/* Line number and position of a line somewhere above the top of the
screen. If this field is zero, it means we don't have a base line. */
ptrdiff_t base_line_number;
diff --git a/src/xdisp.c b/src/xdisp.c
index 982e1b3c103..89b1ae77e6f 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -15380,6 +15380,15 @@ redisplay_tool_bar (struct frame *f)
/* Always do that now. */
clear_glyph_matrix (w->desired_matrix);
f->fonts_changed = true;
+
+ /* Kludge (this applies to the X Windows version as well as
+ Android): when the tool bar size changes,
+ adjust_window_size (presumably called by
+ change_tool_bar_height_hook) does not call through to
+ resize_frame_windows. Pending further investigation,
+ just call it here as well. */
+ resize_frame_windows (f, FRAME_INNER_HEIGHT (f), false);
+
return true;
}
}
@@ -16567,7 +16576,7 @@ redisplay_internal (void)
display area, displaying a different frame means redisplay
the whole thing. */
SET_FRAME_GARBAGED (sf);
-#ifndef DOS_NT
+#if !defined DOS_NT && !defined HAVE_ANDROID
set_tty_color_mode (FRAME_TTY (sf), sf);
#endif
FRAME_TTY (sf)->previous_frame = sf;
@@ -17408,6 +17417,9 @@ static void
mark_window_display_accurate_1 (struct window *w, bool accurate_p)
{
struct buffer *b = XBUFFER (w->contents);
+#ifdef HAVE_TEXT_CONVERSION
+ ptrdiff_t prev_point, prev_mark;
+#endif
w->last_modified = accurate_p ? BUF_MODIFF (b) : 0;
w->last_overlay_modified = accurate_p ? BUF_OVERLAY_MODIFF (b) : 0;
@@ -17437,11 +17449,46 @@ mark_window_display_accurate_1 (struct window *w, bool accurate_p)
w->last_cursor_vpos = w->cursor.vpos;
w->last_cursor_off_p = w->cursor_off_p;
+#ifdef HAVE_TEXT_CONVERSION
+ prev_point = w->last_point;
+ prev_mark = w->last_mark;
+#endif
+
if (w == XWINDOW (selected_window))
w->last_point = BUF_PT (b);
else
w->last_point = marker_position (w->pointm);
+ if (XMARKER (BVAR (b, mark))->buffer == b)
+ w->last_mark = marker_position (BVAR (b, mark));
+ else
+ w->last_mark = -1;
+
+#ifdef HAVE_TEXT_CONVERSION
+ /* See the description of this field in struct window. */
+ w->ephemeral_last_point = w->last_point;
+
+ /* Point motion is only propagated to the input method for use
+ in text conversion during a redisplay. While this can lead
+ to inconsistencies when point has moved but the change has
+ not yet been displayed, it leads to better results most of
+ the time, as point often changes within calls to
+ `save-excursion', and the only way to detect such calls is to
+ observe that the next redisplay never ends with those changes
+ applied.
+
+ Changes to buffer text are immediately propagated to the
+ input method, and the position of point is also updated
+ during such a change, so the consequences are not that
+ severe. */
+
+ if ((prev_point != w->last_point
+ || prev_mark != w->last_mark)
+ && FRAME_WINDOW_P (WINDOW_XFRAME (w))
+ && w == XWINDOW (WINDOW_XFRAME (w)->selected_window))
+ report_point_change (WINDOW_XFRAME (w), w, b);
+#endif
+
w->window_end_valid = true;
w->update_mode_line = false;
w->preserve_vscroll_p = false;
@@ -26491,7 +26538,7 @@ display_menu_bar (struct window *w)
init_iterator (&it, w, -1, -1, f->desired_matrix->rows, MENU_FACE_ID);
it.first_visible_x = 0;
it.last_visible_x = FRAME_PIXEL_WIDTH (f);
-#elif defined (HAVE_X_WINDOWS) /* X without toolkit. */
+#elif defined (HAVE_X_WINDOWS) || defined (HAVE_ANDROID)
struct window *menu_window = NULL;
struct face *face = FACE_FROM_ID (f, MENU_FACE_ID);
@@ -26561,7 +26608,11 @@ display_menu_bar (struct window *w)
it.glyph_row->truncated_on_left_p = false;
it.glyph_row->truncated_on_right_p = false;
-#if defined (HAVE_X_WINDOWS) && !defined (USE_X_TOOLKIT) && !defined (USE_GTK)
+ /* This will break the moment someone tries to add another window
+ system that uses the no toolkit menu bar. Oh well. At least
+ there will be an error, meaning he will correct the ifdef inside
+ which `face' is defined. */
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
/* Make a 3D menu bar have a shadow at its right end. */
extend_face_to_end_of_line (&it);
if (face->box != FACE_NO_BOX)
@@ -26602,6 +26653,11 @@ display_menu_bar (struct window *w)
#endif
}
+/* This code is never used on Android where there are only GUI and
+ initial frames. */
+
+#ifndef HAVE_ANDROID
+
/* Deep copy of a glyph row, including the glyphs. */
static void
deep_copy_glyph_row (struct glyph_row *to, struct glyph_row *from)
@@ -26722,6 +26778,9 @@ display_tty_menu_item (const char *item_text, int width, int face_id,
row->full_width_p = saved_width;
row->reversed_p = saved_reversed;
}
+
+#endif
+
/***********************************************************************
Mode Line
@@ -33610,7 +33669,9 @@ draw_row_with_mouse_face (struct window *w, int start_x, struct glyph_row *row,
}
#endif
+#ifndef HAVE_ANDROID
tty_draw_row_with_mouse_face (w, row, start_hpos, end_hpos, draw);
+#endif
}
/* Display the active region described by mouse_face_* according to DRAW. */
@@ -35101,7 +35162,8 @@ note_mouse_highlight (struct frame *f, int x, int y)
struct buffer *b;
/* When a menu is active, don't highlight because this looks odd. */
-#if defined (HAVE_X_WINDOWS) || defined (HAVE_NS) || defined (MSDOS)
+#if defined (HAVE_X_WINDOWS) || defined (HAVE_NS) || defined (MSDOS) \
+ || defined (HAVE_ANDROID)
if (popup_activated ())
return;
#endif
@@ -36282,14 +36344,10 @@ expose_frame (struct frame *f, int x, int y, int w, int h)
|= expose_window (XWINDOW (f->tool_bar_window), &r);
#endif
-#ifdef HAVE_X_WINDOWS
-#ifndef MSDOS
-#if ! defined (USE_X_TOOLKIT) && ! defined (USE_GTK)
+#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR
if (WINDOWP (f->menu_bar_window))
mouse_face_overwritten_p
|= expose_window (XWINDOW (f->menu_bar_window), &r);
-#endif /* not USE_X_TOOLKIT and not USE_GTK */
-#endif
#endif
/* Some window managers support a focus-follows-mouse style with
@@ -36373,6 +36431,55 @@ gui_intersect_rectangles (const Emacs_Rectangle *r1, const Emacs_Rectangle *r2,
return intersection_p;
}
+/* EXPORT:
+ Determine the union of the rectangles A and B. Return the smallest
+ rectangle encompassing both the bounds of A and B in *RESULT. It
+ is safe for all three arguments to point to each other. */
+
+void
+gui_union_rectangles (const Emacs_Rectangle *a, const Emacs_Rectangle *b,
+ Emacs_Rectangle *result)
+{
+ struct gui_box a_box, b_box, result_box;
+
+ /* Handle special cases where one of the rectangles is empty. */
+
+ if (!a->width || !a->height)
+ {
+ *result = *b;
+ return;
+ }
+ else if (!b->width || !b->height)
+ {
+ *result = *a;
+ return;
+ }
+
+ /* Convert A and B to boxes. */
+ a_box.x1 = a->x;
+ a_box.y1 = a->y;
+ a_box.x2 = a->x + a->width;
+ a_box.y2 = a->y + a->height;
+
+ b_box.x1 = b->x;
+ b_box.y1 = b->y;
+ b_box.x2 = b->x + b->width;
+ b_box.y2 = b->y + b->height;
+
+ /* Compute the union of the boxes. */
+ result_box.x1 = min (a_box.x1, b_box.x1);
+ result_box.y1 = min (a_box.y1, b_box.y1);
+ result_box.x2 = max (a_box.x2, b_box.x2);
+ result_box.y2 = max (a_box.y2, b_box.y2);
+
+ /* Convert result_box to an XRectangle and put the result in
+ RESULT. */
+ result->x = result_box.x1;
+ result->y = result_box.y1;
+ result->width = result_box.x2 - result_box.x1;
+ result->height = result_box.y2 - result_box.y1;
+}
+
#endif /* HAVE_WINDOW_SYSTEM */
diff --git a/src/xfaces.c b/src/xfaces.c
index 37b703984be..af3428ad995 100644
--- a/src/xfaces.c
+++ b/src/xfaces.c
@@ -254,6 +254,10 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#ifdef HAVE_HAIKU
#define GCGraphicsExposures 0
#endif /* HAVE_HAIKU */
+
+#ifdef HAVE_ANDROID
+#define GCGraphicsExposures 0
+#endif /* HAVE_ANDROID */
#endif /* HAVE_WINDOW_SYSTEM */
#include "buffer.h"
@@ -607,6 +611,39 @@ x_free_gc (struct frame *f, Emacs_GC *gc)
}
#endif /* HAVE_NS */
+#ifdef HAVE_ANDROID
+
+/* Android real GCs. */
+
+static struct android_gc *
+x_create_gc (struct frame *f, unsigned long value_mask,
+ Emacs_GC *xgcv)
+{
+ struct android_gc_values gcv;
+ unsigned long mask;
+
+ gcv.foreground = xgcv->foreground;
+ gcv.background = xgcv->background;
+
+ mask = 0;
+
+ if (value_mask & GCForeground)
+ mask |= ANDROID_GC_FOREGROUND;
+
+ if (value_mask & GCBackground)
+ mask |= ANDROID_GC_BACKGROUND;
+
+ return android_create_gc (mask, &gcv);
+}
+
+static void
+x_free_gc (struct frame *f, struct android_gc *gc)
+{
+ android_free_gc (gc);
+}
+
+#endif
+
/***********************************************************************
Frames and faces
***********************************************************************/
@@ -6952,20 +6989,22 @@ where R,G,B are numbers between 0 and 255 and name is an arbitrary string. */)
int num;
while (fgets (buf, sizeof (buf), fp) != NULL)
- if (sscanf (buf, "%d %d %d %n", &red, &green, &blue, &num) == 3)
- {
+ {
+ if (sscanf (buf, "%d %d %d %n", &red, &green, &blue, &num) == 3)
+ {
#ifdef HAVE_NTGUI
- int color = RGB (red, green, blue);
+ int color = RGB (red, green, blue);
#else
- int color = (red << 16) | (green << 8) | blue;
+ int color = (red << 16) | (green << 8) | blue;
#endif
- char *name = buf + num;
- ptrdiff_t len = strlen (name);
- len -= 0 < len && name[len - 1] == '\n';
- cmap = Fcons (Fcons (make_string (name, len), make_fixnum (color)),
- cmap);
- }
- fclose (fp);
+ char *name = buf + num;
+ ptrdiff_t len = strlen (name);
+ len -= 0 < len && name[len - 1] == '\n';
+ cmap = Fcons (Fcons (make_string (name, len), make_fixnum (color)),
+ cmap);
+ }
+ }
+ emacs_fclose (fp);
}
unblock_input ();
return cmap;
diff --git a/src/xfns.c b/src/xfns.c
index 9e004f6a678..b7000462e84 100644
--- a/src/xfns.c
+++ b/src/xfns.c
@@ -3861,7 +3861,7 @@ xic_string_conversion_callback (XIC ic, XPointer client_data,
request.operation = TEXTCONV_RETRIEVAL;
/* Now perform the string conversion. */
- rc = textconv_query (f, &request);
+ rc = textconv_query (f, &request, 0);
if (rc)
{
@@ -5673,6 +5673,8 @@ that operating systems cannot be developed and distributed noncommercially.)
The optional argument TERMINAL specifies which display to ask about.
For GNU and Unix systems, this queries the X server software.
+For Android systems, value is the manufacturer who developed the Android
+system that is being used.
For MS Windows and Nextstep the result is hard-coded.
TERMINAL should be a terminal object, a frame or a display name (a string).
@@ -5696,7 +5698,8 @@ Protocol used on TERMINAL and the 3rd number is the distributor-specific
release number. For MS Windows, the 3 numbers report the OS major and
minor version and build number. For Nextstep, the first 2 numbers are
hard-coded and the 3rd represents the OS version. For Haiku, all 3
-numbers are hard-coded.
+numbers are hard-coded. For Android, the first number represents the
+Android API level, and the next two numbers are all zero.
See also the function `x-server-vendor'.
diff --git a/src/xterm.c b/src/xterm.c
index 0899fcdc2d6..ad4bec63a64 100644
--- a/src/xterm.c
+++ b/src/xterm.c
@@ -20111,6 +20111,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
#endif
+ /* See if keysym should make Emacs quit. */
+
+ if (keysym == dpyinfo->quit_keysym
+ && (xkey.time - dpyinfo->quit_keysym_time
+ <= 350))
+ {
+ Vquit_flag = Qt;
+ goto done_keysym;
+ }
+
+ if (keysym == dpyinfo->quit_keysym)
+ {
+ /* Otherwise, set the last time that keysym was
+ pressed. */
+ dpyinfo->quit_keysym_time = xkey.time;
+ goto done_keysym;
+ }
+
/* If not using XIM/XIC, and a compose sequence is in progress,
we break here. Otherwise, chars_matched is always 0. */
if (compose_status.chars_matched > 0 && nbytes == 0)
@@ -23874,6 +23892,24 @@ handle_one_xevent (struct x_display_info *dpyinfo,
}
#endif
+ /* See if keysym should make Emacs quit. */
+
+ if (keysym == dpyinfo->quit_keysym
+ && (xev->time - dpyinfo->quit_keysym_time
+ <= 350))
+ {
+ Vquit_flag = Qt;
+ goto xi_done_keysym;
+ }
+
+ if (keysym == dpyinfo->quit_keysym)
+ {
+ /* Otherwise, set the last time that keysym was
+ pressed. */
+ dpyinfo->quit_keysym_time = xev->time;
+ goto xi_done_keysym;
+ }
+
/* First deal with keysyms which have defined
translations to characters. */
if (keysym >= 32 && keysym < 128)
@@ -29878,6 +29914,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
struct terminal *terminal;
struct x_display_info *dpyinfo;
XrmDatabase xrdb;
+ Lisp_Object tem, quit_keysym;
#ifdef USE_XCB
xcb_connection_t *xcb_conn;
#endif
@@ -29888,7 +29925,7 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
GdkScreen *gscr;
#endif
#ifdef HAVE_XFIXES
- Lisp_Object tem, lisp_name;
+ Lisp_Object lisp_name;
int num_fast_selections;
Atom selection_name;
#ifdef USE_XCB
@@ -30165,6 +30202,28 @@ x_term_init (Lisp_Object display_name, char *xrm_option, char *resource_name)
terminal->kboard->reference_count++;
}
+ /* Now look through Vx_quit_keysym for the quit keysym associated
+ with this display. */
+ tem = Vx_quit_keysym;
+ FOR_EACH_TAIL_SAFE (tem)
+ {
+ quit_keysym = XCAR (tem);
+
+ /* Check if its car is a string and its cdr a valid keysym.
+ Skip if it is not. */
+
+ if (!CONSP (quit_keysym) || !FIXNUMP (XCDR (quit_keysym))
+ || !STRINGP (XCAR (quit_keysym)))
+ continue;
+
+ /* Check if this is the keysym to be used. */
+
+ if (strcmp (SSDATA (XCAR (quit_keysym)), ServerVendor (dpy)))
+ continue;
+
+ dpyinfo->quit_keysym = XFIXNUM (XCDR (quit_keysym));
+ }
+
/* Put this display on the chain. */
dpyinfo->next = x_display_list;
x_display_list = dpyinfo;
@@ -31575,7 +31634,7 @@ init_xterm (void)
#endif
#ifdef HAVE_X_I18N
- register_texconv_interface (&text_conversion_interface);
+ register_textconv_interface (&text_conversion_interface);
#endif
}
@@ -31909,7 +31968,9 @@ adjusted if the default value does not work for whatever reason. */);
A value of nil means Emacs doesn't use toolkit scroll bars.
With the X Window system, the value is a symbol describing the
X toolkit. Possible values are: gtk, motif, xaw, or xaw3d.
-With MS Windows, Haiku windowing or Nextstep, the value is t. */);
+With MS Windows, Haiku windowing or Nextstep, the value is t.
+With Android, the value is nil, but that is because Emacs on
+Android does not support scroll bars at all. */);
#ifdef USE_TOOLKIT_SCROLL_BARS
#ifdef USE_MOTIF
Vx_toolkit_scroll_bars = intern_c_string ("motif");
@@ -32256,4 +32317,23 @@ frame placement via frame parameters, `set-frame-position', and
`set-frame-size', along with the actual state of a frame after
`x_make_frame_invisible'. */);
Vx_lax_frame_positioning = Qnil;
+
+ DEFVAR_LISP ("x-quit-keysym", Vx_quit_keysym,
+ doc: /* Keysyms which will cause Emacs to quit if rapidly pressed twice.
+
+This is used to support quitting on devices that do not have any kind
+of physical keyboard, or where the physical keyboard is incapable of
+entering `C-g'. It defaults to `XF86XK_AudioLowerVolume' on XFree86
+and X.Org servers, and is unset.
+
+The value is an alist associating between strings, describing X server
+vendor names, and a single number describing the keysym to use. The
+keysym to use for each display connection is determined upon
+connection setup, and does not reflect further changes to this
+variable. */);
+ Vx_quit_keysym
+ = list2 (Fcons (build_string ("The X.Org Foundation"),
+ make_int (269025041)),
+ Fcons (build_string ("The XFree86 Project, Inc."),
+ make_int (269025041)));
}
diff --git a/src/xterm.h b/src/xterm.h
index 28ae00ca190..406a7c5c060 100644
--- a/src/xterm.h
+++ b/src/xterm.h
@@ -920,6 +920,13 @@ struct x_display_info
server_time_monotonic_p will be true). */
int_fast64_t server_time_offset;
#endif
+
+ /* Keysym that will cause Emacs to quit if pressed twice within 150
+ ms. */
+ KeySym quit_keysym;
+
+ /* The last time that keysym was pressed. */
+ Time quit_keysym_time;
};
#ifdef HAVE_X_I18N