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