1
0
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:
Po Lu 2023-01-19 22:19:06 +08:00
parent 6253e7e742
commit a496509ced
81 changed files with 4111 additions and 621 deletions

2
.gitignore vendored
View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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."

View File

@ -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."

View File

@ -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

View File

@ -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

View File

@ -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
View 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}.

View File

@ -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

View File

@ -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})

View File

@ -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
---

View File

@ -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.

View File

@ -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

View File

@ -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 ();

View File

@ -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);
}
}

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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));
}
}

View File

@ -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 ();

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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 ();
}
}

View File

@ -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;
}

View File

@ -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

View File

@ -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)

View 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;
}

View File

@ -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

View File

@ -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
View 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
View 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);
}

View File

@ -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))

View File

@ -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 */

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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
View 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
View 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],
[
:
])

View File

@ -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

View File

@ -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])
])

View File

@ -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])

View File

@ -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>]])

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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. */

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -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 */

View File

@ -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. */

View File

@ -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

View File

@ -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",

View File

@ -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 ();

View File

@ -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;

View File

@ -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")),

View File

@ -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);
}

View File

@ -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
View 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
View 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);
}

View File

@ -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))

View File

@ -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;
}

View File

@ -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)

View 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
View 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 */

View 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)

View 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)

View 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)

View 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)

View 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)

View 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 */

View 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)

View 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)

View 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)

View 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)