commit 08bf8533bb1d0e4a07e644799d25e4551024126f (HEAD, refs/remotes/origin/master) Author: Eli Zaretskii Date: Sun Aug 13 09:34:20 2023 +0300 Avoid unexpected implicit completion in find-func.el * lisp/emacs-lisp/find-func.el (find-function-read): Don't allow implicit completion, which could produce unexpected results. Patch by Jens Schmidt . (Bug#65058) diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el index bf890fc35a9..d393ccc759a 100644 --- a/lisp/emacs-lisp/find-func.el +++ b/lisp/emacs-lisp/find-func.el @@ -591,7 +591,7 @@ find-function-read (list (intern (completing-read (format-prompt "Find %s" symb prompt-type) obarray predicate - t nil nil (and symb (symbol-name symb))))))) + 'lambda nil nil (and symb (symbol-name symb))))))) (defun find-function-do-it (symbol type switch-fn) "Find Emacs Lisp SYMBOL in a buffer and display it. commit 7ca4c714cb89880a63fc7321644ae7069adb025b Author: Po Lu Date: Sun Aug 13 13:05:38 2023 +0800 ; * src/conf_post.h: Fix typo. diff --git a/src/conf_post.h b/src/conf_post.h index 524905949f3..5f18e5ae4bb 100644 --- a/src/conf_post.h +++ b/src/conf_post.h @@ -478,9 +478,9 @@ #define MB_CUR_MAX REPLACEMENT_MB_CUR_MAX defined in the C library and used by Gnulib. Define a prototype for it here. */ -#ifndef HAVE_DECL_ENDUTENT +#if !HAVE_DECL_ENDUTENT extern void endutent (void); -#endif /* HAVE_DECL_ENDUTENT */ +#endif /* !HAVE_DECL_ENDUTENT */ /* Now define substitutes for BOOT_TIME if necessary. */ @@ -488,12 +488,12 @@ #define MB_CUR_MAX REPLACEMENT_MB_CUR_MAX #include #define BOOT_TIME 2 -#endif /* UTMP_H_DEFINES_BOOT_TIME */ +#endif /* !UTMP_H_DEFINES_BOOT_TIME */ /* sysinfo is also absent from some versions of the NDK, yet is present on API level 9 and above. */ -#ifndef HAVE_DECL_SYSINFO +#if !HAVE_DECL_SYSINFO #include #if __ANDROID_API__ >= 9 @@ -502,6 +502,6 @@ #define BOOT_TIME 2 /* Gnulib uses this function unconditionally. */ #define sysinfo(ignored) ((void) ignored, (errno = ENOSYS), -1) #endif /* __ANDROID_API >= 9 */ -#endif /* HAVE_DECL_SYSINFO */ +#endif /* !HAVE_DECL_SYSINFO */ #endif /* __ANDROID__ */ commit e09f60045e3e8cead1e7f0eba94187d18ccdd72b Author: Po Lu Date: Sun Aug 13 12:58:25 2023 +0800 ; * src/conf_post.h: Fix typo. diff --git a/src/conf_post.h b/src/conf_post.h index d48c3494b57..524905949f3 100644 --- a/src/conf_post.h +++ b/src/conf_post.h @@ -501,6 +501,7 @@ #define BOOT_TIME 2 #else /* __ANDROID_API__ < 8 */ /* Gnulib uses this function unconditionally. */ #define sysinfo(ignored) ((void) ignored, (errno = ENOSYS), -1) +#endif /* __ANDROID_API >= 9 */ #endif /* HAVE_DECL_SYSINFO */ #endif /* __ANDROID__ */ commit 0e44ab5f061c81874dd8298a0f3318f14ef95a24 Author: Po Lu Date: Sun Aug 13 12:44:25 2023 +0800 Temporary fixes for the Android build * configure.ac: * src/conf_post.h [__ANDROID__]: Define substitutes for functions used by Gnulib that are occasionally absent. diff --git a/configure.ac b/configure.ac index 0234a82b92f..46836073aa0 100644 --- a/configure.ac +++ b/configure.ac @@ -2710,6 +2710,25 @@ AC_DEFUN # Check for some functions not always present in the NDK. AC_CHECK_DECLS([android_get_device_api_level]) + AC_CHECK_DECLS([endutent, sysinfo], [], [], + [[ +#include +#include +]]) + + # Establish if BOOT_TIME is defined in utmp.h. + AC_CACHE_CHECK([if utmp.h defines BOOT_TIME], + [emacs_cv_utmp_h_defines_boot_time], + [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include +#ifndef BOOT_TIME +BOOT_TIME not defined +#endif /* BOOT_TIME */ +]], [[]])], [emacs_cv_utmp_h_defines_boot_time=yes], + [emacs_cv_utmp_h_defines_boot_time=no])]) + AS_IF([test x"$emacs_cv_utmp_h_defines_boot_time" = xyes], + [AC_DEFINE([UTMP_H_DEFINES_BOOT_TIME], [1], + [Define to 1 if building for Android and utmp.h declares BOOT_TIME])]) # Say this build is really for Android. REALLY_ANDROID=yes])]) diff --git a/src/conf_post.h b/src/conf_post.h index f31e012dc6e..d48c3494b57 100644 --- a/src/conf_post.h +++ b/src/conf_post.h @@ -471,3 +471,36 @@ #define VFORK() vfork () #undef MB_CUR_MAX #define MB_CUR_MAX REPLACEMENT_MB_CUR_MAX #endif /* REPLACEMENT_MB_CUR_MAX */ + +#ifdef __ANDROID__ + +/* The Android NDK r10b omits the function `endutent' that is actually + defined in the C library and used by Gnulib. Define a prototype + for it here. */ + +#ifndef HAVE_DECL_ENDUTENT +extern void endutent (void); +#endif /* HAVE_DECL_ENDUTENT */ + +/* Now define substitutes for BOOT_TIME if necessary. */ + +#ifndef UTMP_H_DEFINES_BOOT_TIME +#include + +#define BOOT_TIME 2 +#endif /* UTMP_H_DEFINES_BOOT_TIME */ + +/* sysinfo is also absent from some versions of the NDK, yet is + present on API level 9 and above. */ + +#ifndef HAVE_DECL_SYSINFO +#include + +#if __ANDROID_API__ >= 9 +extern int sysinfo (struct sysinfo *info); +#else /* __ANDROID_API__ < 8 */ +/* Gnulib uses this function unconditionally. */ +#define sysinfo(ignored) ((void) ignored, (errno = ENOSYS), -1) +#endif /* HAVE_DECL_SYSINFO */ + +#endif /* __ANDROID__ */ commit 748521ba34965fbb5fa8d75eeb9822f4b8711379 Author: Po Lu Date: Sun Aug 13 11:23:14 2023 +0800 Fix the MS-DOS build * msdos/sed1v2.inp (TIMER_TIME_LIB): Define to empty string. diff --git a/msdos/sed1v2.inp b/msdos/sed1v2.inp index dd7f650e8e3..d299ec17574 100644 --- a/msdos/sed1v2.inp +++ b/msdos/sed1v2.inp @@ -186,6 +186,7 @@ s/ *@WEBP_LIBS@// /^HARFBUZZ_CFLAGS *=/s/@HARFBUZZ_CFLAGS@// /^HARFBUZZ_LIBS *=/s/@HARFBUZZ_LIBS@// /^QCOPY_ACL_LIB *=/s/@QCOPY_ACL_LIB@// +/^TIMER_TIME_LIB *=/s/@TIMER_TIME_LIB@// /^LCMS2_CFLAGS *=/s/@LCMS2_CFLAGS@// /^LCMS2_LIBS *=/s/@LCMS2_LIBS@// /^LIBGMP *=/s/@LIBGMP@// commit 5e0c2006088358e13ebabeeb185ae88b852a8691 Author: Po Lu Date: Sun Aug 13 11:10:50 2023 +0800 Fix the MS-DOS build * msdos/sedlibmk.inp (OMIT_GNULIB_MODULE_boot-time): Define to true. * src/filelock.c (get_boot_sec) [MSDOS]: Return 0; this information is probably unavailable. It is also irrelevant as MS-DOS is a single-process operating system. diff --git a/msdos/sedlibmk.inp b/msdos/sedlibmk.inp index f3554e7b001..664bfaf693c 100644 --- a/msdos/sedlibmk.inp +++ b/msdos/sedlibmk.inp @@ -489,6 +489,7 @@ OMIT_GNULIB_MODULE_strtoll = true\ OMIT_GNULIB_MODULE_symlink = true\ OMIT_GNULIB_MODULE_sys_select = true\ OMIT_GNULIB_MODULE_sys_time = true\ +OMIT_GNULIB_MODULE_boot-time = true\ OMIT_GNULIB_MODULE_crypto\/md5 = true /^arg-nonnull\.h:/,/^[ ][ ]*mv /c\ arg-nonnull.h: $(top_srcdir)/build-aux/snippet/arg-nonnull.h\ diff --git a/src/filelock.c b/src/filelock.c index d2161f1e58a..f3075b93322 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -107,7 +107,8 @@ Copyright (C) 1985-1987, 1993-1994, 1996, 1998-2023 Free Software hard nor symbolic links. */ -/* Return the time of the last system boot. */ +/* Return the time of the last system boot, or 0 if that information + is unavailable. */ static time_t get_boot_sec (void) @@ -117,10 +118,16 @@ get_boot_sec (void) if (will_dump_p ()) return 0; - struct timespec boot_time; - boot_time.tv_sec = 0; - get_boot_time (&boot_time); - return boot_time.tv_sec; +#ifndef MSDOS + { + struct timespec boot_time; + boot_time.tv_sec = 0; + get_boot_time (&boot_time); + return boot_time.tv_sec; + } +#else /* MSDOS */ + return 0; +#endif /* MSDOS */ } /* An arbitrary limit on lock contents length. 8 K should be plenty commit b7c194c1ead2ed5018b05482ef9e17a6a7b6f417 Author: Po Lu Date: Sun Aug 13 10:55:48 2023 +0800 ; Fix FSF address in some files' copyright notices * src/sfnt.c: * src/sfnt.h: * src/sfntfont-android.c: * src/sfntfont.c: * src/sfntfont.h: Replace the old FSF address with a link to gnu.org/licenses. These addresses were inadvertently imported when sfnt.c was copied to src. diff --git a/src/sfnt.c b/src/sfnt.c index 8ccb672c5d5..57aa4e3c28e 100644 --- a/src/sfnt.c +++ b/src/sfnt.c @@ -15,8 +15,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs. If not, write to the Free Software Foundation, -Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +along with GNU Emacs. If not, see . */ #include diff --git a/src/sfnt.h b/src/sfnt.h index 365595fa37d..48d22aa8d29 100644 --- a/src/sfnt.h +++ b/src/sfnt.h @@ -15,8 +15,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs. If not, write to the Free Software Foundation, -Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +along with GNU Emacs. If not, see . */ #ifndef _SFNT_H_ #define _SFNT_H_ diff --git a/src/sfntfont-android.c b/src/sfntfont-android.c index de2a9253b57..53589078cda 100644 --- a/src/sfntfont-android.c +++ b/src/sfntfont-android.c @@ -15,8 +15,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs. If not, write to the Free Software Foundation, -Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +along with GNU Emacs. If not, see . */ #include #include diff --git a/src/sfntfont.c b/src/sfntfont.c index dbd244e772c..531622f84df 100644 --- a/src/sfntfont.c +++ b/src/sfntfont.c @@ -15,8 +15,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs. If not, write to the Free Software Foundation, -Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +along with GNU Emacs. If not, see . */ #include diff --git a/src/sfntfont.h b/src/sfntfont.h index df387512d0d..28267cdb1d6 100644 --- a/src/sfntfont.h +++ b/src/sfntfont.h @@ -15,8 +15,7 @@ Copyright (C) 2023 Free Software Foundation, Inc. GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with GNU Emacs. If not, write to the Free Software Foundation, -Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +along with GNU Emacs. If not, see . */ #ifndef _SFNTFONT_H_ #define _SFNTFONT_H_ commit 5e736ca6ccfa131736ab0b3a298de2cb319e7dfb Author: Paul Eggert Date: Sat Aug 12 19:39:11 2023 -0700 Improve boot-time gathering Simplify Emacs proper by using Gnulib’s boot-time module instead of doing it all by hand. This should port Emacs better to obscurish hosts, as Bruno Haible has merged the best of Emacs’s and Gnulib’s boot-time gathering. * lib/boot-time-aux.h, lib/boot-time.c, lib/boot-time.h: * lib/readutmp.h, m4/readutmp.m4: New files, copied from Gnulib. * admin/merge-gnulib (GNULIB_MODULES): Add boot-time. * configure.ac: Do not check for utmp.h; the boot-time module now does this. (BOOT_TIME_FILE): Remove; no longer used. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. * src/filelock.c [__FreeBSD__]: Do not include . [HAVE_UTMP_H]: Do not include utmp.h. Include boot-time instead: boot-time does the work now. (BOOT_TIME) [HAVE_ANDROID && !ANDROID_STUBIFY]: Don’t undef. (WTMP_FILE): Don’t define. (boot_time, boot_time_initialized, get_boot_time_1, get_boot_time): Remove. (get_boot_sec): New function that simply calls Gnulib get_boot_time. (lock_file_1, current_lock_owner): Use get_boot_sec instead of get_boot_time. diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 2a713beb01a..fe88d1106ae 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -26,7 +26,7 @@ GNULIB_URL=https://git.savannah.gnu.org/git/gnulib.git GNULIB_MODULES=' - alignasof alloca-opt binary-io byteswap c-ctype c-strcase + alignasof alloca-opt binary-io boot-time byteswap c-ctype c-strcase canonicalize-lgpl careadlinkat close-stream copy-file-range count-leading-zeros count-one-bits count-trailing-zeros diff --git a/configure.ac b/configure.ac index 35dfed247d0..0234a82b92f 100644 --- a/configure.ac +++ b/configure.ac @@ -2539,7 +2539,7 @@ AC_DEFUN sys/sysinfo.h coff.h pty.h sys/resource.h - sys/utsname.h pwd.h utmp.h util.h + sys/utsname.h pwd.h util.h sanitizer/lsan_interface.h sanitizer/asan_interface.h sanitizer/common_interface_defs.h]) @@ -2635,51 +2635,6 @@ AC_DEFUN fi AC_SUBST([AUTO_DEPEND]) -BOOT_TIME_FILE= -AC_CACHE_CHECK([for old but post-boot file], - [emacs_cv_boot_time_file], - [AS_CASE([$opsys], - [gnu-linux], - [emacs_cv_boot_time_file=unknown - AS_IF([test $cross_compiling = no], - [# systemd puts it in /var/lib/systemd. - # initscripts puts it in /var/lib/urandom (previously /var/lib). - # Linux drivers/char/random.c before 2022-02-21 suggests /var/run. - for file in \ - /var/lib/systemd/random-seed \ - /var/lib/urandom/random-seed \ - /var/lib/random-seed \ - /var/run/random-seed - do - test -f $file && { emacs_cv_boot_time_file=$file; break; } - done])], - # This isn't perfect, as some systems might have the page file in - # another place. Also, I suspect that the time stamp of that - # file might also change when Windows enlarges the file due to - # insufficient VM. Still, this seems to be the most reliable - # way; the alternative (of using GetSystemTimes) won't work on - # laptops that hibernate, because the system clock is stopped - # then. Other possibility would be to run "net statistics - # workstation" and parse the output, but that's gross. So this - # should do; if the file is not there, the boot time will be - # returned as zero, and filelock.c already handles that. - [mingw32], [emacs_cv_boot_time_file=C:/pagefile.sys], - [*], [emacs_cv_boot_time_file=not-needed])]) - -AS_CASE([$emacs_cv_boot_time_file], - [/*|*:*], [BOOT_TIME_FILE=\"$emacs_cv_boot_time_file\"], - [not-needed], [BOOT_TIME_FILE=], - [# Guess systemd if unknown. - # If guess is wrong, Emacs falls back on something else. - BOOT_TIME_FILE=\"/var/lib/systemd/random-seed\"]) - -AS_IF([test -n "$BOOT_TIME_FILE"], - [AC_DEFINE_UNQUOTED([BOOT_TIME_FILE], [$BOOT_TIME_FILE], - [Name of file that, if it exists, postdates boot and predates - the first Emacs invocation; or a null pointer if no such file is known. - This file is used only on GNU/Linux and other systems - that lack the FreeBSD-style sysctl with KERN_BOOTTIME.])]) - #### Choose a window system. ## We leave window_system equal to none if diff --git a/lib/boot-time-aux.h b/lib/boot-time-aux.h new file mode 100644 index 00000000000..348611fc85c --- /dev/null +++ b/lib/boot-time-aux.h @@ -0,0 +1,315 @@ +/* Auxiliary functions for determining the time when the machine last booted. + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + This 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible . */ + +#define SIZEOF(a) (sizeof(a)/sizeof(a[0])) + +#if defined __linux__ || defined __ANDROID__ + +/* Store the uptime counter, as managed by the Linux kernel, in *P_UPTIME. + Return 0 upon success, -1 upon failure. */ +_GL_ATTRIBUTE_MAYBE_UNUSED +static int +get_linux_uptime (struct timespec *p_uptime) +{ + /* The clock_gettime facility returns the uptime with a resolution of 1 µsec. + It is available with glibc >= 2.14, Android, or musl libc. + In glibc < 2.17 it required linking with librt. */ +# if !defined __GLIBC__ || 2 < __GLIBC__ + (17 <= __GLIBC_MINOR__) + if (clock_gettime (CLOCK_BOOTTIME, p_uptime) >= 0) + return 0; +# endif + + /* /proc/uptime contains the uptime with a resolution of 0.01 sec. + But it does not have read permissions on Android. */ +# if !defined __ANDROID__ + FILE *fp = fopen ("/proc/uptime", "re"); + if (fp != NULL) + { + char buf[32 + 1]; + size_t n = fread (buf, 1, sizeof (buf) - 1, fp); + fclose (fp); + if (n > 0) + { + buf[n] = '\0'; + /* buf now contains two values: the uptime and the idle time. */ + time_t s = 0; + char *p; + for (p = buf; '0' <= *p && *p <= '9'; p++) + s = 10 * s + (*p - '0'); + if (buf < p) + { + long ns = 0; + if (*p++ == '.') + for (int i = 0; i < 9; i++) + ns = 10 * ns + ('0' <= *p && *p <= '9' ? *p++ - '0' : 0); + p_uptime->tv_sec = s; + p_uptime->tv_nsec = ns; + return 0; + } + } + } +# endif + + /* The sysinfo call returns the uptime with a resolution of 1 sec only. */ + struct sysinfo info; + if (sysinfo (&info) >= 0) + { + p_uptime->tv_sec = info.uptime; + p_uptime->tv_nsec = 0; + return 0; + } + + return -1; +} + +#endif + +#if defined __linux__ && !defined __ANDROID__ + +static int +get_linux_boot_time_fallback (struct timespec *p_boot_time) +{ + /* On Alpine Linux, UTMP_FILE is not filled. It is always empty. + So, get the time stamp of a file that gets touched only during the + boot process. */ + + const char * const boot_touched_files[] = + { + "/var/lib/systemd/random-seed", /* seen on distros with systemd */ + "/var/run/utmp", /* seen on distros with OpenRC */ + "/var/lib/random-seed" /* seen on older distros */ + }; + for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++) + { + const char *filename = boot_touched_files[i]; + struct stat statbuf; + if (stat (filename, &statbuf) >= 0) + { + *p_boot_time = get_stat_mtime (&statbuf); + return 0; + } + } + return -1; +} + +/* The following approach is only usable as a fallback, because it is of + the form + boot_time = (time now) - (kernel's ktime_get_boottime[_ts64] ()) + and therefore produces wrong values after the date has been bumped in the + running system, which happens frequently if the system is running in a + virtual machine and this VM has been put into "saved" or "sleep" state + and then resumed. */ +static int +get_linux_boot_time_final_fallback (struct timespec *p_boot_time) +{ + struct timespec uptime; + if (get_linux_uptime (&uptime) >= 0) + { + struct timespec result; +# if !defined __GLIBC__ || 2 < __GLIBC__ + (16 <= __GLIBC_MINOR__) + /* Better than: + if (0 <= clock_gettime (CLOCK_REALTIME, &result)) + because timespec_get does not need -lrt in glibc 2.16. + */ + if (! timespec_get (&result, TIME_UTC)) + return -1; +# else + /* Fall back on lower-res approach that does not need -lrt. + This is good enough; on these hosts UPTIME is even lower-res. */ + struct timeval tv; + int r = gettimeofday (&tv, NULL); + if (r < 0) + return r; + result.tv_sec = tv.tv_sec; + result.tv_nsec = tv.tv_usec * 1000; +# endif + + if (result.tv_nsec < uptime.tv_nsec) + { + result.tv_nsec += 1000000000; + result.tv_sec -= 1; + } + result.tv_sec -= uptime.tv_sec; + result.tv_nsec -= uptime.tv_nsec; + *p_boot_time = result; + return 0; + } + return -1; +} + +#endif + +#if defined __ANDROID__ + +static int +get_android_boot_time (struct timespec *p_boot_time) +{ + /* On Android, there is no /var, and normal processes don't have access + to system files. Therefore use the kernel's uptime counter, although + it produces wrong values after the date has been bumped in the running + system. */ + struct timespec uptime; + if (get_linux_uptime (&uptime) >= 0) + { + struct timespec result; + if (clock_gettime (CLOCK_REALTIME, &result) >= 0) + { + if (result.tv_nsec < uptime.tv_nsec) + { + result.tv_nsec += 1000000000; + result.tv_sec -= 1; + } + result.tv_sec -= uptime.tv_sec; + result.tv_nsec -= uptime.tv_nsec; + *p_boot_time = result; + return 0; + } + } + return -1; +} + +#endif + +#if defined __OpenBSD__ + +static int +get_openbsd_boot_time (struct timespec *p_boot_time) +{ + /* On OpenBSD, UTMP_FILE is not filled. It contains only dummy entries. + So, get the time stamp of a file that gets touched only during the + boot process. */ + const char * const boot_touched_files[] = + { + "/var/db/host.random", + "/var/run/utmp" + }; + for (idx_t i = 0; i < SIZEOF (boot_touched_files); i++) + { + const char *filename = boot_touched_files[i]; + struct stat statbuf; + if (stat (filename, &statbuf) >= 0) + { + *p_boot_time = get_stat_mtime (&statbuf); + return 0; + } + } + return -1; +} + +#endif + +#if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \ + && defined CTL_KERN && defined KERN_BOOTTIME \ + && !defined __minix +/* macOS, FreeBSD, GNU/kFreeBSD, NetBSD, OpenBSD */ +/* On Minix 3.3 this sysctl produces garbage results. Therefore avoid it. */ + +/* The following approach is only usable as a fallback, because it produces + wrong values after the date has been bumped in the running system, which + happens frequently if the system is running in a virtual machine and this + VM has been put into "saved" or "sleep" state and then resumed. */ +static int +get_bsd_boot_time_final_fallback (struct timespec *p_boot_time) +{ + static int request[2] = { CTL_KERN, KERN_BOOTTIME }; + struct timeval result; + size_t result_len = sizeof result; + + if (sysctl (request, 2, &result, &result_len, NULL, 0) >= 0) + { + p_boot_time->tv_sec = result.tv_sec; + p_boot_time->tv_nsec = result.tv_usec * 1000; + return 0; + } + return -1; +} + +#endif + +#if defined __HAIKU__ + +static int +get_haiku_boot_time (struct timespec *p_boot_time) +{ + /* On Haiku, /etc/utmp does not exist. During boot, + 1. the current time is restored, but possibly with a wrong time zone, + that is, with an offset of a few hours, + 2. some symlinks and files get created, + 3. the various devices are brought up, in particular the network device, + 4. the correct date and time is set, + 5. some more device nodes get created. + The boot time can be retrieved by looking at a directory created during + phase 5, such as /dev/input. */ + const char * const boot_touched_file = "/dev/input"; + struct stat statbuf; + if (stat (boot_touched_file, &statbuf) >= 0) + { + *p_boot_time = get_stat_mtime (&statbuf); + return 0; + } + return -1; +} + +#endif + +#if HAVE_OS_H /* BeOS, Haiku */ + +/* The following approach is only usable as a fallback, because it produces + wrong values after the date has been bumped in the running system, which + happens frequently if the system is running in a virtual machine and this + VM has been put into "saved" or "sleep" state and then resumed. */ +static int +get_haiku_boot_time_final_fallback (struct timespec *p_boot_time) +{ + system_info si; + + get_system_info (&si); + p_boot_time->tv_sec = si.boot_time / 1000000; + p_boot_time->tv_nsec = (si.boot_time % 1000000) * 1000; + return 0; +} + +#endif + +#if defined __CYGWIN__ || defined _WIN32 + +static int +get_windows_boot_time (struct timespec *p_boot_time) +{ + /* On Cygwin, /var/run/utmp is empty. + On native Windows, and don't exist. + Instead, on Windows, the boot time can be retrieved by looking at the + time stamp of a file that (normally) gets touched only during the boot + process, namely C:\pagefile.sys. */ + const char * const boot_touched_file = + #if defined __CYGWIN__ && !defined _WIN32 + "/cygdrive/c/pagefile.sys" + #else + "C:\\pagefile.sys" + #endif + ; + struct stat statbuf; + if (stat (boot_touched_file, &statbuf) >= 0) + { + *p_boot_time = get_stat_mtime (&statbuf); + return 0; + } + return -1; +} + +#endif diff --git a/lib/boot-time.c b/lib/boot-time.c new file mode 100644 index 00000000000..d813bfa5825 --- /dev/null +++ b/lib/boot-time.c @@ -0,0 +1,285 @@ +/* Determine the time when the machine last booted. + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + This 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible . */ + +#include + +/* Specification. */ +#include "boot-time.h" + +#include +#include +#include +#include +#include + +#if defined __linux__ || defined __ANDROID__ +# include +# include +#endif + +#if HAVE_SYS_SYSCTL_H && !defined __minix +# if HAVE_SYS_PARAM_H +# include +# endif +# include +#endif + +#if HAVE_OS_H +# include +#endif + +#include "idx.h" +#include "readutmp.h" +#include "stat-time.h" + +/* Each of the FILE streams in this file is only used in a single thread. */ +#include "unlocked-io.h" + +/* Some helper functions. */ +#include "boot-time-aux.h" + +/* The following macros describe the 'struct UTMP_STRUCT_NAME', + *not* 'struct gl_utmp'. */ +#undef UT_USER + +/* Accessor macro for the member named ut_user or ut_name. */ +#if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_NAME \ + : HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_NAME) +# define UT_USER(UT) ((UT)->ut_name) +#else +# define UT_USER(UT) ((UT)->ut_user) +#endif + +#if !HAVE_UTMPX_H && HAVE_UTMP_H && defined UTMP_NAME_FUNCTION && !HAVE_DECL_GETUTENT +struct utmp *getutent (void); +#endif + +#if defined __linux__ || HAVE_UTMPX_H || HAVE_UTMP_H || defined __CYGWIN__ || defined _WIN32 + +static int +get_boot_time_uncached (struct timespec *p_boot_time) +{ + struct timespec found_boot_time = {0}; + +# if (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE) + + /* Try to find the boot time in the /var/run/utmp file. */ + +# if defined UTMP_NAME_FUNCTION /* glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, IRIX, Solaris, Cygwin, Android */ + + /* Ignore the return value for now. + Solaris' utmpname returns 1 upon success -- which is contrary + to what the GNU libc version does. In addition, older GNU libc + versions are actually void. */ + UTMP_NAME_FUNCTION ((char *) UTMP_FILE); + + SET_UTMP_ENT (); + +# if (defined __linux__ && !defined __ANDROID__) || defined __minix + /* Timestamp of the "runlevel" entry, if any. */ + struct timespec runlevel_ts = {0}; +# endif + + void const *entry; + + while ((entry = GET_UTMP_ENT ()) != NULL) + { + struct UTMP_STRUCT_NAME const *ut = (struct UTMP_STRUCT_NAME const *) entry; + + struct timespec ts = + #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV) + { .tv_sec = ut->ut_tv.tv_sec, .tv_nsec = ut->ut_tv.tv_usec * 1000 }; + #else + { .tv_sec = ut->ut_time, .tv_nsec = 0 }; + #endif + + if (ut->ut_type == BOOT_TIME) + found_boot_time = ts; + +# if defined __linux__ && !defined __ANDROID__ + if (memcmp (UT_USER (ut), "runlevel", strlen ("runlevel") + 1) == 0 + && memcmp (ut->ut_line, "~", strlen ("~") + 1) == 0) + runlevel_ts = ts; +# endif +# if defined __minix + if (UT_USER (ut)[0] == '\0' + && memcmp (ut->ut_line, "run-level ", strlen ("run-level ")) == 0) + runlevel_ts = ts; +# endif + } + + END_UTMP_ENT (); + +# if defined __linux__ && !defined __ANDROID__ + /* On Raspbian, which runs on hardware without a real-time clock, during boot, + 1. the clock gets set to 1970-01-01 00:00:00, + 2. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME, + ut_user = "reboot", ut_line = "~", time = 1970-01-01 00:00:05 or so, + 3. the clock gets set to a correct value through NTP, + 4. an entry gets written into /var/run/utmp, with + ut_user = "runlevel", ut_line = "~", time = correct value. + In this case, get the time from the "runlevel" entry. */ + + /* Workaround for Raspbian: */ + if (found_boot_time.tv_sec <= 60 && runlevel_ts.tv_sec != 0) + found_boot_time = runlevel_ts; + if (found_boot_time.tv_sec == 0) + { + /* Workaround for Alpine Linux: */ + get_linux_boot_time_fallback (&found_boot_time); + } +# endif + +# if defined __ANDROID__ + if (found_boot_time.tv_sec == 0) + { + /* Workaround for Android: */ + get_android_boot_time (&found_boot_time); + } +# endif + +# if defined __minix + /* On Minix, during boot, + 1. an entry gets written into /var/run/utmp, with ut_type = BOOT_TIME, + ut_user = "", ut_line = "system boot", time = 1970-01-01 00:00:00, + 2. an entry gets written into /var/run/utmp, with + ut_user = "", ut_line = "run-level m", time = correct value. + In this case, copy the time from the "run-level m" entry to the + "system boot" entry. */ + if (found_boot_time.tv_sec <= 60 && runlevel_ts.tv_sec != 0) + found_boot_time = runlevel_ts; +# endif + +# else /* HP-UX, Haiku */ + + FILE *f = fopen (UTMP_FILE, "re"); + + if (f != NULL) + { + for (;;) + { + struct UTMP_STRUCT_NAME ut; + + if (fread (&ut, sizeof ut, 1, f) == 0) + break; + + struct timespec ts = + #if (HAVE_UTMPX_H ? 1 : HAVE_STRUCT_UTMP_UT_TV) + { .tv_sec = ut.ut_tv.tv_sec, .tv_nsec = ut.ut_tv.tv_usec * 1000 }; + #else + { .tv_sec = ut.ut_time, .tv_nsec = 0 }; + #endif + + if (ut.ut_type == BOOT_TIME) + found_boot_time = ts; + } + + fclose (f); + } + +# endif + +# if defined __linux__ && !defined __ANDROID__ + if (found_boot_time.tv_sec == 0) + { + get_linux_boot_time_final_fallback (&found_boot_time); + } +# endif + +# else /* old FreeBSD, OpenBSD, native Windows */ + +# if defined __OpenBSD__ + /* Workaround for OpenBSD: */ + get_openbsd_boot_time (&found_boot_time); +# endif + +# endif + +# if HAVE_SYS_SYSCTL_H && HAVE_SYSCTL \ + && defined CTL_KERN && defined KERN_BOOTTIME \ + && !defined __minix + if (found_boot_time.tv_sec == 0) + { + get_bsd_boot_time_final_fallback (&found_boot_time); + } +# endif + +# if defined __HAIKU__ + if (found_boot_time.tv_sec == 0) + { + get_haiku_boot_time (&found_boot_time); + } +# endif + +# if HAVE_OS_H + if (found_boot_time.tv_sec == 0) + { + get_haiku_boot_time_final_fallback (&found_boot_time); + } +# endif + +# if defined __CYGWIN__ || defined _WIN32 + if (found_boot_time.tv_sec == 0) + { + /* Workaround for Windows: */ + get_windows_boot_time (&found_boot_time); + } +# endif + + if (found_boot_time.tv_sec != 0) + { + *p_boot_time = found_boot_time; + return 0; + } + else + return -1; +} + +int +get_boot_time (struct timespec *p_boot_time) +{ + /* Cache the result from get_boot_time_uncached. */ + static int volatile cached_result = -1; + static struct timespec volatile cached_boot_time; + + if (cached_result < 0) + { + struct timespec boot_time; + int result = get_boot_time_uncached (&boot_time); + cached_boot_time = boot_time; + cached_result = result; + } + + if (cached_result == 0) + { + *p_boot_time = cached_boot_time; + return 0; + } + else + return -1; +} + +#else + +int +get_boot_time (struct timespec *p_boot_time) +{ + return -1; +} + +#endif diff --git a/lib/boot-time.h b/lib/boot-time.h new file mode 100644 index 00000000000..401e854adbb --- /dev/null +++ b/lib/boot-time.h @@ -0,0 +1,44 @@ +/* Determine the time when the machine last booted. + Copyright (C) 2023 Free Software Foundation, Inc. + + This file is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + This 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible . */ + +#ifndef _BOOT_TIME_H +#define _BOOT_TIME_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Store the approximate time when the machine last booted in *P_BOOT_TIME, + and return 0. If it cannot be determined, return -1. + + This function is not multithread-safe, since on many platforms it + invokes the functions setutxent, getutxent, endutxent. These + functions are needed because they may lock FILE (so that we don't + read garbage when a concurrent process writes to FILE), but their + drawback is that they have a common global state. */ +extern int get_boot_time (struct timespec *p_boot_time); + + +#ifdef __cplusplus +} +#endif + +#endif /* _BOOT_TIME_H */ diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 785bdc70c5c..3b33f39f73b 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -76,6 +76,7 @@ # alignasof \ # alloca-opt \ # binary-io \ +# boot-time \ # byteswap \ # c-ctype \ # c-strcase \ @@ -1601,6 +1602,16 @@ libgnu_a_SOURCES += binary-io.h binary-io.c endif ## end gnulib module binary-io +## begin gnulib module boot-time +ifeq (,$(OMIT_GNULIB_MODULE_boot-time)) + +libgnu_a_SOURCES += boot-time.c + +EXTRA_DIST += boot-time-aux.h boot-time.h readutmp.h + +endif +## end gnulib module boot-time + ## begin gnulib module byteswap ifeq (,$(OMIT_GNULIB_MODULE_byteswap)) diff --git a/lib/readutmp.h b/lib/readutmp.h new file mode 100644 index 00000000000..1cf588d265b --- /dev/null +++ b/lib/readutmp.h @@ -0,0 +1,325 @@ +/* Declarations for GNU's read utmp module. + + Copyright (C) 1992-2007, 2009-2023 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by jla; revised by djm */ + +#ifndef __READUTMP_H__ +#define __READUTMP_H__ + +/* This file uses _GL_ATTRIBUTE_MALLOC, _GL_ATTRIBUTE_RETURNS_NONNULL, + HAVE_UTMP_H, HAVE_UTMPX_H, HAVE_STRUCT_UTMP_*, HAVE_STRUCT_UTMPX_*, + HAVE_UTMPNAME, HAVE_UTMPXNAME. */ +#if !_GL_CONFIG_H_INCLUDED +# error "Please include config.h first." +#endif + +#include "idx.h" + +#include +#include +#include + +/* AIX 4.3.3 has both utmp.h and utmpx.h, but only struct utmp + has the ut_exit member. */ +#if (HAVE_UTMPX_H && HAVE_UTMP_H && HAVE_STRUCT_UTMP_UT_EXIT \ + && ! HAVE_STRUCT_UTMPX_UT_EXIT) +# undef HAVE_UTMPX_H +#endif + +/* HPUX 10.20 needs utmp.h, for the definition of e.g., UTMP_FILE. */ +#if HAVE_UTMP_H +# include +#endif + +/* Needed for BOOT_TIME and USER_PROCESS. */ +#if HAVE_UTMPX_H +# if defined _THREAD_SAFE && defined UTMP_DATA_INIT + /* When including both utmp.h and utmpx.h on AIX 4.3, with _THREAD_SAFE + defined, work around the duplicate struct utmp_data declaration. */ +# define utmp_data gl_aix_4_3_workaround_utmp_data +# endif +# include +#endif + + +#ifdef __cplusplus +extern "C" { +#endif + + +/* Type of entries returned by read_utmp on all platforms. */ +struct gl_utmp +{ + /* All 'char *' here are of arbitrary length and point to storage + with lifetime equal to that of this struct. */ + char *ut_user; /* User name */ + char *ut_id; /* Session ID */ + char *ut_line; /* seat / device */ + char *ut_host; /* for remote sessions: user@host or host, + for local sessions: the X11 display :N */ + struct timespec ut_ts; /* time */ + pid_t ut_pid; /* process ID of ? */ + pid_t ut_session; /* process ID of session leader */ + short ut_type; /* BOOT_TIME, USER_PROCESS, or other */ + struct { int e_termination; int e_exit; } ut_exit; +}; + +/* The following types, macros, and constants describe the 'struct gl_utmp'. */ +#define UT_USER(UT) ((UT)->ut_user) +#define UT_TIME_MEMBER(UT) ((UT)->ut_ts.tv_sec) +#define UT_PID(UT) ((UT)->ut_pid) +#define UT_TYPE_EQ(UT, V) ((UT)->ut_type == (V)) +#define UT_TYPE_NOT_DEFINED 0 +#define UT_EXIT_E_TERMINATION(UT) ((UT)->ut_exit.e_termination) +#define UT_EXIT_E_EXIT(UT) ((UT)->ut_exit.e_exit) + +/* Type of entry returned by read_utmp(). */ +typedef struct gl_utmp STRUCT_UTMP; + +/* Size of the UT_USER (ut) member, or -1 if unbounded. */ +enum { UT_USER_SIZE = -1 }; + +/* Size of the ut->ut_id member, or -1 if unbounded. */ +enum { UT_ID_SIZE = -1 }; + +/* Size of the ut->ut_line member, or -1 if unbounded. */ +enum { UT_LINE_SIZE = -1 }; + +/* Size of the ut->ut_host member, or -1 if unbounded. */ +enum { UT_HOST_SIZE = -1 }; + + +/* When read_utmp accesses a file (as opposed to fetching the information + from systemd), it uses the following low-level types and macros. + Keep them here, rather than moving them into readutmp.c, for backward + compatibility. */ + +#if HAVE_UTMPX_H + +/* defines 'struct utmpx' with the following fields: + + Field Type Platforms + ---------- ------ --------- + ⎡ ut_user char[] glibc, musl, macOS, FreeBSD, AIX, HP-UX, IRIX, Solaris, Cygwin + ⎣ ut_name char[] NetBSD, Minix + ut_id char[] glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_line char[] glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_pid pid_t glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_type short glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ⎡ ut_tv struct glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ⎢ { tv_sec; tv_usec; } + ⎣ ut_time time_t Cygwin + ut_host char[] glibc, musl, macOS, FreeBSD, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_exit struct glibc, musl, NetBSD, Minix, HP-UX, IRIX, Solaris + { e_termination; e_exit; } + ut_session [long] int glibc, musl, NetBSD, Minix, IRIX, Solaris + ⎡ ut_addr [long] int HP-UX, Cygwin + ⎢ ut_addr_v6 [u]int[4] glibc, musl + ⎣ ut_ss struct sockaddr_storage NetBSD, Minix + */ + +# if __GLIBC__ && _TIME_BITS == 64 +/* This is a near-copy of glibc's struct utmpx, which stops working + after the year 2038. Unlike the glibc version, struct utmpx32 + describes the file format even if time_t is 64 bits. */ +struct utmpx32 +{ + short int ut_type; /* Type of login. */ + pid_t ut_pid; /* Process ID of login process. */ + char ut_line[__UT_LINESIZE]; /* Devicename. */ + char ut_id[4]; /* Inittab ID. */ + char ut_user[__UT_USERSIZE]; /* Username. */ + char ut_host[__UT_HOSTSIZE]; /* Hostname for remote login. */ + struct __exit_status ut_exit; /* Exit status of a process marked + as DEAD_PROCESS. */ + /* The fields ut_session and ut_tv must be the same size when compiled + 32- and 64-bit. This allows files and shared memory to be shared + between 32- and 64-bit applications. */ + int ut_session; /* Session ID, used for windowing. */ + struct + { + /* Seconds. Unsigned not signed, as glibc did not exist before 1970, + and if the format is still in use after 2038 its timestamps + will surely have the sign bit on. This hack stops working + at 2106-02-07 06:28:16 UTC. */ + unsigned int tv_sec; + int tv_usec; /* Microseconds. */ + } ut_tv; /* Time entry was made. */ + int ut_addr_v6[4]; /* Internet address of remote host. */ + char ut_reserved[20]; /* Reserved for future use. */ +}; +# define UTMP_STRUCT_NAME utmpx32 +# else +# define UTMP_STRUCT_NAME utmpx +# endif +# define SET_UTMP_ENT setutxent +# define GET_UTMP_ENT getutxent +# define END_UTMP_ENT endutxent +# ifdef HAVE_UTMPXNAME /* glibc, musl, macOS, NetBSD, Minix, IRIX, Solaris, Cygwin */ +# define UTMP_NAME_FUNCTION utmpxname +# elif defined UTXDB_ACTIVE /* FreeBSD */ +# define UTMP_NAME_FUNCTION(x) setutxdb (UTXDB_ACTIVE, x) +# endif + +#elif HAVE_UTMP_H + +/* defines 'struct utmp' with the following fields: + + Field Type Platforms + ---------- ------ --------- + ⎡ ut_user char[] glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ⎣ ut_name char[] macOS, old FreeBSD, NetBSD, OpenBSD, Minix + ut_id char[] glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ut_line char[] glibc, musl, macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ut_pid pid_t glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ut_type short glibc, musl, AIX, HP-UX, IRIX, Solaris, Cygwin, Android + ⎡ ut_tv struct glibc, musl, Android + ⎢ { tv_sec; tv_usec; } + ⎣ ut_time time_t macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin + ut_host char[] glibc, musl, macOS, old FreeBSD, NetBSD, OpenBSD, Minix, AIX, HP-UX, Cygwin, Android + ut_exit struct glibc, musl, AIX, HP-UX, IRIX, Solaris, Android + { e_termination; e_exit; } + ut_session [long] int glibc, musl, Android + ⎡ ut_addr [long] int HP-UX, Cygwin + ⎣ ut_addr_v6 [u]int[4] glibc, musl, Android + */ + +# define UTMP_STRUCT_NAME utmp +# define SET_UTMP_ENT setutent +# define GET_UTMP_ENT getutent +# define END_UTMP_ENT endutent +# ifdef HAVE_UTMPNAME /* glibc, musl, NetBSD, Minix, AIX, HP-UX, IRIX, Solaris, Cygwin, Android */ +# define UTMP_NAME_FUNCTION utmpname +# endif + +#endif + +/* Evaluates to 1 if gl_utmp's ut_id field may ever have a non-zero value. */ +#define HAVE_STRUCT_XTMP_UT_ID \ + (READUTMP_USE_SYSTEMD \ + || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_ID : HAVE_STRUCT_UTMP_UT_ID)) + +/* Evaluates to 1 if gl_utmp's ut_pid field may ever have a non-zero value. */ +#define HAVE_STRUCT_XTMP_UT_PID \ + (READUTMP_USE_SYSTEMD \ + || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_PID : HAVE_STRUCT_UTMP_UT_PID)) + +/* Evaluates to 1 if gl_utmp's ut_host field may ever be non-empty. */ +#define HAVE_STRUCT_XTMP_UT_HOST \ + (READUTMP_USE_SYSTEMD \ + || (HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_HOST : HAVE_STRUCT_UTMP_UT_HOST)) + +/* Definition of UTMP_FILE. + On glibc systems, UTMP_FILE is "/var/run/utmp". */ +#if !defined UTMP_FILE && defined _PATH_UTMP +# define UTMP_FILE _PATH_UTMP +#endif +#ifdef UTMPX_FILE /* Solaris, SysVr4 */ +# undef UTMP_FILE +# define UTMP_FILE UTMPX_FILE +#endif +#ifndef UTMP_FILE +# define UTMP_FILE "/etc/utmp" +#endif + +/* Definition of WTMP_FILE. + On glibc systems, UTMP_FILE is "/var/log/wtmp". */ +#if !defined WTMP_FILE && defined _PATH_WTMP +# define WTMP_FILE _PATH_WTMP +#endif +#ifdef WTMPX_FILE /* Solaris, SysVr4 */ +# undef WTMP_FILE +# define WTMP_FILE WTMPX_FILE +#endif +#ifndef WTMP_FILE +# define WTMP_FILE "/etc/wtmp" +#endif + +/* Some platforms, such as OpenBSD, don't have an ut_type field and don't have + the BOOT_TIME and USER_PROCESS macros. But we want to support them in + 'struct gl_utmp'. */ +#if !(HAVE_UTMPX_H ? HAVE_STRUCT_UTMPX_UT_TYPE : HAVE_STRUCT_UTMP_UT_TYPE) +# define BOOT_TIME 2 +# define USER_PROCESS 0 +#endif + +/* Macros that test (UT)->ut_type. */ +#ifdef BOOT_TIME +# define UT_TYPE_BOOT_TIME(UT) UT_TYPE_EQ (UT, BOOT_TIME) +#else +# define UT_TYPE_BOOT_TIME(UT) 0 +#endif +#ifdef USER_PROCESS +# define UT_TYPE_USER_PROCESS(UT) UT_TYPE_EQ (UT, USER_PROCESS) +#else +# define UT_TYPE_USER_PROCESS(UT) 0 +#endif + +/* Determines whether an entry *UT corresponds to a user process. */ +#define IS_USER_PROCESS(UT) \ + (UT_USER (UT)[0] \ + && (UT_TYPE_USER_PROCESS (UT) \ + || (UT_TYPE_NOT_DEFINED && UT_TIME_MEMBER (UT) != 0))) + +/* Define if read_utmp is not just a dummy. */ +#if READUTMP_USE_SYSTEMD || HAVE_UTMPX_H || HAVE_UTMP_H || defined __CYGWIN__ || defined _WIN32 +# define READ_UTMP_SUPPORTED 1 +#endif + +/* Options for read_utmp. */ +enum + { + READ_UTMP_CHECK_PIDS = 1, + READ_UTMP_USER_PROCESS = 2, + READ_UTMP_BOOT_TIME = 4, + READ_UTMP_NO_BOOT_TIME = 8 + }; + +/* Return a copy of (UT)->ut_user, without trailing spaces, + as a freshly allocated string. */ +char *extract_trimmed_name (const STRUCT_UTMP *ut) + _GL_ATTRIBUTE_MALLOC _GL_ATTRIBUTE_DEALLOC_FREE + _GL_ATTRIBUTE_RETURNS_NONNULL; + +/* Read the utmp entries corresponding to file FILE into freshly- + malloc'd storage, set *UTMP_BUF to that pointer, set *N_ENTRIES to + the number of entries, and return zero. If there is any error, + return -1, setting errno, and don't modify the parameters. + A good candidate for FILE is UTMP_FILE. + If OPTIONS & READ_UTMP_CHECK_PIDS is nonzero, omit entries whose + process-IDs do not currently exist. + If OPTIONS & READ_UTMP_USER_PROCESS is nonzero, omit entries which + do not correspond to a user process. + If OPTIONS & READ_UTMP_BOOT_TIME is nonzero, omit all entries except + the one that contains the boot time. + If OPTIONS & READ_UTMP_NO_BOOT_TIME is nonzero, omit the boot time + entries. + + This function is not multithread-safe, since on many platforms it + invokes the functions setutxent, getutxent, endutxent. These + functions are needed because they may lock FILE (so that we don't + read garbage when a concurrent process writes to FILE), but their + drawback is that they have a common global state. */ +int read_utmp (char const *file, idx_t *n_entries, STRUCT_UTMP **utmp_buf, + int options); + + +#ifdef __cplusplus +} +#endif + +#endif /* __READUTMP_H__ */ diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 3382e9bc241..14ff92040a4 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -51,6 +51,7 @@ AC_DEFUN # Code from module at-internal: # Code from module attribute: # Code from module binary-io: + # Code from module boot-time: # Code from module builtin-expect: # Code from module byteswap: # Code from module c-ctype: @@ -243,6 +244,7 @@ AC_DEFUN gl_ASSERT_H gl_CONDITIONAL_HEADER([assert.h]) AC_PROG_MKDIR_P + gl_PREREQ_READUTMP_H gl___BUILTIN_EXPECT gl_BYTESWAP gl_CONDITIONAL_HEADER([byteswap.h]) @@ -1252,6 +1254,9 @@ AC_DEFUN lib/attribute.h lib/binary-io.c lib/binary-io.h + lib/boot-time-aux.h + lib/boot-time.c + lib/boot-time.h lib/byteswap.in.h lib/c++defs.h lib/c-ctype.c @@ -1383,6 +1388,7 @@ AC_DEFUN lib/rawmemchr.valgrind lib/readlink.c lib/readlinkat.c + lib/readutmp.h lib/realloc.c lib/regcomp.c lib/regex.c @@ -1542,6 +1548,7 @@ AC_DEFUN m4/rawmemchr.m4 m4/readlink.m4 m4/readlinkat.m4 + m4/readutmp.m4 m4/realloc.m4 m4/regex.m4 m4/sha1.m4 diff --git a/m4/readutmp.m4 b/m4/readutmp.m4 new file mode 100644 index 00000000000..fff8d4eb7bf --- /dev/null +++ b/m4/readutmp.m4 @@ -0,0 +1,117 @@ +# readutmp.m4 serial 28 +dnl Copyright (C) 2002-2023 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_READUTMP], +[ + AC_REQUIRE([gl_SYSTEMD_CHOICE]) + + dnl Set READUTMP_LIB to '-lsystemd' or '', depending on whether use of + dnl systemd APIs is possible and desired (only the systemd login API, here). + dnl AC_LIB_LINKFLAGS_BODY would be overkill here, since few people install + dnl libsystemd in non-system directories. + READUTMP_LIB= + if test "$SYSTEMD_CHOICE" = yes; then + AC_CHECK_HEADER([systemd/sd-login.h]) + if test $ac_cv_header_systemd_sd_login_h = yes; then + AC_CACHE_CHECK([for libsystemd version >= 254], + [gl_cv_lib_readutmp_systemd], + [gl_save_LIBS="$LIBS" + LIBS="$LIBS -lsystemd" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include + #include + ]], [[ + uint64_t st; + sd_session_get_start_time ("1", &st); + ]]) + ], + [gl_cv_lib_readutmp_systemd=yes], + [gl_cv_lib_readutmp_systemd=no]) + LIBS="$gl_save_LIBS" + ]) + if test $gl_cv_lib_readutmp_systemd = yes; then + AC_DEFINE([READUTMP_USE_SYSTEMD], [1], + [Define if the readutmp module should use the systemd login API.]) + READUTMP_LIB='-lsystemd' + fi + fi + fi + AC_SUBST([READUTMP_LIB]) + + gl_PREREQ_READUTMP_H +]) + +# Prerequisites of readutmp.h and boot-time-aux.h. +AC_DEFUN_ONCE([gl_PREREQ_READUTMP_H], +[ + dnl Persuade utmpx.h to declare utmpxname + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_CHECK_HEADERS_ONCE([utmp.h utmpx.h]) + if test $ac_cv_header_utmp_h = yes || test $ac_cv_header_utmpx_h = yes; then + dnl Prerequisites of lib/readutmp.h and lib/readutmp.c. + AC_CHECK_FUNCS_ONCE([utmpname utmpxname]) + AC_CHECK_DECLS([getutent],,,[[ +/* is a prerequisite of on FreeBSD 8.0, OpenBSD 4.6. */ +#include +#ifdef HAVE_UTMP_H +# include +#endif +]]) + utmp_includes="\ +AC_INCLUDES_DEFAULT +#ifdef HAVE_UTMPX_H +# include +#endif +#ifdef HAVE_UTMP_H +# if defined _THREAD_SAFE && defined UTMP_DATA_INIT + /* When including both utmp.h and utmpx.h on AIX 4.3, with _THREAD_SAFE + defined, work around the duplicate struct utmp_data declaration. */ +# define utmp_data gl_aix_4_3_workaround_utmp_data +# endif +# include +#endif +" + AC_CHECK_MEMBERS([struct utmpx.ut_user],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_user],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_name],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_name],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_type],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_type],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_pid],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_pid],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_tv],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_host],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_host],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_id],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_id],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_session],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_session],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_exit],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_exit],,,[$utmp_includes]) + + AC_CHECK_MEMBERS([struct utmpx.ut_exit.ut_exit],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_exit.e_exit],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_exit.e_exit],,,[$utmp_includes]) + + AC_CHECK_MEMBERS([struct utmpx.ut_exit.ut_termination],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmpx.ut_exit.e_termination],,,[$utmp_includes]) + AC_CHECK_MEMBERS([struct utmp.ut_exit.e_termination],,,[$utmp_includes]) + fi + + AC_CHECK_HEADERS_ONCE([sys/param.h]) + dnl requires on OpenBSD 4.0. + AC_CHECK_HEADERS([sys/sysctl.h],,, + [AC_INCLUDES_DEFAULT + #if HAVE_SYS_PARAM_H + # include + #endif + ]) + AC_CHECK_FUNCS([sysctl]) + + AC_CHECK_HEADERS_ONCE([OS.h]) +]) diff --git a/src/filelock.c b/src/filelock.c index 3b1ff8ad566..d2161f1e58a 100644 --- a/src/filelock.c +++ b/src/filelock.c @@ -36,13 +36,9 @@ Copyright (C) 1985-1987, 1993-1994, 1996, 1998-2023 Free Software #include #include #include - -#ifdef __FreeBSD__ -#include -#endif /* __FreeBSD__ */ - #include +#include #include #include "lisp.h" @@ -55,20 +51,6 @@ Copyright (C) 1985-1987, 1993-1994, 1996, 1998-2023 Free Software #ifndef MSDOS -#ifdef HAVE_UTMP_H -#include -#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 - #ifdef HAVE_ANDROID #include "android.h" /* For `android_is_special_directory'. */ #endif /* HAVE_ANDROID */ @@ -127,153 +109,19 @@ #define WTMP_FILE "/var/log/wtmp" /* Return the time of the last system boot. */ -static time_t boot_time; -static bool boot_time_initialized; - -#ifdef BOOT_TIME -static void get_boot_time_1 (const char *, bool); -#endif - static time_t -get_boot_time (void) +get_boot_sec (void) { - if (boot_time_initialized) - return boot_time; - boot_time_initialized = 1; - -#if defined (CTL_KERN) && defined (KERN_BOOTTIME) - { - int mib[2]; - size_t size; - struct timeval boottime_val; - - mib[0] = CTL_KERN; - mib[1] = KERN_BOOTTIME; - size = sizeof (boottime_val); - - if (sysctl (mib, 2, &boottime_val, &size, NULL, 0) >= 0 && size != 0) - { - boot_time = boottime_val.tv_sec; - return boot_time; - } - } -#endif /* defined (CTL_KERN) && defined (KERN_BOOTTIME) */ - -#ifdef BOOT_TIME_FILE - { - struct stat st; - if (stat (BOOT_TIME_FILE, &st) == 0) - { - boot_time = st.st_mtime; - return boot_time; - } - } -#endif /* BOOT_TIME_FILE */ - -#if defined (BOOT_TIME) - /* The utmp routines maintain static state. Don't touch that state + /* get_boot_time maintains static state. Don't touch that state if we are going to dump, since it might not survive dumping. */ if (will_dump_p ()) - return boot_time; - - /* Try to get boot time from utmp before wtmp, - since utmp is typically much smaller than wtmp. - Passing a null pointer causes get_boot_time_1 - to inspect the default file, namely utmp. */ - get_boot_time_1 (0, 0); - if (boot_time) - return boot_time; - - /* Try to get boot time from the current wtmp file. */ - get_boot_time_1 (WTMP_FILE, 1); - - /* If we did not find a boot time in wtmp, look at wtmp.1, - wtmp.1.gz, wtmp.2, wtmp.2.gz, and so on. */ - for (int counter = 0; counter < 20 && ! boot_time; counter++) - { - Lisp_Object filename = Qnil; - bool delete_flag = false; - char cmd_string[sizeof WTMP_FILE ".19.gz"]; - AUTO_STRING_WITH_LEN (tempname, cmd_string, - sprintf (cmd_string, "%s.%d", WTMP_FILE, counter)); - if (! NILP (Ffile_exists_p (tempname))) - filename = tempname; - else - { - tempname = make_formatted_string (cmd_string, "%s.%d.gz", - WTMP_FILE, counter); - if (! NILP (Ffile_exists_p (tempname))) - { - /* The utmp functions on older systems accept only file - names up to 8 bytes long. Choose a 2 byte prefix, so - the 6-byte suffix does not make the name too long. */ - filename = Fmake_temp_file_internal (build_string ("wt"), Qnil, - empty_unibyte_string, Qnil); - CALLN (Fcall_process, build_string ("gzip"), Qnil, - list2 (QCfile, filename), Qnil, - build_string ("-cd"), tempname); - delete_flag = true; - } - } - - if (! NILP (filename)) - { - get_boot_time_1 (SSDATA (filename), 1); - if (delete_flag) - emacs_unlink (SSDATA (filename)); - } - } - - return boot_time; -#else - return 0; -#endif -} + return 0; -#ifdef BOOT_TIME -/* Try to get the boot time from wtmp file FILENAME. - This succeeds if that file contains a reboot record. - - If FILENAME is zero, use the same file as before; - if no FILENAME has ever been specified, this is the utmp file. - Use the newest reboot record if NEWEST, - the first reboot record otherwise. - Ignore all reboot records on or before BOOT_TIME. - Success is indicated by setting BOOT_TIME to a larger value. */ - -void -get_boot_time_1 (const char *filename, bool newest) -{ - struct utmp ut, *utp; - - if (filename) - utmpname (filename); - - setutent (); - - while (1) - { - /* Find the next reboot record. */ - ut.ut_type = BOOT_TIME; - utp = getutid (&ut); - if (! utp) - break; - /* Compare reboot times and use the newest one. */ - if (utp->ut_time > boot_time) - { - boot_time = utp->ut_time; - if (! newest) - break; - } - /* Advance on element in the file - so that getutid won't repeat the same one. */ - utp = getutent (); - if (! utp) - break; - } - endutent (); + struct timespec boot_time; + boot_time.tv_sec = 0; + get_boot_time (&boot_time); + return boot_time.tv_sec; } -#endif /* BOOT_TIME */ /* An arbitrary limit on lock contents length. 8 K should be plenty big enough in practice. */ @@ -418,7 +266,7 @@ create_lock_file (char *lfname, char *lock_info_str, bool force) static int lock_file_1 (Lisp_Object lfname, bool force) { - intmax_t boot = get_boot_time (); + intmax_t boot = get_boot_sec (); Lisp_Object luser_name = Fuser_login_name (Qnil); Lisp_Object lhost_name = Fsystem_name (); @@ -604,7 +452,7 @@ current_lock_owner (lock_info_type *owner, Lisp_Object lfname) && (kill (pid, 0) >= 0 || errno == EPERM) && (boot_time == 0 || (boot_time <= TYPE_MAXIMUM (time_t) - && within_one_second (boot_time, get_boot_time ())))) + && within_one_second (boot_time, get_boot_sec ())))) return ANOTHER_OWNS_IT; /* The owner process is dead or has a strange pid, so try to zap the lockfile. */ commit b35431b218ada2d84eb251d18e5543388b598d80 Author: Po Lu Date: Sun Aug 13 09:45:36 2023 +0800 Fix bootstrap-clean in the cross directory on USG * cross/Makefile.in (FIND_DELETE): New variable. (CLEAN_SUBDIRS): Avoid including src if not already present. (clean, bootstrap-clean, etc): Use FIND_DELETE. diff --git a/cross/Makefile.in b/cross/Makefile.in index a7b5700440e..b66025283aa 100644 --- a/cross/Makefile.in +++ b/cross/Makefile.in @@ -22,6 +22,8 @@ srcdir = top_builddir = @top_builddir@ builddir = @builddir@ +FIND_DELETE = @FIND_DELETE@ + -include $(top_builddir)/src/verbose.mk # Cross-compiling Emacs for Android. @@ -51,7 +53,7 @@ LIB_SRC_TOP_SRCDIR = LIBSRC_BINARIES = lib-src/etags lib-src/ctags lib-src/emacsclient \ lib-src/ebrowse lib-src/hexl lib-src/movemail -CLEAN_SUBDIRS = src lib-src lib etc +CLEAN_SUBDIRS = $(wildcard src lib-src lib etc) .PHONY: all all: lib/libgnu.a src/libemacs.so src/android-emacs $(LIBSRC_BINARIES) @@ -174,17 +176,19 @@ $(LIBSRC_BINARIES) &: .PHONY: clean maintainer-clean distclean clean: - for dir in $(CLEAN_SUBDIRS); do \ - find $$dir -type f -delete; \ + for dir in $(CLEAN_SUBDIRS); do \ + find $$dir -type f $(FIND_DELETE); \ done rm -rf lib/config.h lib-src/config.h # ndk-build won't have been generated in a non-Android build. - -make -C ndk-build clean + if test -f ndk-build/Makefile; then \ + $(MAKE) -C ndk-build clean; \ + fi maintainer-clean distclean bootstrap-clean: clean # Remove links created by configure. - for dir in $(CLEAN_SUBDIRS); do \ - find $$dir -type l -delete; \ + for dir in $(CLEAN_SUBDIRS); do \ + find $$dir -type l $(FIND_DELETE); \ done rm -rf lib/Makefile lib/gnulib.mk ndk-build/Makefile rm -rf ndk-build/ndk-build.mk Makefile commit d21e84d1e72988bd55d43ea991dd365b7bd713cf Author: Po Lu Date: Sun Aug 13 09:10:07 2023 +0800 Employ careadlinkat in getProcName * src/android.c (android_proc_name): Delete args BUFFER and SIZE. Return buffer allocated by careadlinkat. (NATIVE_NAME): Adjust correspondingly. diff --git a/src/android.c b/src/android.c index b5b4359dcd3..70779f8ccae 100644 --- a/src/android.c +++ b/src/android.c @@ -18,31 +18,28 @@ Copyright (C) 2023 Free Software Foundation, Inc. along with GNU Emacs. If not, see . */ #include +#include +#include +#include +#include #include -#include -#include +#include +#include +#include #include -#include -#include -#include -#include #include -#include +#include +#include +#include #include -#include -#include -#include - -#include -#include +#include #include +#include +#include /* Old NDK versions lack MIN and MAX. */ #include -#include -#include - #include "android.h" #include "androidgui.h" @@ -1157,24 +1154,23 @@ android_get_home_directory (void) } /* Return the name of the file behind a file descriptor FD by reading - /proc/self/fd/. Place the name in BUFFER, which should be able to - hold size bytes. Value is 0 upon success, and 1 upon failure. */ + /proc/self/fd/. Value is allocated memory holding the file name + upon success, and 0 upon failure. */ -static int -android_proc_name (int fd, char *buffer, size_t size) +static char * +android_proc_name (int fd) { char format[sizeof "/proc/self/fd/" + INT_STRLEN_BOUND (int)]; - ssize_t read; + static struct allocator allocator = { + /* Fill the allocator with C library malloc functions. xmalloc + and so aren't thread safe. */ + malloc, realloc, free, NULL, + }; sprintf (format, "/proc/self/fd/%d", fd); - read = readlink (format, buffer, size - 1); - - if (read == -1) - return 1; - - buffer[read] = '\0'; - return 0; + return careadlinkat (AT_FDCWD, format, NULL, 0, + &allocator, readlinkat); } /* Try to guarantee the existence of the `lib' directory within the @@ -1465,11 +1461,12 @@ NATIVE_NAME (getProcName) (JNIEnv *env, jobject object, jint fd) { JNI_STACK_ALIGNMENT_PROLOGUE; - char buffer[PATH_MAX + 1]; + char *buffer; size_t length; jbyteArray array; - if (android_proc_name (fd, buffer, PATH_MAX + 1)) + buffer = android_proc_name (fd); + if (!buffer) return NULL; /* Return a byte array, as Java strings cannot always encode file @@ -1477,11 +1474,13 @@ NATIVE_NAME (getProcName) (JNIEnv *env, jobject object, jint fd) length = strlen (buffer); array = (*env)->NewByteArray (env, length); if (!array) - return NULL; + goto finish; (*env)->SetByteArrayRegion (env, array, 0, length, (jbyte *) buffer); + finish: + free (buffer); return array; } commit 5315e6e8d7e7233d54cce2b4c1bc8cf3b7acf4dc Author: Paul Eggert Date: Sat Aug 12 12:50:15 2023 -0700 Avoid stpncpy It’s not worth the porting hassle, and as the glibc manual says, “this function is generally a poor choice for processing strings”. * admin/merge-gnulib (GNULIB_MODULES): Remove stpncpy. * exec/configure.ac: Do not check for stpncpy. * exec/exec.c (rpl_stpncpy, stpncpy): Remove this replacement. (exec_0): Properly clear buffer1. Use memcpy instead of stpncpy to add the trailing name. This code is clearly still suboptimal but efficiency is not that important here and I tried to minimize the change. * lib/gnulib.mk.in, m4/gnulib-comp.m4: Regenerate. diff --git a/admin/merge-gnulib b/admin/merge-gnulib index f64c0670bcf..2a713beb01a 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -45,7 +45,7 @@ GNULIB_MODULES= pathmax pipe2 pselect pthread_sigmask qcopy-acl readlink readlinkat regex sig2str sigdescr_np socklen stat-time std-gnu11 stdbool stdckdint stddef stdio - stpcpy stpncpy strnlen strnlen strtoimax symlink sys_stat sys_time + stpcpy strnlen strnlen strtoimax symlink sys_stat sys_time tempname time-h time_r time_rz timegm timer-time timespec-add timespec-sub update-copyright unlocked-io utimensat vla warnings year2038 diff --git a/exec/configure.ac b/exec/configure.ac index e78d8ebea90..180c200d13d 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -62,8 +62,8 @@ AC_TYPE_PID_T AC_HEADER_STDBOOL -AC_CHECK_FUNCS([getpagesize stpcpy stpncpy]) -AC_CHECK_DECLS([stpcpy, stpncpy]) +AC_CHECK_FUNCS([getpagesize stpcpy]) +AC_CHECK_DECLS([stpcpy]) AC_CHECK_FUNC([process_vm_readv], [AC_CHECK_FUNC([process_vm_writev], [AC_CHECK_DECL([process_vm_readv], diff --git a/exec/exec.c b/exec/exec.c index 935c94a59af..dae05755675 100644 --- a/exec/exec.c +++ b/exec/exec.c @@ -66,74 +66,6 @@ rpl_stpcpy (char *dest, const char *src) #define stpcpy rpl_stpcpy #endif /* !defined HAVE_STPCPY || !defined HAVE_DECL_STPCPY */ -#if !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY - -/* Copy no more than N bytes of SRC to DST, returning a pointer past - the last non-NUL byte written into DST. */ - -static char * -rpl_stpncpy (char *dest, const char *src, size_t n) -{ - char c, *s; - size_t n4; - - s = dest; - - if (n >= 4) - { - n4 = n >> 2; - - for (;;) - { - c = *src++; - *dest++ = c; - if (c == '\0') - break; - c = *src++; - *dest++ = c; - if (c == '\0') - break; - c = *src++; - *dest++ = c; - if (c == '\0') - break; - c = *src++; - *dest++ = c; - if (c == '\0') - break; - if (--n4 == 0) - goto last_chars; - } - n -= dest - s; - goto zero_fill; - } - - last_chars: - n &= 3; - if (n == 0) - return dest; - - for (;;) - { - c = *src++; - --n; - *dest++ = c; - if (c == '\0') - break; - if (n == 0) - return dest; - } - - zero_fill: - while (n-- > 0) - dest[n] = '\0'; - - return dest - 1; -} - -#define stpncpy rpl_stpncpy -#endif /* !defined HAVE_STPNCPY || !defined HAVE_DECL_STPNCPY */ - /* Executable reading functions. @@ -1005,13 +937,14 @@ exec_0 (char *name, struct exec_tracee *tracee, else { /* If name is not absolute, then make it relative to TRACEE's - cwd. Use stpcpy, as sprintf is not reentrant. */ + cwd. Do not use sprintf at it is not reentrant and it + mishandles results longer than INT_MAX. */ if (name[0] && name[0] != '/') { - /* Clear `buffer'. */ + /* Clear both buffers. */ memset (buffer, 0, sizeof buffer); - memset (buffer1, 0, sizeof buffer); + memset (buffer1, 0, sizeof buffer1); /* Copy over /proc, the PID, and /cwd/. */ rewrite = stpcpy (buffer, "/proc/"); @@ -1042,7 +975,7 @@ exec_0 (char *name, struct exec_tracee *tracee, rewrite = buffer1 + link_size; remaining = buffer1 + sizeof buffer1 - rewrite - 1; - rewrite = stpncpy (rewrite, name, remaining); + memcpy (rewrite, name, strnlen (name, remaining)); /* Replace name with buffer1. */ #ifndef REENTRANT diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 78ecb544c6e..785bdc70c5c 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -157,7 +157,6 @@ # stddef \ # stdio \ # stpcpy \ -# stpncpy \ # strnlen \ # strtoimax \ # symlink \ @@ -336,7 +335,6 @@ GL_COND_OBJ_SIGDESCR_NP_CONDITION = @GL_COND_OBJ_SIGDESCR_NP_CONDITION@ GL_COND_OBJ_STDIO_READ_CONDITION = @GL_COND_OBJ_STDIO_READ_CONDITION@ GL_COND_OBJ_STDIO_WRITE_CONDITION = @GL_COND_OBJ_STDIO_WRITE_CONDITION@ GL_COND_OBJ_STPCPY_CONDITION = @GL_COND_OBJ_STPCPY_CONDITION@ -GL_COND_OBJ_STPNCPY_CONDITION = @GL_COND_OBJ_STPNCPY_CONDITION@ GL_COND_OBJ_STRNLEN_CONDITION = @GL_COND_OBJ_STRNLEN_CONDITION@ GL_COND_OBJ_STRTOIMAX_CONDITION = @GL_COND_OBJ_STRTOIMAX_CONDITION@ GL_COND_OBJ_STRTOLL_CONDITION = @GL_COND_OBJ_STRTOLL_CONDITION@ @@ -3454,16 +3452,6 @@ endif endif ## end gnulib module stpcpy -## begin gnulib module stpncpy -ifeq (,$(OMIT_GNULIB_MODULE_stpncpy)) - -ifneq (,$(GL_COND_OBJ_STPNCPY_CONDITION)) -libgnu_a_SOURCES += stpncpy.c -endif - -endif -## end gnulib module stpncpy - ## begin gnulib module string ifeq (,$(OMIT_GNULIB_MODULE_string)) diff --git a/lib/stpncpy.c b/lib/stpncpy.c deleted file mode 100644 index d1422a927df..00000000000 --- a/lib/stpncpy.c +++ /dev/null @@ -1,92 +0,0 @@ -/* Copyright (C) 1993, 1995-1997, 2002-2003, 2005-2007, 2009-2023 Free Software - * Foundation, Inc. - - NOTE: The canonical source of this file is maintained with the GNU C Library. - Bugs can be reported to bug-glibc@gnu.org. - - This file is free software: you can redistribute it and/or modify - it under the terms of the GNU Lesser General Public License as - published by the Free Software Foundation; either version 2.1 of the - License, or (at your option) any later version. - - This file is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public License - along with this program. If not, see . */ - -/* This is almost copied from strncpy.c, written by Torbjorn Granlund. */ - -#include - -/* Specification. */ -#include - -#ifndef weak_alias -# define __stpncpy stpncpy -#endif - -/* Copy no more than N bytes of SRC to DST, returning a pointer past the - last non-NUL byte written into DST. */ -char * -(__stpncpy) (char *dest, const char *src, size_t n) -{ - char c; - char *s = dest; - - if (n >= 4) - { - size_t n4 = n >> 2; - - for (;;) - { - c = *src++; - *dest++ = c; - if (c == '\0') - break; - c = *src++; - *dest++ = c; - if (c == '\0') - break; - c = *src++; - *dest++ = c; - if (c == '\0') - break; - c = *src++; - *dest++ = c; - if (c == '\0') - break; - if (--n4 == 0) - goto last_chars; - } - n -= dest - s; - goto zero_fill; - } - - last_chars: - n &= 3; - if (n == 0) - return dest; - - for (;;) - { - c = *src++; - --n; - *dest++ = c; - if (c == '\0') - break; - if (n == 0) - return dest; - } - - zero_fill: - while (n-- > 0) - dest[n] = '\0'; - - return dest - 1; -} -#ifdef weak_alias -weak_alias (__stpncpy, stpncpy) -#endif diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 72adcf90d5e..3382e9bc241 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -181,7 +181,6 @@ AC_DEFUN gl_STDIO_H_EARLY # Code from module stdlib: # Code from module stpcpy: - # Code from module stpncpy: # Code from module string: # Code from module strnlen: # Code from module strtoimax: @@ -567,13 +566,6 @@ AC_DEFUN gl_PREREQ_STPCPY ]) gl_STRING_MODULE_INDICATOR([stpcpy]) - gl_FUNC_STPNCPY - gl_CONDITIONAL([GL_COND_OBJ_STPNCPY], - [test $HAVE_STPNCPY = 0 || test $REPLACE_STPNCPY = 1]) - AM_COND_IF([GL_COND_OBJ_STPNCPY], [ - gl_PREREQ_STPNCPY - ]) - gl_STRING_MODULE_INDICATOR([stpncpy]) gl_STRING_H gl_STRING_H_REQUIRE_DEFAULTS AC_PROG_MKDIR_P @@ -1422,7 +1414,6 @@ AC_DEFUN lib/stdio.in.h lib/stdlib.in.h lib/stpcpy.c - lib/stpncpy.c lib/str-two-way.h lib/strftime.h lib/string.in.h @@ -1569,7 +1560,6 @@ AC_DEFUN m4/stdio_h.m4 m4/stdlib_h.m4 m4/stpcpy.m4 - m4/stpncpy.m4 m4/string_h.m4 m4/strnlen.m4 m4/strtoimax.m4 diff --git a/m4/stpncpy.m4 b/m4/stpncpy.m4 deleted file mode 100644 index 073607004be..00000000000 --- a/m4/stpncpy.m4 +++ /dev/null @@ -1,108 +0,0 @@ -# stpncpy.m4 serial 22 -dnl Copyright (C) 2002-2003, 2005-2007, 2009-2023 Free Software Foundation, -dnl Inc. -dnl This file is free software; the Free Software Foundation -dnl gives unlimited permission to copy and/or distribute it, -dnl with or without modifications, as long as this notice is preserved. - -AC_DEFUN([gl_FUNC_STPNCPY], -[ - AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles - - dnl Persuade glibc to declare stpncpy(). - AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) - - dnl The stpncpy() declaration in lib/string.in.h uses 'restrict'. - AC_REQUIRE([AC_C_RESTRICT]) - - AC_REQUIRE([gl_STRING_H_DEFAULTS]) - - dnl Both glibc and AIX (4.3.3, 5.1) have an stpncpy() function - dnl declared in . Its side effects are the same as those - dnl of strncpy(): - dnl stpncpy (dest, src, n) - dnl overwrites dest[0..n-1], min(strlen(src),n) bytes coming from src, - dnl and the remaining bytes being NULs. However, the return value is - dnl in glibc: dest + min(strlen(src),n) - dnl in AIX: dest + max(0,n-1) - dnl Only the glibc return value is useful in practice. - - AC_CHECK_DECLS_ONCE([stpncpy]) - gl_CHECK_FUNCS_ANDROID([stpncpy], [[#include ]]) - if test $ac_cv_func_stpncpy = yes; then - AC_CACHE_CHECK([for working stpncpy], [gl_cv_func_stpncpy], [ - AC_RUN_IFELSE( - [AC_LANG_SOURCE([[ -#include -#include /* for strcpy */ -/* The stpncpy prototype is missing in on AIX 4. */ -#if !HAVE_DECL_STPNCPY -extern -# ifdef __cplusplus -"C" -# endif -char *stpncpy (char *dest, const char *src, size_t n); -#endif -int main () -{ - int result = 0; - const char *src = "Hello"; - char dest[10]; - /* AIX 4.3.3 and AIX 5.1 stpncpy() returns dest+1 here. */ - { - strcpy (dest, "\377\377\377\377\377\377"); - if (stpncpy (dest, src, 2) != dest + 2) - result |= 1; - } - /* AIX 4.3.3 and AIX 5.1 stpncpy() returns dest+4 here. */ - { - strcpy (dest, "\377\377\377\377\377\377"); - if (stpncpy (dest, src, 5) != dest + 5) - result |= 2; - } - /* AIX 4.3.3 and AIX 5.1 stpncpy() returns dest+6 here. */ - { - strcpy (dest, "\377\377\377\377\377\377"); - if (stpncpy (dest, src, 7) != dest + 5) - result |= 4; - } - return result; -} -]])], - [gl_cv_func_stpncpy=yes], - [gl_cv_func_stpncpy=no], - [dnl Guess yes on glibc systems and musl systems. - AC_EGREP_CPP([Thanks for using GNU], [ -#include -#ifdef __GNU_LIBRARY__ - Thanks for using GNU -#endif -], [gl_cv_func_stpncpy="guessing yes"], - [case "$host_os" in - *-musl* | midipix*) gl_cv_func_stpncpy="guessing yes" ;; - *) gl_cv_func_stpncpy="$gl_cross_guess_normal" ;; - esac - ]) - ]) - ]) - case "$gl_cv_func_stpncpy" in - *yes) - AC_DEFINE([HAVE_STPNCPY], [1], - [Define if you have the stpncpy() function and it works.]) - ;; - *) - REPLACE_STPNCPY=1 - ;; - esac - else - HAVE_STPNCPY=0 - case "$gl_cv_onwards_func_stpncpy" in - future*) REPLACE_STPNCPY=1 ;; - esac - fi -]) - -# Prerequisites of lib/stpncpy.c. -AC_DEFUN([gl_PREREQ_STPNCPY], [ - : -]) commit f3868cb9d1806b35186eabc0262393316ebe689a Author: Paul Eggert Date: Sat Aug 12 12:50:15 2023 -0700 Update from Gnulib by running admin/merge-gnulib diff --git a/build-aux/config.guess b/build-aux/config.guess index 354a8ccde42..b187213930f 100755 --- a/build-aux/config.guess +++ b/build-aux/config.guess @@ -4,7 +4,7 @@ # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2023-06-23' +timestamp='2023-07-20' # 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 @@ -976,7 +976,27 @@ main () GUESS=$UNAME_MACHINE-unknown-minix ;; aarch64:Linux:*:*) - GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __ARM_EABI__ + #ifdef __ARM_PCS_VFP + ABI=eabihf + #else + ABI=eabi + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; + esac + fi + GUESS=$CPU-unknown-linux-$LIBCABI ;; aarch64_be:Linux:*:*) UNAME_MACHINE=aarch64_be @@ -1042,6 +1062,15 @@ main () k1om:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; + kvx:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:cos:*:*) + GUESS=$UNAME_MACHINE-unknown-cos + ;; + kvx:mbr:*:*) + GUESS=$UNAME_MACHINE-unknown-mbr + ;; loongarch32:Linux:*:* | loongarch64:Linux:*:*) GUESS=$UNAME_MACHINE-unknown-linux-$LIBC ;; diff --git a/build-aux/config.sub b/build-aux/config.sub index 9865d6ea4d1..6ae25027537 100755 --- a/build-aux/config.sub +++ b/build-aux/config.sub @@ -4,7 +4,7 @@ # shellcheck disable=SC2006,SC2268 # see below for rationale -timestamp='2023-06-26' +timestamp='2023-07-31' # 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 @@ -1206,6 +1206,7 @@ IFS= | i370 | i*86 | i860 | i960 | ia16 | ia64 \ | ip2k | iq2000 \ | k1om \ + | kvx \ | le32 | le64 \ | lm32 \ | loongarch32 | loongarch64 \ @@ -1214,31 +1215,7 @@ IFS= | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \ | m88110 | m88k | maxq | mb | mcore | mep | metag \ | microblaze | microblazeel \ - | mips | mipsbe | mipseb | mipsel | mipsle \ - | mips16 \ - | mips64 | mips64eb | mips64el \ - | mips64octeon | mips64octeonel \ - | mips64orion | mips64orionel \ - | mips64r5900 | mips64r5900el \ - | mips64vr | mips64vrel \ - | mips64vr4100 | mips64vr4100el \ - | mips64vr4300 | mips64vr4300el \ - | mips64vr5000 | mips64vr5000el \ - | mips64vr5900 | mips64vr5900el \ - | mipsisa32 | mipsisa32el \ - | mipsisa32r2 | mipsisa32r2el \ - | mipsisa32r3 | mipsisa32r3el \ - | mipsisa32r5 | mipsisa32r5el \ - | mipsisa32r6 | mipsisa32r6el \ - | mipsisa64 | mipsisa64el \ - | mipsisa64r2 | mipsisa64r2el \ - | mipsisa64r3 | mipsisa64r3el \ - | mipsisa64r5 | mipsisa64r5el \ - | mipsisa64r6 | mipsisa64r6el \ - | mipsisa64sb1 | mipsisa64sb1el \ - | mipsisa64sr71k | mipsisa64sr71kel \ - | mipsr5900 | mipsr5900el \ - | mipstx39 | mipstx39el \ + | mips* \ | mmix \ | mn10200 | mn10300 \ | moxie \ @@ -1733,7 +1710,7 @@ kernel= | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \ | hiux* | abug | nacl* | netware* | windows* \ - | os9* | macos* | osx* | ios* \ + | os9* | macos* | osx* | ios* | tvos* | watchos* \ | mpw* | magic* | mmixware* | mon960* | lnews* \ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \ | aos* | aros* | cloudabi* | sortix* | twizzler* \ @@ -1759,7 +1736,7 @@ kernel= | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx* | zephyr* \ - | fiwix* | mlibc* ) + | fiwix* | mlibc* | cos* | mbr* ) ;; # This one is extra strict with allowed versions sco3.2v2 | sco3.2v[4-9]* | sco5v6*) @@ -1816,6 +1793,10 @@ kernel= ;; *-eabi* | *-gnueabi*) ;; + none-coff* | none-elf*) + # None (no kernel, i.e. freestanding / bare metal), + # can be paired with an output format "OS" + ;; -*) # Blank kernel with real OS is always fine. ;; diff --git a/doc/misc/texinfo.tex b/doc/misc/texinfo.tex index b1d2999e5d7..6e521944b22 100644 --- a/doc/misc/texinfo.tex +++ b/doc/misc/texinfo.tex @@ -3,7 +3,7 @@ % Load plain if necessary, i.e., if running under initex. \expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi % -\def\texinfoversion{2023-07-02.10} +\def\texinfoversion{2023-07-27.21} % % Copyright 1985, 1986, 1988, 1990-2023 Free Software Foundation, Inc. % @@ -426,42 +426,21 @@ } % First remove any @comment, then any @c comment. Pass the result on to -% \argcheckspaces. +% \argremovespace. \def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} -\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} - -% Each occurrence of `\^^M' or `\^^M' is replaced by a single space. -% -% \argremovec might leave us with trailing space, e.g., +\def\argremovec#1\c#2\ArgTerm{\argremovespace#1$ $\ArgTerm} +% \argremovec might leave us with trailing space, though; e.g., % @end itemize @c foo -% This space token undergoes the same procedure and is eventually removed -% by \finishparsearg. -% -\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} -\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} -\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% - \def\temp{#3}% - \ifx\temp\empty - % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: - \let\temp\finishparsearg - \else - \let\temp\argcheckspaces - \fi - % Put the space token in: - \temp#1 #3\ArgTerm -} +% Note that the argument cannot contain the TeX $, as its catcode is +% changed to \other when Texinfo source is read. +\def\argremovespace#1 $#2\ArgTerm{\finishparsearg#1$\ArgTerm} % If a _delimited_ argument is enclosed in braces, they get stripped; so % to get _exactly_ the rest of the line, we had to prevent such situation. -% We prepended an \empty token at the very beginning and we expand it now, -% just before passing the control to \argtorun. -% (Similarly, we have to think about #3 of \argcheckspacesY above: it is -% either the null string, or it ends with \^^M---thus there is no danger -% that a pair of braces would be stripped. -% -% But first, we have to remove the trailing space token. -% -\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} +% We prepended an \empty token at the very beginning and we expand it +% just before passing the control to \next. +% (But first, we have to remove the remaining $ or two.) +\def\finishparsearg#1$#2\ArgTerm{\expandafter\argtorun\expandafter{#1}} % \parseargdef - define a command taking an argument on the line @@ -5575,6 +5554,11 @@ \newdimen\entryrightmargin \entryrightmargin=0pt +% amount to indent subsequent lines in an entry when it spans more than +% one line. +\newdimen\entrycontskip +\entrycontskip=1em + % for PDF output, whether to make the text of the entry a link to the page % number. set for @contents and @shortcontents where there is only one % page number. @@ -5684,25 +5668,30 @@ \advance\dimen@ii by 1\dimen@i \ifdim\wd\boxA > \dimen@ii % If the entry doesn't fit in one line \ifdim\dimen@ > 0.8\dimen@ii % due to long index text + % Undo changes above + \advance \parfillskip by 0pt minus -1\dimen@i + \advance\dimen@ii by -1\dimen@i + % % Try to split the text roughly evenly. \dimen@ will be the length of % the first line. \dimen@ = 0.7\dimen@ \dimen@ii = \hsize \ifnum\dimen@>\dimen@ii % If the entry is too long (for example, if it needs more than - % two lines), use all the space in the first line. + % two lines), use the same line length for all lines. \dimen@ = \dimen@ii + \else + \advance \dimen@ by 1\rightskip \fi \advance\leftskip by 0pt plus 1fill % ragged right - \advance \dimen@ by 1\rightskip \parshape = 2 0pt \dimen@ 0em \dimen@ii % Ideally we'd add a finite glue at the end of the first line only, % instead of using \parshape with explicit line lengths, but TeX % doesn't seem to provide a way to do such a thing. % % Indent all lines but the first one. - \advance\leftskip by 1em - \advance\parindent by -1em + \advance\leftskip by \entrycontskip + \advance\parindent by -\entrycontskip \fi\fi \indent % start paragraph \unhbox\boxA @@ -6721,6 +6710,82 @@ \input \tocreadfilename } +% process toc file to find the maximum width of the section numbers for +% each chapter +\def\findsecnowidths{% + \begingroup + \setupdatafile + \activecatcodes + \secentryfonts + % Redefinitions + \def\numchapentry##1##2##3##4{% + \def\curchapname{secnowidth-##2}% + \curchapmax=0pt + }% + \let\appentry\numchapentry + % + \def\numsecentry##1##2##3##4{% + \def\cursecname{secnowidth-##2}% + \cursecmax=0pt + % + \setbox0=\hbox{##2}% + \ifdim\wd0>\curchapmax + \curchapmax=\wd0 + \expandafter\xdef\csname\curchapname\endcsname{\the\wd0}% + \fi + }% + \let\appsecentry\numsecentry + % + \def\numsubsecentry##1##2##3##4{% + \def\curssecname{secnowidth-##2}% + \curssecmax=0pt + % + \setbox0=\hbox{##2}% + \ifdim\wd0>\cursecmax + \cursecmax=\wd0 + \expandafter\xdef\csname\cursecname\endcsname{\the\wd0}% + \fi + }% + \let\appsubsecentry\numsubsecentry + % + \def\numsubsubsecentry##1##2##3##4{% + \setbox0=\hbox{##2}% + \ifdim\wd0>\curssecmax + \curssecmax=\wd0 + \expandafter\xdef\csname\curssecname\endcsname{\the\wd0}% + \fi + }% + \let\appsubsubsecentry\numsubsubsecentry + % + % Discard any output by outputting to dummy vbox, in case the toc file + % contains macros that we have not redefined above. + \setbox\dummybox\vbox\bgroup + \input \tocreadfilename\relax + \egroup + \endgroup +} +\newdimen\curchapmax +\newdimen\cursecmax +\newdimen\curssecmax + + +% set #1 to the maximum section width for #2 +\def\retrievesecnowidth#1#2{% + \expandafter\let\expandafter\savedsecnowidth \csname secnowidth-#2\endcsname + \ifx\savedsecnowidth\relax + #1=0pt + \else + #1=\savedsecnowidth + \fi +} +\newdimen\secnowidthchap +\secnowidthchap=0pt +\newdimen\secnowidthsec +\secnowidthsec=0pt +\newdimen\secnowidthssec +\secnowidthssec=0pt + + \newskip\contentsrightmargin \contentsrightmargin=1in \newcount\savepageno \newcount\lastnegativepageno \lastnegativepageno = -1 @@ -6766,6 +6831,7 @@ \startcontents{\putwordTOC}% \openin 1 \tocreadfilename\space \ifeof 1 \else + \findsecnowidths \readtocfile \fi \vfill \eject @@ -6793,6 +6859,7 @@ \rm \hyphenpenalty = 10000 \advance\baselineskip by 1pt % Open it up a little. + \extrasecnoskip=0.4pt \def\numsecentry##1##2##3##4{} \let\appsecentry = \numsecentry \let\unnsecentry = \numsecentry @@ -6828,8 +6895,6 @@ % This space should be enough, since a single number is .5em, and the % widest letter (M) is 1em, at least in the Computer Modern fonts. % But use \hss just in case. - % (This space doesn't include the extra space that gets added after - % the label; that gets put in by \shortchapentry above.) % % We'd like to right-justify chapter numbers, but that looks strange % with appendix letters. And right-justifying numbers and @@ -6839,10 +6904,15 @@ \hbox to 1em{#1\hss}% } -% These macros generate individual entries in the table of contents. -% The first argument is the chapter or section name. -% The last argument is the page number. -% The arguments in between are the chapter number, section number, ... +% These macros generate individual entries in the table of contents, +% and are read in from the *.toc file. +% +% The arguments are like: +% \def\numchapentry#1#2#3#4 +% #1 - the chapter or section name. +% #2 - section number +% #3 - level of section (e.g "chap", "sec") +% #4 - page number % Parts, in the main contents. Replace the part number, which doesn't % exist, with an empty box. Let's hope all the numbers have the same width. @@ -6855,7 +6925,7 @@ \vskip 0pt plus 5\baselineskip \penalty-300 \vskip 0pt plus -5\baselineskip - \dochapentry{\numeralbox\labelspace#1}{}% + \dochapentry{#1}{\numeralbox}{}% } % % Parts, in the short toc. @@ -6866,12 +6936,14 @@ } % Chapters, in the main contents. -\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} +\def\numchapentry#1#2#3#4{% + \retrievesecnowidth\secnowidthchap{#2}% + \dochapentry{#1}{#2}{#4}% +} % Chapters, in the short toc. -% See comments in \dochapentry re vbox and related settings. \def\shortchapentry#1#2#3#4{% - \tocentry{\shortchaplabel{#2}\labelspace #1}{#4}% + \tocentry{#1}{\shortchaplabel{#2}}{#4}% } % Appendices, in the main contents. @@ -6882,67 +6954,111 @@ \setbox0 = \hbox{\putwordAppendix{} M}% \hbox to \wd0{\putwordAppendix{} #1\hss}} % -\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\hskip.7em#1}{#4}} +\def\appentry#1#2#3#4{% + \retrievesecnowidth\secnowidthchap{#2}% + \dochapentry{\appendixbox{#2}\hskip.7em#1}{}{#4}% +} % Unnumbered chapters. -\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} -\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{#4}} +\def\unnchapentry#1#2#3#4{\dochapentry{#1}{}{#4}} +\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{}{#4}} % Sections. -\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} +\def\numsecentry#1#2#3#4{\dosecentry{#1}{#2}{#4}} + +\def\numsecentry#1#2#3#4{% + \retrievesecnowidth\secnowidthsec{#2}% + \dosecentry{#1}{#2}{#4}% +} \let\appsecentry=\numsecentry -\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} +\def\unnsecentry#1#2#3#4{% + \retrievesecnowidth\secnowidthsec{#2}% + \dosecentry{#1}{}{#4}% +} % Subsections. -\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} +\def\numsubsecentry#1#2#3#4{% + \retrievesecnowidth\secnowidthssec{#2}% + \dosubsecentry{#1}{#2}{#4}% +} \let\appsubsecentry=\numsubsecentry -\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} +\def\unnsubsecentry#1#2#3#4{% + \retrievesecnowidth\secnowidthssec{#2}% + \dosubsecentry{#1}{}{#4}% +} % And subsubsections. -\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} +\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#2}{#4}} \let\appsubsubsecentry=\numsubsubsecentry -\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} +\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{}{#4}} % This parameter controls the indentation of the various levels. % Same as \defaultparindent. \newdimen\tocindent \tocindent = 15pt -% Now for the actual typesetting. In all these, #1 is the text and #2 is the -% page number. +% Now for the actual typesetting. In all these, #1 is the text, #2 is +% a section number if present, and #3 is the page number. % % If the toc has to be broken over pages, we want it to be at chapters % if at all possible; hence the \penalty. -\def\dochapentry#1#2{% +\def\dochapentry#1#2#3{% \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip \begingroup % Move the page numbers slightly to the right \advance\entryrightmargin by -0.05em \chapentryfonts - \tocentry{#1}{#2}% + \extrasecnoskip=0.4em % separate chapter number more + \tocentry{#1}{#2}{#3}% \endgroup \nobreak\vskip .25\baselineskip plus.1\baselineskip } -\def\dosecentry#1#2{\begingroup +\def\dosecentry#1#2#3{\begingroup + \secnowidth=\secnowidthchap \secentryfonts \leftskip=\tocindent - \tocentry{#1}{#2}% + \tocentry{#1}{#2}{#3}% \endgroup} -\def\dosubsecentry#1#2{\begingroup +\def\dosubsecentry#1#2#3{\begingroup + \secnowidth=\secnowidthsec \subsecentryfonts \leftskip=2\tocindent - \tocentry{#1}{#2}% + \tocentry{#1}{#2}{#3}% \endgroup} -\def\dosubsubsecentry#1#2{\begingroup +\def\dosubsubsecentry#1#2#3{\begingroup + \secnowidth=\secnowidthssec \subsubsecentryfonts \leftskip=3\tocindent - \tocentry{#1}{#2}% + \tocentry{#1}{#2}{#3}% \endgroup} -% We use the same \entry macro as for the index entries. -\let\tocentry = \entry +% Used for the maximum width of a section number so we can align +% section titles. +\newdimen\secnowidth +\secnowidth=0pt +\newdimen\extrasecnoskip +\extrasecnoskip=0pt -% Space between chapter (or whatever) number and the title. -\def\labelspace{\hskip1em \relax} +% \tocentry{TITLE}{SEC NO}{PAGE} +% +\def\tocentry#1#2#3{% + \def\secno{#2}% + \ifx\empty\secno + \entry{#1}{#3}% + \else + \ifdim 0pt=\secnowidth + \setbox0=\hbox{#2\hskip\labelspace\hskip\extrasecnoskip}% + \else + \advance\secnowidth by \labelspace + \advance\secnowidth by \extrasecnoskip + \setbox0=\hbox to \secnowidth{% + #2\hskip\labelspace\hskip\extrasecnoskip\hfill}% + \fi + \entrycontskip=\wd0 + \entry{\box0 #1}{#3}% + \fi +} +\newdimen\labelspace +\labelspace=0.6em \def\chapentryfonts{\secfonts \rm} \def\secentryfonts{\textfonts} @@ -7787,6 +7903,8 @@ \tolerance=10000 \hbadness=10000 \exdentamount=\defbodyindent {% + \def\^^M{}% for line continuation + % % defun fonts. We use typewriter by default (used to be bold) because: % . we're printing identifiers, they should be in tt in principle. % . in languages with many accents, such as Czech or French, it's @@ -7819,6 +7937,7 @@ % Print arguments. Use slanted for @def*, typewriter for @deftype*. \def\defunargs#1{% \bgroup + \def\^^M{}% for line continuation \df \ifdoingtypefn \tt \else \sl \fi \ifflagclear{txicodevaristt}{}% {\def\var##1{{\setregularquotes \ttsl ##1}}}% diff --git a/lib/diffseq.h b/lib/diffseq.h index 06e1465bf1b..3f85ab2ec41 100644 --- a/lib/diffseq.h +++ b/lib/diffseq.h @@ -92,20 +92,11 @@ # define NOTE_ORDERED false #endif -/* Use this to suppress gcc's "...may be used before initialized" warnings. - Beware: The Code argument must not contain commas. */ +/* Suppress gcc's "...may be used before initialized" warnings, + generated by GCC versions up to at least GCC 13.2. */ #if __GNUC__ + (__GNUC_MINOR__ >= 7) > 4 # pragma GCC diagnostic push -#endif -#ifndef IF_LINT -# if defined GCC_LINT || defined lint -# define IF_LINT(Code) Code -# else -# define IF_LINT(Code) /* empty */ -# if __GNUC__ + (__GNUC_MINOR__ >= 7) > 4 -# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" -# endif -# endif +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" #endif /* @@ -388,13 +379,8 @@ diag (OFFSET xoff, OFFSET xlim, OFFSET yoff, OFFSET ylim, bool find_minimal, and report halfway between our best results so far. */ if (c >= ctxt->too_expensive) { - OFFSET fxybest; - OFFSET fxbest IF_LINT (= 0); - OFFSET bxybest; - OFFSET bxbest IF_LINT (= 0); - /* Find forward diagonal that maximizes X + Y. */ - fxybest = -1; + OFFSET fxybest = -1, fxbest; for (d = fmax; d >= fmin; d -= 2) { OFFSET x = MIN (fd[d], xlim); @@ -412,7 +398,7 @@ diag (OFFSET xoff, OFFSET xlim, OFFSET yoff, OFFSET ylim, bool find_minimal, } /* Find backward diagonal that minimizes X + Y. */ - bxybest = OFFSET_MAX; + OFFSET bxybest = OFFSET_MAX, bxbest; for (d = bmax; d >= bmin; d -= 2) { OFFSET x = MAX (xoff, bd[d]); diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 1bfa1daa0b3..78ecb544c6e 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -41,6 +41,10 @@ # --avoid=dup \ # --avoid=fchdir \ # --avoid=fstat \ +# --avoid=iswblank \ +# --avoid=iswctype \ +# --avoid=iswdigit \ +# --avoid=iswxdigit \ # --avoid=langinfo \ # --avoid=lock \ # --avoid=mbrtowc \ @@ -67,6 +71,7 @@ # --avoid=utime-h \ # --avoid=wchar \ # --avoid=wcrtomb \ +# --avoid=wctype \ # --avoid=wctype-h \ # alignasof \ # alloca-opt \ @@ -658,7 +663,6 @@ GL_GNULIB__EXIT = @GL_GNULIB__EXIT@ GMALLOC_OBJ = @GMALLOC_OBJ@ GMP_H = @GMP_H@ GNULIBHEADERS_OVERRIDE_WINT_T = @GNULIBHEADERS_OVERRIDE_WINT_T@ -GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@ GNULIB_WARN_CFLAGS = @GNULIB_WARN_CFLAGS@ GNUSTEP_CFLAGS = @GNUSTEP_CFLAGS@ GNU_OBJC_CFLAGS = @GNU_OBJC_CFLAGS@ diff --git a/lib/time.in.h b/lib/time.in.h index 06428adb1d0..06824da9d3d 100644 --- a/lib/time.in.h +++ b/lib/time.in.h @@ -143,6 +143,12 @@ _GL_CXXALIAS_SYS (timespec_get, int, (struct timespec *ts, int base)); # if __GLIBC__ >= 2 _GL_CXXALIASWARN (timespec_get); # endif +# elif defined GNULIB_POSIXCHECK +# undef timespec_get +# if HAVE_RAW_DECL_TIMESPEC_GET +_GL_WARN_ON_USE (timespec_get, "timespec_get is unportable - " + "use gnulib module timespec_get for portability"); +# endif # endif /* Set *TS to the current time resolution, and return BASE. @@ -154,6 +160,12 @@ _GL_FUNCDECL_SYS (timespec_getres, int, (struct timespec *ts, int base) # endif _GL_CXXALIAS_SYS (timespec_getres, int, (struct timespec *ts, int base)); _GL_CXXALIASWARN (timespec_getres); +# elif defined GNULIB_POSIXCHECK +# undef timespec_getres +# if HAVE_RAW_DECL_TIMESPEC_GETRES +_GL_WARN_ON_USE (timespec_getres, "timespec_getres is unportable - " + "use gnulib module timespec_getres for portability"); +# endif # endif /* Return the number of seconds that have elapsed since the Epoch. */ @@ -170,6 +182,12 @@ _GL_CXXALIAS_SYS (time, time_t, (time_t *__tp)); # if __GLIBC__ >= 2 _GL_CXXALIASWARN (time); # endif +# elif defined GNULIB_POSIXCHECK +# undef time +# if HAVE_RAW_DECL_TIME +_GL_WARN_ON_USE (time, "time has consistency problems - " + "use gnulib module time for portability"); +# endif # endif /* Sleep for at least RQTP seconds unless interrupted, If interrupted, @@ -195,6 +213,12 @@ _GL_CXXALIAS_SYS (nanosleep, int, (struct timespec const *__rqtp, struct timespec *__rmtp)); # endif _GL_CXXALIASWARN (nanosleep); +# elif defined GNULIB_POSIXCHECK +# undef nanosleep +# if HAVE_RAW_DECL_NANOSLEEP +_GL_WARN_ON_USE (nanosleep, "nanosleep is unportable - " + "use gnulib module nanosleep for portability"); +# endif # endif /* Initialize time conversion information. */ @@ -230,6 +254,12 @@ _GL_CXXALIAS_MDA (tzset, void, (void)); _GL_CXXALIAS_SYS (tzset, void, (void)); # endif _GL_CXXALIASWARN (tzset); +# elif defined GNULIB_POSIXCHECK +# undef tzset +# if HAVE_RAW_DECL_TZSET +_GL_WARN_ON_USE (tzset, "tzset has portability problems - " + "use gnulib module tzset for portability"); +# endif # endif /* Return the 'time_t' representation of TP and normalize TP. */ @@ -246,6 +276,12 @@ _GL_CXXALIAS_SYS (mktime, time_t, (struct tm *__tp)); # if __GLIBC__ >= 2 _GL_CXXALIASWARN (mktime); # endif +# elif defined GNULIB_POSIXCHECK +# undef mktime +# if HAVE_RAW_DECL_MKTIME +_GL_WARN_ON_USE (mktime, "mktime has portability problems - " + "use gnulib module mktime for portability"); +# endif # endif /* Convert TIMER to RESULT, assuming local time and UTC respectively. See @@ -296,6 +332,17 @@ _GL_CXXALIAS_SYS (gmtime_r, struct tm *, (time_t const *restrict __timer, # if @HAVE_DECL_LOCALTIME_R@ _GL_CXXALIASWARN (gmtime_r); # endif +# elif defined GNULIB_POSIXCHECK +# undef localtime_r +# if HAVE_RAW_DECL_LOCALTIME_R +_GL_WARN_ON_USE (localtime_r, "localtime_r is unportable - " + "use gnulib module time_r for portability"); +# endif +# undef gmtime_r +# if HAVE_RAW_DECL_GMTIME_R +_GL_WARN_ON_USE (gmtime_r, "gmtime_r is unportable - " + "use gnulib module time_r for portability"); +# endif # endif /* Convert TIMER to RESULT, assuming local time and UTC respectively. See @@ -316,6 +363,12 @@ _GL_CXXALIAS_SYS (localtime, struct tm *, (time_t const *__timer)); # if __GLIBC__ >= 2 _GL_CXXALIASWARN (localtime); # endif +# elif defined GNULIB_POSIXCHECK +# undef localtime +# if HAVE_RAW_DECL_LOCALTIME +_GL_WARN_ON_USE (localtime, "localtime has portability problems - " + "use gnulib module localtime for portability"); +# endif # endif # if 0 || @REPLACE_GMTIME@ @@ -347,6 +400,12 @@ _GL_CXXALIAS_SYS (strptime, char *, (char const *restrict __buf, char const *restrict __format, struct tm *restrict __tm)); _GL_CXXALIASWARN (strptime); +# elif defined GNULIB_POSIXCHECK +# undef strptime +# if HAVE_RAW_DECL_STRPTIME +_GL_WARN_ON_USE (strptime, "strptime is unportable - " + "use gnulib module strptime for portability"); +# endif # endif /* Convert *TP to a date and time string. See @@ -368,6 +427,12 @@ _GL_CXXALIAS_SYS (ctime, char *, (time_t const *__tp)); # if __GLIBC__ >= 2 _GL_CXXALIASWARN (ctime); # endif +# elif defined GNULIB_POSIXCHECK +# undef ctime +# if HAVE_RAW_DECL_CTIME +_GL_WARN_ON_USE (ctime, "ctime has portability problems - " + "use gnulib module ctime for portability"); +# endif # endif /* Convert *TP to a date and time string. See @@ -392,6 +457,12 @@ _GL_CXXALIAS_SYS (strftime, size_t, # if __GLIBC__ >= 2 _GL_CXXALIASWARN (strftime); # endif +# elif defined GNULIB_POSIXCHECK +# undef strftime +# if HAVE_RAW_DECL_STRFTIME +_GL_WARN_ON_USE (strftime, "strftime has portability problems - " + "use gnulib module strftime-fixes for portability"); +# endif # endif # if defined _GNU_SOURCE && @GNULIB_TIME_RZ@ && ! @HAVE_TIMEZONE_T@ @@ -469,6 +540,12 @@ _GL_CXXALIAS_SYS (timegm, time_t, (struct tm *__tm)); # if __GLIBC__ >= 2 _GL_CXXALIASWARN (timegm); # endif +# elif defined GNULIB_POSIXCHECK +# undef timegm +# if HAVE_RAW_DECL_TIMEGM +_GL_WARN_ON_USE (timegm, "timegm is unportable - " + "use gnulib module timegm for portability"); +# endif # endif /* Encourage applications to avoid unsafe functions that can overrun @@ -476,8 +553,10 @@ _GL_CXXALIASWARN (timegm); applications should use strftime (or even sprintf) instead. */ # if defined GNULIB_POSIXCHECK # undef asctime +# if HAVE_RAW_DECL_ASCTIME _GL_WARN_ON_USE (asctime, "asctime can overrun buffers in some cases - " "better use strftime (or even sprintf) instead"); +# endif # endif # if defined GNULIB_POSIXCHECK # undef asctime_r @@ -488,8 +567,10 @@ _GL_WARN_ON_USE (asctime_r, "asctime_r can overrun buffers in some cases - " # endif # if defined GNULIB_POSIXCHECK # undef ctime +# if HAVE_RAW_DECL_CTIME _GL_WARN_ON_USE (ctime, "ctime can overrun buffers in some cases - " "better use strftime (or even sprintf) instead"); +# endif # endif # if defined GNULIB_POSIXCHECK # undef ctime_r diff --git a/m4/clock_time.m4 b/m4/clock_time.m4 index d624a73d35d..28534db1c76 100644 --- a/m4/clock_time.m4 +++ b/m4/clock_time.m4 @@ -1,4 +1,4 @@ -# clock_time.m4 serial 12 +# clock_time.m4 serial 13 dnl Copyright (C) 2002-2006, 2009-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -12,9 +12,17 @@ AC_DEFUN([gl_CLOCK_TIME], [ + AC_REQUIRE([AC_CANONICAL_HOST]) + dnl Persuade glibc and Solaris to declare these functions. AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + # On mingw, these functions are defined in the libwinpthread library, + # which is better avoided. In fact, the clock_gettime function is buggy + # in 32-bit mingw, when -D__MINGW_USE_VC2005_COMPAT is used (which Gnulib's + # year2038 module does): It leaves the upper 32 bits of the tv_sec field + # of the result uninitialized. + # Solaris 2.5.1 needs -lposix4 to get the clock_gettime function. # Solaris 7 prefers the library name -lrt to the obsolescent name -lposix4. @@ -23,12 +31,22 @@ AC_DEFUN # library, inducing unnecessary run-time overhead. CLOCK_TIME_LIB= AC_SUBST([CLOCK_TIME_LIB]) - gl_saved_libs=$LIBS - AC_SEARCH_LIBS([clock_gettime], [rt posix4], - [test "$ac_cv_search_clock_gettime" = "none required" || - CLOCK_TIME_LIB=$ac_cv_search_clock_gettime]) - AC_CHECK_FUNCS([clock_getres clock_gettime clock_settime]) - LIBS=$gl_saved_libs + case "$host_os" in + mingw*) + ac_cv_func_clock_getres=no + ac_cv_func_clock_gettime=no + ac_cv_func_clock_settime=no + ;; + *) + gl_saved_libs=$LIBS + AC_SEARCH_LIBS([clock_gettime], [rt posix4], + [test "$ac_cv_search_clock_gettime" = "none required" || + CLOCK_TIME_LIB=$ac_cv_search_clock_gettime]) + AC_CHECK_FUNCS([clock_getres clock_gettime clock_settime]) + LIBS=$gl_saved_libs + ;; + esac + # For backward compatibility. LIB_CLOCK_GETTIME="$CLOCK_TIME_LIB" AC_SUBST([LIB_CLOCK_GETTIME]) diff --git a/m4/mktime.m4 b/m4/mktime.m4 index e9d31f35a46..69cce86da5a 100644 --- a/m4/mktime.m4 +++ b/m4/mktime.m4 @@ -1,4 +1,4 @@ -# serial 37 +# serial 38 dnl Copyright (C) 2002-2003, 2005-2007, 2009-2023 Free Software Foundation, dnl Inc. dnl This file is free software; the Free Software Foundation @@ -280,7 +280,6 @@ AC_DEFUN AC_REQUIRE([AC_CANONICAL_HOST]) AC_REQUIRE([gl_FUNC_MKTIME_WORKS]) - REPLACE_MKTIME=0 if test "$gl_cv_func_working_mktime" != yes; then REPLACE_MKTIME=1 AC_DEFINE([NEED_MKTIME_WORKING], [1], diff --git a/m4/nanosleep.m4 b/m4/nanosleep.m4 index e21a3e343cb..b7f22d7b606 100644 --- a/m4/nanosleep.m4 +++ b/m4/nanosleep.m4 @@ -1,4 +1,4 @@ -# serial 43 +# serial 44 dnl From Jim Meyering. dnl Check for the nanosleep function. @@ -126,9 +126,7 @@ AC_DEFUN ]) ]) case "$gl_cv_func_nanosleep" in - *yes) - REPLACE_NANOSLEEP=0 - ;; + *yes) ;; *) REPLACE_NANOSLEEP=1 case "$gl_cv_func_nanosleep" in diff --git a/m4/stdalign.m4 b/m4/stdalign.m4 index 1a236d66d2f..6a39ffe7565 100644 --- a/m4/stdalign.m4 +++ b/m4/stdalign.m4 @@ -68,8 +68,10 @@ AC_DEFUN dnl The "zz" puts this toward config.h's end, to avoid potential dnl collisions with other definitions. AH_VERBATIM([zzalignas], -[#if !defined HAVE_C_ALIGNASOF && __cplusplus < 201103 && !defined alignof -# if HAVE_STDALIGN_H +[#if !defined HAVE_C_ALIGNASOF \ + && !(defined __cplusplus && 201103 <= __cplusplus) \ + && !defined alignof +# if defined HAVE_STDALIGN_H # include # endif @@ -166,7 +168,7 @@ AC_DEFUN # define _Alignas(a) __declspec (align (a)) # endif # endif -# if !HAVE_STDALIGN_H +# if !defined HAVE_STDALIGN_H # if ((defined _Alignas \ && !(defined __cplusplus \ && (201103 <= __cplusplus || defined _MSC_VER))) \ @@ -175,7 +177,7 @@ AC_DEFUN # endif # endif -# if _GL_STDALIGN_NEEDS_STDDEF +# if defined _GL_STDALIGN_NEEDS_STDDEF # include # endif #endif]) diff --git a/m4/time_h.m4 b/m4/time_h.m4 index 51d553a2f1a..632d18fc071 100644 --- a/m4/time_h.m4 +++ b/m4/time_h.m4 @@ -2,7 +2,7 @@ # Copyright (C) 2000-2001, 2003-2007, 2009-2023 Free Software Foundation, Inc. -# serial 22 +# serial 24 # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -23,7 +23,10 @@ AC_DEFUN_ONCE dnl corresponding gnulib module is not in use. gl_WARN_ON_USE_PREPARE([[ #include - ]], [asctime_r ctime_r]) + ]], [ + asctime asctime_r ctime ctime_r gmtime_r localtime localtime_r mktime + nanosleep strftime strptime time timegm timespec_get timespec_getres tzset + ]) AC_REQUIRE([AC_C_RESTRICT]) @@ -162,25 +165,15 @@ AC_DEFUN HAVE_TIMESPEC_GETRES=1; AC_SUBST([HAVE_TIMESPEC_GETRES]) dnl Even GNU libc does not have timezone_t yet. HAVE_TIMEZONE_T=0; AC_SUBST([HAVE_TIMEZONE_T]) - dnl If another module says to replace or to not replace, do that. - dnl Otherwise, replace only if someone compiles with -DGNULIB_PORTCHECK; - dnl this lets maintainers check for portability. - REPLACE_CTIME=GNULIB_PORTCHECK; AC_SUBST([REPLACE_CTIME]) - REPLACE_LOCALTIME_R=GNULIB_PORTCHECK; AC_SUBST([REPLACE_LOCALTIME_R]) - REPLACE_MKTIME=GNULIB_PORTCHECK; AC_SUBST([REPLACE_MKTIME]) - REPLACE_NANOSLEEP=GNULIB_PORTCHECK; AC_SUBST([REPLACE_NANOSLEEP]) - REPLACE_STRFTIME=GNULIB_PORTCHECK; AC_SUBST([REPLACE_STRFTIME]) - REPLACE_TIME=0; AC_SUBST([REPLACE_TIME]) - REPLACE_TIMEGM=GNULIB_PORTCHECK; AC_SUBST([REPLACE_TIMEGM]) - REPLACE_TIMESPEC_GET=GNULIB_PORTCHECK; AC_SUBST([REPLACE_TIMESPEC_GET]) - REPLACE_TZSET=GNULIB_PORTCHECK; AC_SUBST([REPLACE_TZSET]) - - dnl Hack so that the time module doesn't depend on the sys_time module. - dnl First, default GNULIB_GETTIMEOFDAY to 0 if sys_time is absent. - : ${GNULIB_GETTIMEOFDAY=0}; AC_SUBST([GNULIB_GETTIMEOFDAY]) - dnl Second, it's OK to not use GNULIB_PORTCHECK for REPLACE_GMTIME - dnl and REPLACE_LOCALTIME, as portability to Solaris 2.6 and earlier - dnl is no longer a big deal. + REPLACE_CTIME=0; AC_SUBST([REPLACE_CTIME]) REPLACE_GMTIME=0; AC_SUBST([REPLACE_GMTIME]) REPLACE_LOCALTIME=0; AC_SUBST([REPLACE_LOCALTIME]) + REPLACE_LOCALTIME_R=0; AC_SUBST([REPLACE_LOCALTIME_R]) + REPLACE_MKTIME=0; AC_SUBST([REPLACE_MKTIME]) + REPLACE_NANOSLEEP=0; AC_SUBST([REPLACE_NANOSLEEP]) + REPLACE_STRFTIME=0; AC_SUBST([REPLACE_STRFTIME]) + REPLACE_TIME=0; AC_SUBST([REPLACE_TIME]) + REPLACE_TIMEGM=0; AC_SUBST([REPLACE_TIMEGM]) + REPLACE_TIMESPEC_GET=0; AC_SUBST([REPLACE_TIMESPEC_GET]) + REPLACE_TZSET=0; AC_SUBST([REPLACE_TZSET]) ]) diff --git a/m4/time_r.m4 b/m4/time_r.m4 index adce438abf1..4831eb26f90 100644 --- a/m4/time_r.m4 +++ b/m4/time_r.m4 @@ -57,9 +57,7 @@ AC_DEFUN [gl_cv_time_r_posix=yes], [gl_cv_time_r_posix=no]) ]) - if test $gl_cv_time_r_posix = yes; then - REPLACE_LOCALTIME_R=0 - else + if test $gl_cv_time_r_posix != yes; then REPLACE_LOCALTIME_R=1 fi else diff --git a/m4/timegm.m4 b/m4/timegm.m4 index 8ab265e65fe..6da07807698 100644 --- a/m4/timegm.m4 +++ b/m4/timegm.m4 @@ -1,4 +1,4 @@ -# timegm.m4 serial 15 +# timegm.m4 serial 16 dnl Copyright (C) 2003, 2007, 2009-2023 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -8,7 +8,6 @@ AC_DEFUN [ AC_REQUIRE([gl_TIME_H_DEFAULTS]) AC_REQUIRE([gl_FUNC_MKTIME_WORKS]) - REPLACE_TIMEGM=0 gl_CHECK_FUNCS_ANDROID([timegm], [[#include ]]) if test $ac_cv_func_timegm = yes; then if test "$gl_cv_func_working_mktime" != yes; then diff --git a/src/Makefile.in b/src/Makefile.in index e818b6c1655..b14681f2537 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -167,7 +167,7 @@ CLOCK_TIME_LIB= EUIDACCESS_LIBGEN=@EUIDACCESS_LIBGEN@ NANOSLEEP_LIB=@NANOSLEEP_LIB@ QCOPY_ACL_LIB=@QCOPY_ACL_LIB@ -LIB_TIMER_TIME=@LIB_TIMER_TIME@ +TIMER_TIME_LIB=@TIMER_TIME_LIB@ DBUS_CFLAGS = @DBUS_CFLAGS@ DBUS_LIBS = @DBUS_LIBS@ @@ -596,7 +596,7 @@ LIBES = $(LIBX_OTHER) $(LIBSOUND) \ $(RSVG_LIBS) $(IMAGEMAGICK_LIBS) $(LIB_ACL) $(CLOCK_TIME_LIB) \ $(NANOSLEEP_LIB) $(QCOPY_ACL_LIB) $(WEBKIT_LIBS) \ - $(EUIDACCESS_LIBGEN) $(LIB_TIMER_TIME) $(DBUS_LIBS) \ + $(EUIDACCESS_LIBGEN) $(TIMER_TIME_LIB) $(DBUS_LIBS) \ $(LIB_EXECINFO) $(XRANDR_LIBS) $(XINERAMA_LIBS) $(XFIXES_LIBS) \ $(XDBE_LIBS) $(XSYNC_LIBS) \ $(LIBXML2_LIBS) $(LIBGPM) $(LIBS_SYSTEM) $(CAIRO_LIBS) \ commit b66c16c789d8046ff40c48b9346b26238b1b97e1 Author: Paul Eggert Date: Sat Aug 12 12:50:15 2023 -0700 Adjust to recent Gnulib changes The Gnulib regex module now pulls in some wide-char stuff that I think Emacs doesn’t need. * admin/merge-gnulib (AVOIDED_MODULES): Add iswblank, iswctype, iswdigit, iswxdigit, wctype. diff --git a/admin/merge-gnulib b/admin/merge-gnulib index b533f69cceb..f64c0670bcf 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -52,12 +52,13 @@ GNULIB_MODULES= ' AVOIDED_MODULES=' - btowc chmod close crypto/af_alg dup fchdir fstat langinfo lock + btowc chmod close crypto/af_alg dup fchdir fstat + iswblank iswctype iswdigit iswxdigit langinfo lock mbrtowc mbsinit memchr mkdir msvc-inval msvc-nothrow nl_langinfo openat-die opendir pthread-h raise save-cwd select setenv sigprocmask stat stdarg threadlib tzset unsetenv utime utime-h - wchar wcrtomb wctype-h + wchar wcrtomb wctype wctype-h ' GNULIB_TOOL_FLAGS=' commit f789cb464094990db9fcba8c173f4988ada4e935 Author: Stefan Monnier Date: Sat Aug 12 14:20:59 2023 -0400 * lisp/emacs-lisp/cl-macs.el (cl--labels-convert): Remove outdated fixme This was fixed in commit 44d7fd3805. diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index 87b6801bf00..a2e36fa3582 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -2040,7 +2040,6 @@ cl--labels-convert ;; In Common Lisp, we'd use the `&whole' arg instead (see ;; "Macro Lambda Lists" in the CLHS). ((let ((symbols-with-pos-enabled nil)) ;Don't rewrite #' => #' - ;; FIXME: The above `let' is incorrectly optimized away (bug#65017). (eq f (car cl--labels-convert-cache))) ;; This value should be `eq' to the `&whole' form. ;; If this is not the case, we have a bug. commit 3fc9793a260feed0deda8b6bdfd3e1c73774181a Merge: 276bf5369ff 842dbf500e0 Author: Eli Zaretskii Date: Sat Aug 12 13:50:38 2023 -0400 Merge from origin/emacs-29 842dbf500e0 * Fix `batch-byte+native-compile' target directory. 8dbd5aa1eef Avoid crashes in 'display_count_lines' when current buffe... 47b4f4cf783 ; * doc/emacs/mini.texi (Completion Commands): Fix a typo... 78999d52136 Update csharp tree-sitter support due to upstream changes 556cb01b48e Substitute command keys in 'ielm-header' at use time 99367078e50 ; * etc/PROBLEMS: Add entry about XIM problems (bug#65205). 03e897c08fa Fix rare crashes in redisplay due to problems with fontsets b1ba06a1e4b Fix a typo in 'leuven-dark-theme.el' e80a9dcabb9 ; * admin/git-bisect-start: Update failing commits 9091f42784b ; * admin/git-bisect-start: Update failing commits 344ac529ca9 ; * etc/PROBLEMS: Fix typo. bccf848b26c Adapt Tramp test 71bc060e403 Properly expand the JSX indentation rules in 'js-ts-mode' 889cfb42ed9 * Add `emacs-lisp-native-compile' to easy-menu. 4535aafa8a3 * lisp/progmodes/elisp-mode.el (emacs-lisp-mode-menu): Si... 173af0cad51 * Introduce `emacs-lisp-native-compile'. 063d7d89d7a Fix the -x switch in non-X builds 5a7fb0b39b7 Document that `set-mouse-color' does not work everywhere d06fc72496f ; * doc/emacs/dired.texi (Misc Dired Features): Fix last ... 97b8ac376b5 Fix the effects and documentation of 'dired-free-space' 75c72e59f69 ; Fix typo 6d11b7b3007 Fix cross-reference to eldoc in eglot manual 913e50aba6c Add native-compilation to Emacs Lisp mode menu b93107c20b2 Fix emacs-lisp-native-compile-and-load eln target directo... commit 276bf5369ffa47f994586b932cb6a9dd760ecfeb Merge: b289f0769f0 7e3c6160860 Author: Eli Zaretskii Date: Sat Aug 12 13:48:34 2023 -0400 ; Merge from origin/emacs-29 The following commits were skipped: 7e3c6160860 ; Re-enable the /dev/urandom test ff21717f05b Disable failing test (bug#65156) commit b289f0769f0a43fdaabec41b92bacc51acbc4b9b Merge: de6c1c4d5c9 4767f5eaeed Author: Eli Zaretskii Date: Sat Aug 12 13:42:14 2023 -0400 Merge from origin/emacs-29 4767f5eaeed Better fix for bug#65156 dd1d8414b33 Fix insert-file-contents with pipes and /dev/stdin 50649a6d1a2 ; * etc/PROBLEMS: Fix wording. f0dda682ffb ; * etc/NEWS.28: Add deletion of levents.el. f4acae842c0 Fix bug#65042 e1874c4e8bf * configure.ac (HAVE_TREE_SITTER): Set NEED_DYNLIB=yes (b... ef8838c3a5f * etc/NEWS: Mention tramp-show-ad-hoc-proxies. 495bee253fc * test/lisp/net/tramp-tests.el (tramp-test42-utf8): Skip ... de1effd73b4 ; Fix last change 7c7966862bc * test/lisp/net/tramp-tests.el (tramp-test10-write-region... 16205e8db65 ; Improve help-echo in package.el a95e7006989 ; Filter packages available for upgrade via menu bar adff72dd1d2 Fix reverting Rmail buffers commit 842dbf500e0fcf44d686d480f39d067b19ff38a9 Author: Andrea Corallo Date: Sat Aug 12 18:42:33 2023 +0200 * Fix `batch-byte+native-compile' target directory. * lisp/emacs-lisp/comp.el (batch-native-compile): Don't shadow `native-compile-target-directory' unless necessary. diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el index bdce4254bf5..0c70789be71 100644 --- a/lisp/emacs-lisp/comp.el +++ b/lisp/emacs-lisp/comp.el @@ -4305,8 +4305,9 @@ batch-native-compile (comp-ensure-native-compiler) (let ((comp-running-batch-compilation t) (native-compile-target-directory - (if for-tarball - (car (last native-comp-eln-load-path))))) + (if for-tarball + (car (last native-comp-eln-load-path)) + native-compile-target-directory))) (cl-loop for file in command-line-args-left if (or (null byte+native-compile) (cl-notany (lambda (re) (string-match re file)) commit de6c1c4d5c92b92d5b280e157c2a5bc3228749f2 Author: Mattias Engdegård Date: Sat Aug 12 17:39:58 2023 +0200 rx: Better translation of char-matching patterns Translate or-patterns that (even partially) match single characters into character alternatives which are more efficient in matching, sometimes algorithmically so. Example: (or "%" (in "a-z") space) was previously translated to "%\\|[a-z]\\|[[:space:]]" but now becomes "[%a-z[:space:]]" Single-char patterns include `nonl` and `anychar`, which now can also be used in set operations (union, complement and intersection), and character classes. For example, `(or nonl "\n")` is now equivalent to `anychar`. * lisp/emacs-lisp/rx.el (rx--expand-def): Remove, split into... (rx--expand-def-form, rx--expand-def-symbol): ...these. (rx--translate-compat-symbol-entry) (rx--translate-compat-form-entry): New functions for handling the legacy extension mechanism. (rx--normalise-or-arg): Renamed to... (rx--normalise-char-pattern): ...this, and rewrite. (rx--all-string-or-args): Remove, split into... (rx--all-string-branches-p, rx--collect-or-strings): ...these. (rx--char-alt-union, rx--intersection-intervals) (rx--reduce-to-char-alt, rx--optimise-or-args) (rx--translate-char-alt, rx--human-readable): New. (rx--translate-or, rx--translate-not, rx--translate-intersection): Rewrite. (rx--charset-p, rx--intervals-to-alt, rx--charset-intervals) (rx--charset-union, rx--charset-intersection, rx--charset-all) (rx--translate-union): Remove. (rx--generate-alt): Decide whether to generate a negated character alternative. (rx--complement-intervals, rx--intersect-intervals) (rx--union-intervals): Rename to... (rx--interval-set-complement, rx--interval-set-intersection) (rx--interval-set-union): ...these. (rx--translate-symbol, rx--translate-form): Refactor extension processing. Handle synthetic `rx--char-alt` form. * test/lisp/emacs-lisp/rx-tests.el (rx-or, rx-char-any-raw-byte) (rx-any, rx-charset-or): Adapt to changes and extend. * test/lisp/emacs-lisp/rx-tests.el (rx--complement-intervals) (rx--union-intervals, rx--intersect-intervals): Rename to... (rx--interval-set-complement, rx--interval-set-union) (rx--interval-set-intersection): ...these. diff --git a/lisp/emacs-lisp/rx.el b/lisp/emacs-lisp/rx.el index d46d0ca5a98..aa1174ea08b 100644 --- a/lisp/emacs-lisp/rx.el +++ b/lisp/emacs-lisp/rx.el @@ -161,27 +161,23 @@ rx--lookup-def (or (cdr (assq name rx--local-definitions)) (get name 'rx-definition))) -(defun rx--expand-def (form) - "FORM expanded (once) if a user-defined construct; otherwise nil." - (cond ((symbolp form) - (let ((def (rx--lookup-def form))) - (and def - (if (cdr def) - (error "Not an `rx' symbol definition: %s" form) - (car def))))) - ((and (consp form) (symbolp (car form))) - (let* ((op (car form)) - (def (rx--lookup-def op))) +(defun rx--expand-def-form (form) + "List FORM expanded (once) if a user-defined construct; otherwise nil." + (let ((op (car form))) + (and (symbolp op) + (let ((def (rx--lookup-def op))) (and def (if (cdr def) - (rx--expand-template - op (cdr form) (nth 0 def) (nth 1 def)) + (rx--expand-template op (cdr form) (nth 0 def) (nth 1 def)) (error "Not an `rx' form definition: %s" op))))))) -;; TODO: Additions to consider: -;; - A construct like `or' but without the match order guarantee, -;; maybe `unordered-or'. Useful for composition or generation of -;; alternatives; permits more effective use of regexp-opt. +(defun rx--expand-def-symbol (symbol) + "SYM expanded (once) if a user-defined name; otherwise nil." + (let ((def (rx--lookup-def symbol))) + (and def + (if (cdr def) + (error "Not an `rx' symbol definition: %s" symbol) + (car def))))) (defun rx--translate-symbol (sym) "Translate an rx symbol. Return (REGEXP . PRECEDENCE)." @@ -208,22 +204,13 @@ rx--translate-symbol ((let ((class (cdr (assq sym rx--char-classes)))) (and class (cons (list (concat "[[:" (symbol-name class) ":]]")) t)))) - ((let ((expanded (rx--expand-def sym))) + ((let ((expanded (rx--expand-def-symbol sym))) (and expanded (rx--translate expanded)))) ;; For compatibility with old rx. ((let ((entry (assq sym rx-constituents))) - (and (progn - (while (and entry (not (stringp (cdr entry)))) - (setq entry - (if (symbolp (cdr entry)) - ;; Alias for another entry. - (assq (cdr entry) rx-constituents) - ;; Wrong type, try further down the list. - (assq (car entry) - (cdr (memq entry rx-constituents)))))) - entry) - (cons (list (cdr entry)) nil)))) + (and entry (rx--translate-compat-symbol-entry entry)))) + (t (error "Unknown rx symbol `%s'" sym)))))) (defun rx--enclose (left-str rexp right-str) @@ -289,83 +276,225 @@ rx--foldl (setq l (cdr l))) x) -(defun rx--normalise-or-arg (form) - "Normalize the `or' argument FORM. -Characters become strings, user-definitions and `eval' forms are expanded, -and `or' forms are normalized recursively." - (cond ((characterp form) +;; FIXME: flatten nested `or' patterns when performing char-pattern combining. +;; The only reason for not flattening is to ensure regexp-opt processing +;; (which we do for entire `or' patterns, not subsequences), but we +;; obviously want to translate +;; (or "a" space (or "b" (+ nonl) word) "c") +;; -> (or (in "ab" space) (+ nonl) (in "c" word)) + +;; FIXME: normalise `seq', both the construct and implicit sequences, +;; so that they are flattened, adjacent strings concatenated, and +;; empty strings removed. That would give more opportunities for regexp-opt: +;; (or "a" (seq "ab" (seq "c" "d") "")) -> (or "a" "abcd") + +;; FIXME: Since `rx--normalise-char-pattern' recurses through `or', `not' and +;; `intersection', we may end up normalising subtrees multiple times +;; which wastes time (but should be idempotent). +;; One way to avoid this is to aggressively normalise the entire tree +;; before translating anything at all, but we must then recurse through +;; all constructs and probably copy them. +;; Such normalisation could normalise synonyms, eliminate `minimal-match' +;; and `maximal-match' and convert affected `1+' to either `+' or `+?' etc. +;; We would also consolidate the user-def lookup, both modern and legacy, +;; in one place. + +(defun rx--normalise-char-pattern (form) + "Normalize FORM as a pattern matching a single-character. +Characters become strings, `any' forms and character classes become +`rx--char-alt' forms, user-definitions and `eval' forms are expanded, +and `or', `not' and `intersection' forms are normalized recursively. + +A `rx--char-alt' form is shaped (rx--char-alt INTERVALS . CLASSES) +where INTERVALS is a sorted list of disjoint nonadjacent intervals, +each a cons of characters, and CLASSES an unordered list of unique +name-normalised character classes." + (defvar rx--builtin-forms) + (defvar rx--builtin-symbols) + (cond ((consp form) + (let ((op (car form)) + (body (cdr form))) + (cond ((memq op '(or |)) + ;; Normalise the constructor to `or' and the args recursively. + (cons 'or (mapcar #'rx--normalise-char-pattern body))) + ;; Convert `any' forms and char classes now so that we + ;; don't need to do it later on. + ((memq op '(any in char)) + (cons 'rx--char-alt (rx--parse-any body))) + ((memq op '(not intersection)) + (cons op (mapcar #'rx--normalise-char-pattern body))) + ((eq op 'eval) + (rx--normalise-char-pattern (rx--expand-eval body))) + ((memq op rx--builtin-forms) form) + ((let ((expanded (rx--expand-def-form form))) + (and expanded + (rx--normalise-char-pattern expanded)))) + (t form)))) + ;; FIXME: Should we expand legacy definitions from + ;; `rx-constituents' here as well? + ((symbolp form) + (cond ((let ((class (assq form rx--char-classes))) + (and class + `(rx--char-alt nil . (,(cdr class)))))) + ((memq form rx--builtin-symbols) form) + ((let ((expanded (rx--expand-def-symbol form))) + (and expanded + (rx--normalise-char-pattern expanded)))) + (t form))) + ((characterp form) (char-to-string form)) - ((and (consp form) (memq (car form) '(or |))) - (cons (car form) (mapcar #'rx--normalise-or-arg (cdr form)))) - ((and (consp form) (eq (car form) 'eval)) - (rx--normalise-or-arg (rx--expand-eval (cdr form)))) - (t - (let ((expanded (rx--expand-def form))) - (if expanded - (rx--normalise-or-arg expanded) - form))))) - -(defun rx--all-string-or-args (body) - "If BODY only consists of strings or such `or' forms, return all the strings. -Otherwise throw `rx--nonstring'." + (t form))) + +(defun rx--char-alt-union (a b) + "Union of the (INTERVALS . CLASSES) pairs A and B." + (let* ((a-cl (cdr a)) + (b-cl (cdr b)) + (classes (if (and a-cl b-cl) + (let ((acc a-cl)) + (dolist (c b-cl) + (unless (memq c a-cl) + (push c acc))) + acc) + (or a-cl b-cl)))) + (cons (rx--interval-set-union (car a) (car b)) classes))) + +(defun rx--intersection-intervals (forms) + "Intersection of the normalised FORMS, as an interval set." + (rx--foldl #'rx--interval-set-intersection '((0 . #x3fffff)) + (mapcar (lambda (x) + (let ((char (rx--reduce-to-char-alt x))) + (if (and char (null (cdr char))) + (car char) + (error "Cannot be used in rx intersection: %S" + (rx--human-readable x))))) + forms))) + +(defun rx--reduce-to-char-alt (form) + "Transform FORM into (INTERVALS . CLASSES) or nil if not possible. +Process `or', `intersection' and `not'. +FORM must be normalised (from `rx--normalise-char-pattern')." + (cond + ((stringp form) + (and (= (length form) 1) + (let ((c (aref form 0))) + (list (list (cons c c)))))) + ((consp form) + (let ((head (car form))) + (cond + ;; FIXME: Transform `digit', `xdigit', `cntrl', `ascii', `nonascii' + ;; to ranges? That would allow them to be negated and intersected. + ((eq head 'rx--char-alt) (cdr form)) + ((eq head 'not) + (unless (= (length form) 2) + (error "rx `not' form takes exactly one argument")) + (let ((arg (rx--reduce-to-char-alt (cadr form)))) + ;; Only interval sets without classes are closed under complement. + (and arg (null (cdr arg)) + (list (rx--interval-set-complement (car arg)))))) + ((eq head 'or) + (let ((args (cdr form))) + (let ((acc '(nil))) ; union identity + (while (and args + (let ((char (rx--reduce-to-char-alt (car args)))) + (setq acc (and char (rx--char-alt-union acc char))))) + (setq args (cdr args))) + acc))) + ((eq head 'intersection) + (list (rx--intersection-intervals (cdr form)))) + ))) + ((memq form '(nonl not-newline any)) + '(((0 . 9) (11 . #x3fffff)))) + ((memq form '(anychar anything)) + '(((0 . #x3fffff)))) + ;; FIXME: A better handling of `unmatchable' would be: + ;; * (seq ... unmatchable ...) -> unmatchable + ;; * any or-pattern branch that is `unmatchable' is deleted + ;; * (REPEAT unmatchable) -> "", if REPEAT accepts 0 repetitions + ;; * (REPEAT unmatchable) -> unmatchable, otherwise + ;; if it's worth the trouble (probably not). + ((eq form 'unmatchable) + '(nil)) + )) + +(defun rx--optimise-or-args (args) + "Optimise `or' arguments. Return a new rx form. +Each element of ARGS should have been normalised using +`rx--normalise-char-pattern'." + (if (null args) + ;; No arguments. + '(rx--char-alt nil . nil) ; FIXME: not `unmatchable'? + ;; Join consecutive single-char branches into a char alt where possible. + ;; Ideally we should collect all single-char branches but that might + ;; alter matching order in some cases. + (let ((branches nil) + (prev-char nil)) + (while args + (let* ((item (car args)) + (item-char (rx--reduce-to-char-alt item))) + (if item-char + (setq prev-char (if prev-char + (rx--char-alt-union prev-char item-char) + item-char)) + (when prev-char + (push (cons 'rx--char-alt prev-char) branches) + (setq prev-char nil)) + (push item branches))) + (setq args (cdr args))) + (when prev-char + (push (cons 'rx--char-alt prev-char) branches)) + (if (cdr branches) + (cons 'or (nreverse branches)) + (car branches))))) + +(defun rx--all-string-branches-p (forms) + "Whether FORMS are all strings or `or' forms with the same property." + (rx--every (lambda (x) (or (stringp x) + (and (eq (car-safe x) 'or) + (rx--all-string-branches-p (cdr x))))) + forms)) + +(defun rx--collect-or-strings (forms) + "All strings from FORMS, which are strings or `or' forms." (mapcan (lambda (form) - (cond ((stringp form) (list form)) - ((and (consp form) (memq (car form) '(or |))) - (rx--all-string-or-args (cdr form))) - (t (throw 'rx--nonstring nil)))) - body)) + (if (stringp form) + (list form) + ;; must be an `or' form + (rx--collect-or-strings (cdr form)))) + forms)) + +;; TODO: Write a more general rx-level factoriser to replace +;; `regexp-opt' for our purposes. It would handle non-literals: +;; +;; (or "ab" (: "a" space) "bc" (: "b" (+ digit))) +;; -> (or (: "a" (in "b" space)) (: "b" (or "c" (+ digit)))) +;; +;; As a minor side benefit we would get less useless bracketing. +;; The main problem is how to deal with matching order, which `regexp-opt' +;; alters in its own way. (defun rx--translate-or (body) "Translate an or-pattern of zero or more rx items. Return (REGEXP . PRECEDENCE)." - ;; FIXME: Possible improvements: - ;; - ;; - Flatten sub-patterns first: (or (or A B) (or C D)) -> (or A B C D) - ;; Then call regexp-opt on runs of string arguments. Example: - ;; (or (+ digit) "CHARLIE" "CHAN" (+ blank)) - ;; -> (or (+ digit) (or "CHARLIE" "CHAN") (+ blank)) - ;; - ;; - Optimize single-character alternatives better: - ;; * classes: space, alpha, ... - ;; * (syntax S), for some S (whitespace, word) - ;; so that (or "@" "%" digit (any "A-Z" space) (syntax word)) - ;; -> (any "@" "%" digit "A-Z" space word) - ;; -> "[A-Z@%[:digit:][:space:][:word:]]" (cond ((null body) ; No items: a never-matching regexp. (rx--empty)) ((null (cdr body)) ; Single item. (rx--translate (car body))) (t - (let* ((args (mapcar #'rx--normalise-or-arg body)) - (all-strings (catch 'rx--nonstring (rx--all-string-or-args args)))) - (cond - (all-strings ; Only strings. - (cons (list (regexp-opt all-strings nil)) - t)) - ((rx--every #'rx--charset-p args) ; All charsets. - (rx--translate-union nil args)) - (t - (cons (append (car (rx--translate (car args))) - (mapcan (lambda (item) - (cons "\\|" (car (rx--translate item)))) - (cdr args))) - nil))))))) - -(defun rx--charset-p (form) - "Whether FORM looks like a charset, only consisting of character intervals -and set operations." - (or (and (consp form) - (or (and (memq (car form) '(any in char)) - (rx--every (lambda (x) (not (symbolp x))) (cdr form))) - (and (memq (car form) '(not or | intersection)) - (rx--every #'rx--charset-p (cdr form))))) - (characterp form) - (and (stringp form) (= (length form) 1)) - (and (or (symbolp form) (consp form)) - (let ((expanded (rx--expand-def form))) - (and expanded - (rx--charset-p expanded)))))) + (let ((args (mapcar #'rx--normalise-char-pattern body))) + (if (rx--all-string-branches-p args) + ;; All branches are strings: use `regexp-opt'. + (cons (list (regexp-opt (rx--collect-or-strings args) nil)) + t) + (let ((form (rx--optimise-or-args args))) + (if (eq (car-safe form) 'or) + (let ((branches (cdr form))) + (cons (append (car (rx--translate (car branches))) + (mapcan (lambda (item) + (cons "\\|" (car (rx--translate item)))) + (cdr branches))) + nil)) + (rx--translate form)))))))) (defun rx--string-to-intervals (str) "Decode STR as intervals: A-Z becomes (?A . ?Z), and the single @@ -420,7 +549,7 @@ rx--condense-intervals (defun rx--parse-any (body) "Parse arguments of an (any ...) construct. Return (INTERVALS . CLASSES), where INTERVALS is a sorted list of -disjoint intervals (each a cons of chars), and CLASSES +disjoint nonadjacent intervals (each a cons of chars), and CLASSES a list of named character classes in the order they occur in BODY." (let ((classes nil) (strings nil) @@ -447,7 +576,7 @@ rx--parse-any (sort (append conses (mapcan #'rx--string-to-intervals strings)) #'car-less-than-car)) - (reverse classes)))) + (nreverse classes)))) (defun rx--generate-alt (negated intervals classes) "Generate a character alternative. Return (REGEXP . PRECEDENCE). @@ -456,6 +585,19 @@ rx--generate-alt classes." ;; No, this is not pretty code. You try doing it in a way that is both ;; elegant and efficient. Or just one of the two. I dare you. + + ;; Detect whether the interval set is better described in + ;; complemented form. This is not just a matter of aesthetics: any + ;; range that straddles the char-raw boundary will be mutilated by the + ;; regexp engine. Ranges from ASCII to raw bytes will exclude the + ;; all non-ASCII non-raw bytes, and ranges from non-ASCII Unicode + ;; to raw bytes are ignored. + (unless (or classes + ;; Any interval set covering #x3fff7f should be negated. + (rx--every (lambda (iv) (not (<= (car iv) #x3fff7f (cdr iv)))) + intervals)) + (setq negated (not negated)) + (setq intervals (rx--interval-set-complement intervals))) (cond ;; Single character. ((and intervals (eq (caar intervals) (cdar intervals)) @@ -547,28 +689,18 @@ rx--generate-alt "]")) t))))) +(defun rx--translate-char-alt (negated body) + "Translate a (rx--char-alt ...) construct. Return (REGEXP . PRECEDENCE). +If NEGATED, negate the sense." + (rx--generate-alt negated (car body) (cdr body))) + (defun rx--translate-any (negated body) "Translate an (any ...) construct. Return (REGEXP . PRECEDENCE). If NEGATED, negate the sense." (let ((parsed (rx--parse-any body))) (rx--generate-alt negated (car parsed) (cdr parsed)))) -(defun rx--intervals-to-alt (negated intervals) - "Generate a character alternative from an interval set. -Return (REGEXP . PRECEDENCE). -INTERVALS is a sorted list of disjoint intervals. -If NEGATED, negate the sense." - ;; Detect whether the interval set is better described in - ;; complemented form. This is not just a matter of aesthetics: any - ;; range from ASCII to raw bytes will automatically exclude the - ;; entire non-ASCII Unicode range by the regexp engine. - (if (rx--every (lambda (iv) (not (<= (car iv) #x3ffeff (cdr iv)))) - intervals) - (rx--generate-alt negated intervals nil) - (rx--generate-alt - (not negated) (rx--complement-intervals intervals) nil))) - -;; FIXME: Consider turning `not' into a variadic operator, following SRE: +;; TODO: Consider turning `not' into a variadic operator, following SRE: ;; (not A B) = (not (or A B)) = (intersection (not A) (not B)), and ;; (not) = anychar. ;; Maybe allow singleton characters as arguments. @@ -578,43 +710,27 @@ rx--translate-not If NEGATED, negate the sense (thus making it positive)." (unless (and body (null (cdr body))) (error "rx `not' form takes exactly one argument")) - (let ((arg (car body))) - (cond - ((and (consp arg) - (pcase (car arg) - ((or 'any 'in 'char) - (rx--translate-any (not negated) (cdr arg))) - ('syntax - (rx--translate-syntax (not negated) (cdr arg))) - ('category - (rx--translate-category (not negated) (cdr arg))) - ('not - (rx--translate-not (not negated) (cdr arg))) - ((or 'or '|) - (rx--translate-union (not negated) (cdr arg))) - ('intersection - (rx--translate-intersection (not negated) (cdr arg)))))) - ((let ((class (cdr (assq arg rx--char-classes)))) - (and class - (rx--generate-alt (not negated) nil (list class))))) - ((eq arg 'word-boundary) - (rx--translate-symbol - (if negated 'word-boundary 'not-word-boundary))) - ((characterp arg) - (rx--generate-alt (not negated) (list (cons arg arg)) nil)) - ((and (stringp arg) (= (length arg) 1)) - (let ((char (string-to-char arg))) - (rx--generate-alt (not negated) (list (cons char char)) nil))) - ((let ((expanded (rx--expand-def arg))) - (and expanded - (rx--translate-not negated (list expanded))))) - (t (error "Illegal argument to rx `not': %S" arg))))) - -(defun rx--complement-intervals (intervals) - "Complement of the interval list INTERVALS." + (let ((arg (rx--normalise-char-pattern (car body)))) + (pcase arg + (`(not . ,args) + (rx--translate-not (not negated) args)) + (`(syntax . ,args) + (rx--translate-syntax (not negated) args)) + (`(category . ,args) + (rx--translate-category (not negated) args)) + ('word-boundary ; legacy syntax + (rx--translate-symbol (if negated 'word-boundary 'not-word-boundary))) + (_ (let ((char (rx--reduce-to-char-alt arg))) + (if char + (rx--generate-alt (not negated) (car char) (cdr char)) + (error "Illegal argument to rx `not': %S" + (rx--human-readable arg)))))))) + +(defun rx--interval-set-complement (ivs) + "Complement of the interval set IVS." (let ((compl nil) (c 0)) - (dolist (iv intervals) + (dolist (iv ivs) (when (< c (car iv)) (push (cons c (1- (car iv))) compl)) (setq c (1+ (cdr iv)))) @@ -622,8 +738,8 @@ rx--complement-intervals (push (cons c (max-char)) compl)) (nreverse compl))) -(defun rx--intersect-intervals (ivs-a ivs-b) - "Intersection of the interval lists IVS-A and IVS-B." +(defun rx--interval-set-intersection (ivs-a ivs-b) + "Intersection of the interval sets IVS-A and IVS-B." (let ((isect nil)) (while (and ivs-a ivs-b) (let ((a (car ivs-a)) @@ -645,8 +761,8 @@ rx--intersect-intervals ivs-a))))))) (nreverse isect))) -(defun rx--union-intervals (ivs-a ivs-b) - "Union of the interval lists IVS-A and IVS-B." +(defun rx--interval-set-union (ivs-a ivs-b) + "Union of the interval sets IVS-A and IVS-B." (let ((union nil)) (while (and ivs-a ivs-b) (let ((a (car ivs-a)) @@ -670,53 +786,66 @@ rx--union-intervals ivs-a)))))) (nconc (nreverse union) (or ivs-a ivs-b)))) -(defun rx--charset-intervals (charset) - "Return a sorted list of non-adjacent disjoint intervals from CHARSET. -CHARSET is any expression allowed in a character set expression: -characters, single-char strings, `any' forms (no classes permitted), -or `not', `or' or `intersection' forms whose arguments are charsets." - (pcase charset - (`(,(or 'any 'in 'char) . ,body) - (let ((parsed (rx--parse-any body))) - (when (cdr parsed) - (error - "Character class not permitted in set operations: %S" - (cadr parsed))) - (car parsed))) - (`(not ,x) (rx--complement-intervals (rx--charset-intervals x))) - (`(,(or 'or '|) . ,body) (rx--charset-union body)) - (`(intersection . ,body) (rx--charset-intersection body)) - ((pred characterp) - (list (cons charset charset))) - ((guard (and (stringp charset) (= (length charset) 1))) - (let ((char (string-to-char charset))) - (list (cons char char)))) - (_ (let ((expanded (rx--expand-def charset))) - (if expanded - (rx--charset-intervals expanded) - (error "Bad character set: %S" charset)))))) - -(defun rx--charset-union (charsets) - "Union of CHARSETS, as a set of intervals." - (rx--foldl #'rx--union-intervals nil - (mapcar #'rx--charset-intervals charsets))) - -(defconst rx--charset-all (list (cons 0 (max-char)))) - -(defun rx--charset-intersection (charsets) - "Intersection of CHARSETS, as a set of intervals." - (rx--foldl #'rx--intersect-intervals rx--charset-all - (mapcar #'rx--charset-intervals charsets))) - -(defun rx--translate-union (negated body) - "Translate an (or ...) construct of charsets. Return (REGEXP . PRECEDENCE). -If NEGATED, negate the sense." - (rx--intervals-to-alt negated (rx--charset-union body))) +(defun rx--human-readable (form) + "Turn FORM into something that is more human-readable, for error messages." + ;; FIXME: Should we produce a string instead? + ;; That way we wouldn't have problems with ? and ??, and we could escape + ;; single chars. + ;; We could steal `xr--rx-to-string' and just file off the serials. + (let ((recurse (lambda (op skip) + (cons op (append (take skip (cdr form)) + (mapcar #'rx--human-readable + (nthcdr skip (cdr form)))))))) + (pcase form + ;; strings are more readable than numbers for single chars + ((pred characterp) (char-to-string form)) + ;; resugar `rx--char-alt' + (`(rx--char-alt ((,c . ,c)) . nil) + (char-to-string form)) + (`(rx--char-alt nil . (,class)) + class) + ;; TODO: render in complemented form if more readable that way? + (`(rx--char-alt ,ivs . ,classes) + (let ((strings (mapcan (lambda (iv) + (let ((beg (car iv)) + (end (cdr iv))) + (cond + ;; single char + ((eq beg end) + (list (string beg))) + ;; two chars + ((eq end (1+ beg)) + (list (string beg) (string end))) + ;; first char is hyphen + ((eq beg ?-) + (cons (string "-") + (if (eq end (+ ?- 2)) + (list (string (1+ ?-) end)) + (list (string (1+ ?-) ?- end))))) + ;; other range + (t (list (string beg ?- end)))))) + ivs))) + `(any ,@strings ,@classes))) + ;; avoid numbers as ops + (`(? . ,_) (funcall recurse '\? 0)) + (`(?? . ,_) (funcall recurse '\?? 0)) + ;; recurse on arguments + (`(repeat ,_ ,_) (funcall recurse (car form) 1)) + (`(,(or '** 'repeat) . ,_) (funcall recurse (car form) 2)) + (`(,(or '= '>= 'group-n 'submatch-n) . ,_) (funcall recurse (car form) 1)) + (`(,(or 'backref 'syntax 'not-syntax 'category + 'eval 'regex 'regexp 'literal) + . ,_) + form) + (`(,_ . ,_) (funcall recurse (car form) 0)) + (_ form)))) (defun rx--translate-intersection (negated body) "Translate an (intersection ...) construct. Return (REGEXP . PRECEDENCE). If NEGATED, negate the sense." - (rx--intervals-to-alt negated (rx--charset-intersection body))) + (rx--generate-alt negated (rx--intersection-intervals + (mapcar #'rx--normalise-char-pattern body)) + nil)) (defun rx--atomic-regexp (item) "ITEM is (REGEXP . PRECEDENCE); return a regexp of precedence t." @@ -1006,6 +1135,36 @@ rx--translate-compat-form (error "The `%s' form did not expand to a string" (car form))) (cons (list regexp) nil)))) +(defun rx--translate-compat-symbol-entry (entry) + "Translate a compatibility symbol definition for ENTRY. +Return (REGEXP . PRECEDENCE) or nil if none." + (and (progn + (while (and entry (not (stringp (cdr entry)))) + (setq entry + (if (symbolp (cdr entry)) + ;; Alias for another entry. + (assq (cdr entry) rx-constituents) + ;; Wrong type, try further down the list. + (assq (car entry) + (cdr (memq entry rx-constituents)))))) + entry) + (cons (list (cdr entry)) nil))) + +(defun rx--translate-compat-form-entry (orig-form entry) + "Translate a compatibility ORIG-FORM definition for ENTRY. +Return (REGEXP . PRECEDENCE) or nil if none." + (and (progn + (while (and entry (not (consp (cdr entry)))) + (setq entry + (if (symbolp (cdr entry)) + ;; Alias for another entry. + (assq (cdr entry) rx-constituents) + ;; Wrong type, try further down the list. + (assq (car entry) + (cdr (memq entry rx-constituents)))))) + entry) + (rx--translate-compat-form (cdr entry) orig-form))) + (defun rx--substitute (bindings form) "Substitute BINDINGS in FORM. BINDINGS is an alist of (NAME . VALUES) where VALUES is a list to splice into FORM wherever NAME occurs. @@ -1101,6 +1260,7 @@ rx--translate-form ((or 'seq : 'and 'sequence) (rx--translate-seq body)) ((or 'or '|) (rx--translate-or body)) ((or 'any 'in 'char) (rx--translate-any nil body)) + ('rx--char-alt (rx--translate-char-alt nil body)) ('not-char (rx--translate-any t body)) ('not (rx--translate-not nil body)) ('intersection (rx--translate-intersection nil body)) @@ -1141,23 +1301,13 @@ rx--translate-form (cond ((not (symbolp op)) (error "Bad rx operator `%S'" op)) - ((let ((expanded (rx--expand-def form))) + ((let ((expanded (rx--expand-def-form form))) (and expanded (rx--translate expanded)))) ;; For compatibility with old rx. ((let ((entry (assq op rx-constituents))) - (and (progn - (while (and entry (not (consp (cdr entry)))) - (setq entry - (if (symbolp (cdr entry)) - ;; Alias for another entry. - (assq (cdr entry) rx-constituents) - ;; Wrong type, try further down the list. - (assq (car entry) - (cdr (memq entry rx-constituents)))))) - entry) - (rx--translate-compat-form (cdr entry) form)))) + (and entry (rx--translate-compat-form-entry form entry)))) (t (error "Unknown rx form `%s'" op))))))) diff --git a/test/lisp/emacs-lisp/rx-tests.el b/test/lisp/emacs-lisp/rx-tests.el index ae83f28d9c4..e773ddf158e 100644 --- a/test/lisp/emacs-lisp/rx-tests.el +++ b/test/lisp/emacs-lisp/rx-tests.el @@ -41,19 +41,31 @@ rx-seq (should (equal (rx "" (or "ab" nonl) "") "ab\\|."))) +;; FIXME: Extend tests for `or', `not' etc to cover char pattern combination, +;; including (syntax whitespace) and (syntax word). + (ert-deftest rx-or () - (should (equal (rx (or "ab" (| "c" nonl) "de")) - "ab\\|c\\|.\\|de")) + (should (equal (rx (or "ab" (| "cd" nonl) "de")) + "ab\\|cd\\|.\\|de")) (should (equal (rx (or "ab" "abc" ?a)) "\\(?:a\\(?:bc?\\)?\\)")) (should (equal (rx (or "ab" (| (or "abcd" "abcde")) (or "a" "abc"))) "\\(?:a\\(?:b\\(?:c\\(?:de?\\)?\\)?\\)?\\)")) (should (equal (rx (or "a" (eval (string ?a ?b)))) "\\(?:ab?\\)")) + (should (equal (rx (| nonl "ac") (| "bd" blank)) + "\\(?:.\\|ac\\)\\(?:bd\\|[[:blank:]]\\)")) (should (equal (rx (| nonl "a") (| "b" blank)) - "\\(?:.\\|a\\)\\(?:b\\|[[:blank:]]\\)")) + ".[b[:blank:]]")) (should (equal (rx (|)) - "\\`a\\`"))) + "\\`a\\`")) + (should (equal (rx (or "a" (not anychar) punct ?c "b" (not (not ?d)))) + "[a-d[:punct:]]")) + (should (equal (rx (or nonl ?\n)) + "[^z-a]")) + (should (equal (rx (or "ab" "a" "b" blank (syntax whitespace) word "z")) + "ab\\|[ab[:blank:]]\\|\\s-\\|[z[:word:]]")) + ) (ert-deftest rx-def-in-or () (rx-let ((a b) @@ -101,14 +113,18 @@ rx-char-any-raw-byte "[\177ÿ\200-\377]")) ;; Range between normal chars and raw bytes: must be split to be parsed ;; correctly by the Emacs regexp engine. - (should (equal - (rx (any (0 . #x3fffff)) (any (?G . #x3fff9a)) (any (?Ü . #x3ffff2))) - "[\0-\x3fff7f\x80-\xff][G-\x3fff7f\x80-\x9a][Ü-\x3fff7f\x80-\xf2]")) + (should (equal (rx (any (0 . #x3fffff) word) (any (?G . #x3fff9a) word) + (any (?Ü . #x3ffff2) word)) + (concat "[\0-\x3fff7f\x80-\xff[:word:]]" + "[G-\x3fff7f\x80-\x9a[:word:]]" + "[Ü-\x3fff7f\x80-\xf2[:word:]]"))) ;; As above but with ranges in string form. For historical reasons, ;; we special-case ASCII-to-raw ranges to exclude non-ASCII unicode. - (should (equal - (rx (any "\x00-\xff") (any "G-\x9a") (any "Ü-\xf2")) - "[\0-\x7f\x80-\xff][G-\x7f\x80-\x9a][Ü-\x3fff7f\x80-\xf2]"))) + (should (equal (rx (any "\x00-\xff" alpha) (any "G-\x9a" alpha) + (any "Ü-\xf2" alpha)) + (concat "[\0-\x7f\x80-\xff[:alpha:]]" + "[G-\x7f\x80-\x9a[:alpha:]]" + "[Ü-\x3fff7f\x80-\xf2[:alpha:]]")))) (ert-deftest rx-any () (should (equal (rx (any ?A (?C . ?D) "F-H" "J-L" "M" "N-P" "Q" "RS")) @@ -175,7 +191,10 @@ rx-any "[a[:space:][:digit:]]")) (should (equal (rx (not "\n") (not ?\n) (not (any "\n")) (not-char ?\n) (| (not (in "a\n")) (not (char ?\n (?b . ?b))))) - "....."))) + ".....")) + (should (equal (rx (or (in "g-k") (in "a-f") (or ?r (in "i-m" "n-q")))) + "[a-r]")) + ) (ert-deftest rx-pcase () (should (equal (pcase "i18n" ((rx (let x (+ digit))) (list 'ok x))) @@ -392,7 +411,16 @@ rx-charset-or (should (equal (rx (or (not (in "abc")) (not (char "bcd")))) "[^bc]")) (should (equal (rx (or "x" (? "yz"))) - "x\\|\\(?:yz\\)?"))) + "x\\|\\(?:yz\\)?")) + (should (equal (rx (or anychar (not anychar))) + "[^z-a]")) + (should (equal (rx (or (not (in "a-p")) (not (in "k-u")))) + "[^k-p]")) + (should (equal (rx (or (not (in "a-p")) word (not (in "k-u")))) + "[\0-jq-\x3fff7f\x80-\xff[:word:]]")) + (should (equal (rx (or (in "a-f" blank) (in "c-z") blank)) + "[a-z[:blank:]]")) + ) (ert-deftest rx-def-in-charset-or () (rx-let ((a (any "badc")) @@ -613,52 +641,52 @@ rx-compat ;;; unit tests for internal functions -(ert-deftest rx--complement-intervals () - (should (equal (rx--complement-intervals '()) +(ert-deftest rx--interval-set-complement () + (should (equal (rx--interval-set-complement '()) '((0 . #x3fffff)))) - (should (equal (rx--complement-intervals '((10 . 20) (30 . 40))) + (should (equal (rx--interval-set-complement '((10 . 20) (30 . 40))) '((0 . 9) (21 . 29) (41 . #x3fffff)))) - (should (equal (rx--complement-intervals '((0 . #x3fffff))) + (should (equal (rx--interval-set-complement '((0 . #x3fffff))) '())) - (should (equal (rx--complement-intervals + (should (equal (rx--interval-set-complement '((0 . 10) (20 . 20) (30 . #x3fffff))) '((11 . 19) (21 . 29))))) -(ert-deftest rx--union-intervals () - (should (equal (rx--union-intervals '() '()) '())) - (should (equal (rx--union-intervals '() '((10 . 20) (30 . 40))) +(ert-deftest rx--interval-set-union () + (should (equal (rx--interval-set-union '() '()) '())) + (should (equal (rx--interval-set-union '() '((10 . 20) (30 . 40))) '((10 . 20) (30 . 40)))) - (should (equal (rx--union-intervals '((10 . 20) (30 . 40)) '()) + (should (equal (rx--interval-set-union '((10 . 20) (30 . 40)) '()) '((10 . 20) (30 . 40)))) - (should (equal (rx--union-intervals '((5 . 15) (18 . 24) (32 . 40)) + (should (equal (rx--interval-set-union '((5 . 15) (18 . 24) (32 . 40)) '((10 . 20) (30 . 40) (50 . 60))) '((5 . 24) (30 . 40) (50 . 60)))) - (should (equal (rx--union-intervals '((10 . 20) (30 . 40) (50 . 60)) + (should (equal (rx--interval-set-union '((10 . 20) (30 . 40) (50 . 60)) '((0 . 9) (21 . 29) (41 . 50))) '((0 . 60)))) - (should (equal (rx--union-intervals '((10 . 20) (30 . 40)) + (should (equal (rx--interval-set-union '((10 . 20) (30 . 40)) '((12 . 18) (28 . 42))) '((10 . 20) (28 . 42)))) - (should (equal (rx--union-intervals '((10 . 20) (30 . 40)) + (should (equal (rx--interval-set-union '((10 . 20) (30 . 40)) '((0 . #x3fffff))) '((0 . #x3fffff))))) -(ert-deftest rx--intersect-intervals () - (should (equal (rx--intersect-intervals '() '()) '())) - (should (equal (rx--intersect-intervals '() '((10 . 20) (30 . 40))) +(ert-deftest rx--interval-set-intersection () + (should (equal (rx--interval-set-intersection '() '()) '())) + (should (equal (rx--interval-set-intersection '() '((10 . 20) (30 . 40))) '())) - (should (equal (rx--intersect-intervals '((10 . 20) (30 . 40)) '()) + (should (equal (rx--interval-set-intersection '((10 . 20) (30 . 40)) '()) '())) - (should (equal (rx--intersect-intervals '((5 . 15) (18 . 24) (32 . 40)) + (should (equal (rx--interval-set-intersection '((5 . 15) (18 . 24) (32 . 40)) '((10 . 20) (30 . 40) (50 . 60))) '((10 . 15) (18 . 20) (32 . 40)))) - (should (equal (rx--intersect-intervals '((10 . 20) (30 . 40) (50 . 60)) + (should (equal (rx--interval-set-intersection '((10 . 20) (30 . 40) (50 . 60)) '((0 . 9) (21 . 29) (41 . 50))) '((50 . 50)))) - (should (equal (rx--intersect-intervals '((10 . 20) (30 . 40)) + (should (equal (rx--interval-set-intersection '((10 . 20) (30 . 40)) '((12 . 18) (28 . 42))) '((12 . 18) (30 . 40)))) - (should (equal (rx--intersect-intervals '((10 . 20) (30 . 40)) + (should (equal (rx--interval-set-intersection '((10 . 20) (30 . 40)) '((0 . #x3fffff))) '((10 . 20) (30 . 40))))) commit 7b1eb9d753bed5f2891d10efe164eb40ed3ab4fc Author: Eli Zaretskii Date: Sat Aug 12 18:35:24 2023 +0300 ; * lisp/net/dictionary.el (dictionary-search): Fix last change. diff --git a/lisp/net/dictionary.el b/lisp/net/dictionary.el index 8eb5c142f08..f4a381db75d 100644 --- a/lisp/net/dictionary.el +++ b/lisp/net/dictionary.el @@ -1209,11 +1209,12 @@ dictionary-read-word-default ;;;###autoload (defun dictionary-search (word &optional dictionary) - "Prompt for a word WORD to search for in all dictionaries. -Presents the selection or word at point as default input. + "Search for WORD in all the known dictionaries. +Interactively, prompt for WORD, and offer the word at point as default. -With prefix argument DICTIONARY, prompt for a dictionary and -restrict the search to only that one." +Optional argument DICTIONARY means restrict the search to only +that one dictionary. Interactively, with prefix argument, +prompt for DICTIONARY." (interactive (let ((dict (if current-prefix-arg commit d3dae88e6cc8118c875957ba0347be9599014b34 Author: Stefan Kangas Date: Sat Aug 12 16:40:10 2023 +0200 Improve dictionary-mode documentation * lisp/net/dictionary.el (dictionary-mode): Improve docstring diff --git a/lisp/net/dictionary.el b/lisp/net/dictionary.el index c1bbf04d4f2..8eb5c142f08 100644 --- a/lisp/net/dictionary.el +++ b/lisp/net/dictionary.el @@ -23,11 +23,12 @@ ;;; Commentary: ;; dictionary allows you to interact with dictionary servers. -;; Use M-x customize-group dictionary to modify user settings. +;; +;; Use `M-x customize-group RET dictionary RET' to modify user settings. ;; ;; Main commands for interaction are: -;; M-x dictionary - opens a new dictionary buffer -;; M-x dictionary-search - search for the definition of a word +;; `M-x dictionary' - open a new dictionary buffer +;; `M-x dictionary-search' - search for the definition of a word ;; ;; You can find more information in the README file of the GitHub ;; repository https://github.com/myrkr/dictionary-el @@ -425,23 +426,25 @@ dictionary--last-match ;;;###autoload (define-derived-mode dictionary-mode special-mode "Dictionary" "Mode for searching a dictionary. + This is a mode for searching a dictionary server implementing the protocol defined in RFC 2229. This is a quick reference to this mode describing the default key bindings: \\ -* \\[dictionary-close] close the dictionary buffer -* \\[describe-mode] display this help information -* \\[dictionary-search] ask for a new word to search -* \\[dictionary-lookup-definition] search the word at point -* \\[forward-button] or TAB place point to the next link -* \\[backward-button] or S-TAB place point to the prev link - -* \\[dictionary-match-words] ask for a pattern and list all matching words. -* \\[dictionary-select-dictionary] select the default dictionary -* \\[dictionary-select-strategy] select the default search strategy - -* \\`RET' or \\`' visit that link" + \\[dictionary-close] close the dictionary buffer + \\[describe-mode] display this help + \\[dictionary-search] ask for a new word to search + \\[dictionary-lookup-definition] search for word at point + \\[forward-button] or \\`TAB' move point to the next link + \\[backward-button] or \\`S-TAB' move point to the previous link + + \\[dictionary-match-words] ask for a pattern and list all matching words + \\[dictionary-select-dictionary] select the default dictionary + \\[dictionary-select-strategy] select the default search strategy + + \\`RET' visit link at point + \\`' visit clicked link" (buffer-disable-undo) (setq-local dictionary-data-stack nil) (setq-local dictionary-position-stack nil) @@ -1206,9 +1209,11 @@ dictionary-read-word-default ;;;###autoload (defun dictionary-search (word &optional dictionary) - "Search the WORD in DICTIONARY if given or in all if nil. -It presents the selection or word at point as default input and -allows editing it." + "Prompt for a word WORD to search for in all dictionaries. +Presents the selection or word at point as default input. + +With prefix argument DICTIONARY, prompt for a dictionary and +restrict the search to only that one." (interactive (let ((dict (if current-prefix-arg commit 5892b4db8de4694904f631dd86438926fb9d7ff1 Author: Stefan Kangas Date: Sat Aug 12 16:30:08 2023 +0200 Convert dictionary-mode to define-derived-mode * lisp/net/dictionary.el (dictionary-instances): Delete. (dictionary-mode): Use define-derived-mode. (dictionary--count-mode-buffers): New function. (dictionary-close): Use above new function. diff --git a/lisp/net/dictionary.el b/lisp/net/dictionary.el index d583f8157eb..c1bbf04d4f2 100644 --- a/lisp/net/dictionary.el +++ b/lisp/net/dictionary.el @@ -34,7 +34,6 @@ ;;; Code: -(require 'cl-lib) (require 'custom) (require 'dictionary-connection) (require 'button) @@ -409,10 +408,6 @@ dictionary-connection nil "The current network connection.") -(defvar dictionary-instances - 0 - "The number of open dictionary buffers.") - (defvar dictionary-marker nil "Stores the point position while buffer display.") @@ -428,8 +423,7 @@ dictionary--last-match ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;###autoload -(defun dictionary-mode () - ;; FIXME: Use define-derived-mode. +(define-derived-mode dictionary-mode special-mode "Dictionary" "Mode for searching a dictionary. This is a mode for searching a dictionary server implementing the protocol defined in RFC 2229. @@ -448,27 +442,14 @@ dictionary-mode * \\[dictionary-select-strategy] select the default search strategy * \\`RET' or \\`' visit that link" - - (unless (eq major-mode 'dictionary-mode) - (cl-incf dictionary-instances)) - - (kill-all-local-variables) (buffer-disable-undo) - (use-local-map dictionary-mode-map) - (setq major-mode 'dictionary-mode) - (setq mode-name "Dictionary") - (setq-local dictionary-data-stack nil) (setq-local dictionary-position-stack nil) - (make-local-variable 'dictionary-current-data) (make-local-variable 'dictionary-positions) - (make-local-variable 'dictionary-default-dictionary) (make-local-variable 'dictionary-default-strategy) - - (add-hook 'kill-buffer-hook #'dictionary-close t t) - (run-hooks 'dictionary-mode-hook)) + (add-hook 'kill-buffer-hook #'dictionary-close t t)) ;;;###autoload (defun dictionary () @@ -589,19 +570,28 @@ dictionary-ensure-buffer ;; Dealing with closing the buffer ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(defun dictionary--count-mode-buffers () + "Return the number of buffers that " + (seq-reduce #'+ + (mapcar + (lambda (buf) + (with-current-buffer buf + (if (derived-mode-p 'dictionary-mode) 1 0))) + (buffer-list)) + 0)) + (defun dictionary-close (&rest _ignored) "Close the current dictionary buffer and its connection." (interactive) - (if (eq major-mode 'dictionary-mode) - (progn - (setq major-mode nil) - (if (<= (cl-decf dictionary-instances) 0) - (dictionary-connection-close dictionary-connection)) - (let ((configuration dictionary-window-configuration) - (selected-window dictionary-selected-window)) - (kill-buffer (current-buffer)) - (set-window-configuration configuration) - (select-window selected-window))))) + (when (derived-mode-p 'dictionary-mode) + (setq major-mode nil) + (if (<= (dictionary--count-mode-buffers) 0) + (dictionary-connection-close dictionary-connection)) + (let ((configuration dictionary-window-configuration) + (selected-window dictionary-selected-window)) + (kill-buffer (current-buffer)) + (set-window-configuration configuration) + (select-window selected-window)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Helpful functions commit b6884820963d4a5f15cb771894f3cf9a7fdb5195 Author: Stefan Kangas Date: Sat Aug 12 16:06:10 2023 +0200 Remove dictionary-color-support compat code * lisp/net/dictionary.el (dictionary-color-support): Make obsolete in favor of 'display-color-p'. Update single caller. diff --git a/lisp/net/dictionary.el b/lisp/net/dictionary.el index 8d81b3ec9d8..d583f8157eb 100644 --- a/lisp/net/dictionary.el +++ b/lisp/net/dictionary.el @@ -417,12 +417,6 @@ dictionary-marker nil "Stores the point position while buffer display.") -(defvar dictionary-color-support - (condition-case nil - (display-color-p) - (error nil)) - "Determines if the Emacs has support to display color.") - (defvar dictionary-word-history '() "History list of searched word.") @@ -900,7 +894,7 @@ dictionary-display-word-definition (if (search-forward-regexp regexp nil t) (let ((match-start (match-beginning 2)) (match-end (match-end 2))) - (if dictionary-color-support + (if (display-color-p) ;; Compensate for the replacement (let ((brace-match-length (- (match-end 1) (match-beginning 1)))) @@ -1559,5 +1553,9 @@ dictionary-display-definition-in-help-buffer (match-string 1) dictionary)))))) +(defvar dictionary-color-support (display-color-p) + "Determines if the Emacs has support to display color.") +(make-obsolete-variable 'dictionary-color-support 'display-color-p "30.1") + (provide 'dictionary) ;;; dictionary.el ends here commit 98e7d37451ce6dbe7baf94c190b2394631ae74ab Author: Stefan Kangas Date: Sat Aug 12 15:46:32 2023 +0200 ; Fix typo diff --git a/java/INSTALL b/java/INSTALL index 7ee39a71e9a..fb235af1346 100644 --- a/java/INSTALL +++ b/java/INSTALL @@ -39,7 +39,7 @@ script like so: Replacing the paths in the command line above with: - the path to the `android.jar' headers which come with the Android - SDK. They must correspond to Android version 13 (API level 33.) + SDK. They must correspond to Android version 13 (API level 33). - the path to the C compiler in the Android NDK, for the kind of CPU you are building Emacs to run on. commit 11be475eeebe957ba3a4640e59c9ab41ddbf74b2 Author: Stefan Kangas Date: Sat Aug 12 15:29:09 2023 +0200 Remove compat code for XEmacs' itimer.el * lisp/progmodes/idlwave.el (idlwave-update-routine-info) (idlwave-start-load-rinfo-timer): * lisp/type-break.el (type-break-cancel-function-timers) (type-break-run-at-time): Remove XEmacs compat code. diff --git a/lisp/progmodes/idlwave.el b/lisp/progmodes/idlwave.el index 488e6aa3d2d..7bdaa6b1b6f 100644 --- a/lisp/progmodes/idlwave.el +++ b/lisp/progmodes/idlwave.el @@ -4311,10 +4311,7 @@ idlwave-update-routine-info information updated immediately, leave NO-CONCATENATE nil." (interactive "P\np") ;; Stop any idle processing - (if (or (and (fboundp 'itimerp) - (itimerp idlwave-load-rinfo-idle-timer)) - (and (fboundp 'timerp) - (timerp idlwave-load-rinfo-idle-timer))) + (if (timerp idlwave-load-rinfo-idle-timer) (cancel-timer idlwave-load-rinfo-idle-timer)) (cond ((equal arg '(64)) @@ -4388,10 +4385,7 @@ idlwave-update-routine-info (defvar idlwave-load-rinfo-steps-done (make-vector 6 nil)) (defvar idlwave-load-rinfo-idle-timer nil) (defun idlwave-start-load-rinfo-timer () - (if (or (and (fboundp 'itimerp) - (itimerp idlwave-load-rinfo-idle-timer)) - (and (fboundp 'timerp) - (timerp idlwave-load-rinfo-idle-timer))) + (if (timerp idlwave-load-rinfo-idle-timer) (cancel-timer idlwave-load-rinfo-idle-timer)) (setq idlwave-load-rinfo-steps-done (make-vector 6 nil)) (setq idlwave-load-rinfo-idle-timer nil) diff --git a/lisp/type-break.el b/lisp/type-break.el index 1aa2b9d2997..494ed80c496 100644 --- a/lisp/type-break.el +++ b/lisp/type-break.el @@ -584,13 +584,13 @@ type-break-schedule (type-break-check-post-command-hook) (type-break-cancel-schedule) (type-break-time-warning-schedule time 'reset) - (type-break-run-at-time (max 1 time) nil 'type-break-alarm) + (run-at-time (max 1 time) nil 'type-break-alarm) (setq type-break-time-next-break (type-break-time-sum start (or interval time)))) (defun type-break-cancel-schedule () (type-break-cancel-time-warning-schedule) - (type-break-cancel-function-timers 'type-break-alarm) + (cancel-function-timers 'type-break-alarm) (setq type-break-alarm-p nil) (setq type-break-time-next-break nil)) @@ -621,7 +621,7 @@ type-break-time-warning-schedule ;(let (type-break-current-time-warning-interval) ; (type-break-cancel-time-warning-schedule)) - (type-break-run-at-time (max 1 time) nil 'type-break-time-warning-alarm) + (run-at-time (max 1 time) nil 'type-break-time-warning-alarm) (cond (resetp @@ -631,7 +631,7 @@ type-break-time-warning-schedule (setq type-break-warning-countdown-string-type "seconds")))))))) (defun type-break-cancel-time-warning-schedule () - (type-break-cancel-function-timers 'type-break-time-warning-alarm) + (cancel-function-timers 'type-break-time-warning-alarm) (remove-hook 'type-break-post-command-hook 'type-break-time-warning) (setq type-break-current-time-warning-interval type-break-time-warning-intervals) @@ -983,21 +983,6 @@ type-break-force-mode-line-update (defun type-break-check-post-command-hook () (add-hook 'post-command-hook 'type-break-run-tb-post-command-hook 'append)) - -;;; Timer wrapper functions -;; -;; These shield type-break from variations in the interval timer packages -;; for different versions of Emacs. - -(defun type-break-run-at-time (time repeat function) - (condition-case nil (or (require 'timer) (require 'itimer)) (error nil)) - (run-at-time time repeat function)) - -(defvar timer-dont-exit) -(defun type-break-cancel-function-timers (function) - (let ((timer-dont-exit t)) - (cancel-function-timers function))) - ;;; Demo wrappers @@ -1144,6 +1129,8 @@ type-break-demo-boring (kill-buffer buffer-name)))))) (define-obsolete-function-alias 'timep 'type-break-timep "29.1") +(define-obsolete-function-alias 'type-break-run-at-time #'run-at-time "30.1") +(define-obsolete-function-alias 'type-break-cancel-function-timers #'cancel-function-timers "30.1") (provide 'type-break) commit 6cc9910227212490a451aeb16325a7e27eeb998c Author: Stefan Kangas Date: Sat Aug 12 14:36:59 2023 +0200 Make dired-move-to-filename-regexp obsolete * lisp/dired.el (dired-move-to-filename-regexp): Make Emacs 21 compatibility alias obsolete. diff --git a/lisp/dired.el b/lisp/dired.el index 255d79fa42c..fcbecbf3c08 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -577,9 +577,6 @@ dired-switches-alist "Keeps track of which switches to use for inserted subdirectories. This is an alist of the form (SUBDIR . SWITCHES).") -(defvaralias 'dired-move-to-filename-regexp - 'directory-listing-before-filename-regexp) - (defvar dired-subdir-regexp "^. \\(.+\\)\\(:\\)\n" "Regexp matching a maybe hidden subdirectory line in `ls -lR' output. Subexpression 1 is the subdirectory proper, no trailing colon. @@ -5040,6 +5037,9 @@ dired-click-to-select-mode ;; Redisplay the tool bar. (force-mode-line-update)) +(define-obsolete-variable-alias 'dired-move-to-filename-regexp + 'directory-listing-before-filename-regexp "30.1") + (provide 'dired) (run-hooks 'dired-load-hook) ; for your customizations diff --git a/lisp/files.el b/lisp/files.el index 69c4a33cdeb..52cf03ee085 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -7743,7 +7743,6 @@ get-free-disk-space (if avail (funcall byte-count-to-string-function avail))))) -;; The following expression replaces `dired-move-to-filename-regexp'. (defvar directory-listing-before-filename-regexp (let* ((l "\\([A-Za-z]\\|[^\0-\177]\\)") (l-or-quote "\\([A-Za-z']\\|[^\0-\177]\\)") commit 57c6501270e200c40820562f56b6be4420419138 Merge: 26688e0e9c2 69a1a63ebda Author: Michael Albinus Date: Sat Aug 12 15:17:59 2023 +0200 Merge branch 'master' of git.sv.gnu.org:/srv/git/emacs commit 26688e0e9c2cd74f4ea7866ab08773388aa084c4 Author: Michael Albinus Date: Sat Aug 12 15:17:32 2023 +0200 Remove superfluous check from vc-hooks.el * lisp/vc/vc-hooks.el (vc-registered): Don't check whether `file-name-handler-alist' is bound, the check is superfluous. diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el index 00a7659209e..e75165ea2e9 100644 --- a/lisp/vc/vc-hooks.el +++ b/lisp/vc/vc-hooks.el @@ -321,8 +321,7 @@ vc-registered ((and (file-name-directory file) (string-match vc-ignore-dir-regexp (file-name-directory file))) nil) - ((and (boundp 'file-name-handler-alist) - (setq handler (find-file-name-handler file 'vc-registered))) + ((setq handler (find-file-name-handler file 'vc-registered)) ;; handler should set vc-backend and return t if registered (funcall handler 'vc-registered file)) (t commit 69a1a63ebda282830a75e774f2cedbb84893a109 Author: Po Lu Date: Sat Aug 12 20:58:13 2023 +0800 ; * java/INSTALL (LOCATING NECESSARY FILES): New section. diff --git a/java/INSTALL b/java/INSTALL index ef05b811989..7ee39a71e9a 100644 --- a/java/INSTALL +++ b/java/INSTALL @@ -39,8 +39,7 @@ script like so: Replacing the paths in the command line above with: - the path to the `android.jar' headers which come with the Android - SDK. They must correspond to Android version 13 (API level 33) or - later. + SDK. They must correspond to Android version 13 (API level 33.) - the path to the C compiler in the Android NDK, for the kind of CPU you are building Emacs to run on. @@ -68,6 +67,57 @@ built for. The generated package can be uploaded onto an SD card (or similar medium) and installed on-device. + +LOCATING NECESSARY FILES + +As illustrated above, building Emacs for Android requires the presence +three separate components of the Android SDK and NDK. Subsequent to +their installation, the contents of the Android development tools are +organized into several directories, of which those pertinent to the +Emacs compilation process are: + + platforms + ndk + build-tools + +The platforms directory contains one subdirectory for each API level +whose headers have been installed. Each of these directories in turn +includes the android.jar archive for that version of Android, also +necessary for compiling Emacs. + +It is imperative that Emacs is compiled using the headers for the +exact API level that it is written for. This is currently API level +33, so the correct android.jar archive is located within a directory +whose name begins with `android-33'. Minor revisions to the headers +are inconsequential towards the Emacs compilation process; if there is +a directory named `android-33-extN' (where N represents a revision to +the Android SDK), whether you provide `configure' with that +directory's android.jar or the android.jar contained within the +directory named `android-33' is of no special importance. + +The ndk directory contains one subdirectory for each version of the +Android NDK installed. This directory in turn contains the C and C++ +compilation system. In contrast to the Java headers mentioned within +the previous paragraph, the version of the NDK used does not affect +Emacs to the extent the version of android.jar does. Having said +that, each version of the NDK only supports a limited range of API +levels; your choice of C compiler binary (or __ANDROID_API__) bears +upon the earliest version of Android the compiled package will +support. + +In most cases, each subdirectory contains a folder named `toolchains', +holding an `llvm' directory and one directory for each GCC toolchain +supplied by the NDK. The C compiler is then positioned within +`prebuilt/*/bin' inside that directory. + +The build-tools directory holds subdirectories containing the utility +programs used to convert class files output by the Java compiler to +the DEX format employed by Android. There is one subdirectory for +each version of the build tools, but the version you opt for is not of +paramount significance: if your version does not work, configure will +protest, so install a newer one. We anticipate that most recent +releases will work, such as those from the 33.0.x and 34.0.x series. + BUILDING WITH OLD NDK VERSIONS commit 8dbd5aa1eef9db956ad3a4ed73add23fe0e48268 Author: Eli Zaretskii Date: Sat Aug 12 15:06:48 2023 +0300 Avoid crashes in 'display_count_lines' when current buffer was killed * src/xdisp.c (Fformat_mode_line): * src/fns.c (Fline_number_at_pos): Don't allow to count lines in a dead buffer. (Bug#65060) diff --git a/src/fns.c b/src/fns.c index 2ed62d6e8c6..af5f9479a36 100644 --- a/src/fns.c +++ b/src/fns.c @@ -6123,6 +6123,9 @@ DEFUN ("line-number-at-pos", Fline_number_at_pos, { ptrdiff_t pos_byte, start_byte = BEGV_BYTE; + if (!BUFFER_LIVE_P (current_buffer)) + error ("Attempt to count lines in a dead buffer"); + if (MARKERP (position)) { /* We don't trust the byte position if the marker's buffer is diff --git a/src/xdisp.c b/src/xdisp.c index 22508978298..11417070804 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -27653,6 +27653,8 @@ DEFUN ("format-mode-line", Fformat_mode_line, Sformat_mode_line, if (NILP (buffer)) buffer = w->contents; CHECK_BUFFER (buffer); + if (!BUFFER_LIVE_P (XBUFFER (buffer))) + error ("Attempt to format a mode line for a dead buffer"); /* Make formatting the modeline a non-op when noninteractive, otherwise there will be problems later caused by a partially initialized frame. */ commit a4171e94667eb9e75053e77c092cdd57a0377bc0 Author: Stefan Kangas Date: Sat Aug 12 12:43:19 2023 +0200 Stop `use-package-report` from displaying an empty buffer * lisp/use-package/use-package-core.el (use-package-report): Signal user-error if there are no statistics to display, instead of showing an empty buffer. diff --git a/lisp/use-package/use-package-core.el b/lisp/use-package/use-package-core.el index 21a831ce26d..34c45b7aec3 100644 --- a/lisp/use-package/use-package-core.el +++ b/lisp/use-package/use-package-core.el @@ -1039,15 +1039,23 @@ use-package-report Configured :config has been processed (the package is loaded!) Initialized :init has been processed (load status unknown) Prefaced :preface has been processed - Declared the use-package declaration was seen" + Declared the use-package declaration was seen + +Customize the user option `use-package-compute-statistics' to +enable gathering statistics." (interactive) - (with-current-buffer (get-buffer-create "*use-package statistics*") - (setq tabulated-list-entries - (mapcar #'use-package-statistics-convert - (hash-table-keys use-package-statistics))) - (use-package-statistics-mode) - (tabulated-list-print) - (display-buffer (current-buffer)))) + (let ((statistics (hash-table-keys use-package-statistics))) + (unless statistics + (if use-package-compute-statistics + (user-error "No use-package statistics available") + (user-error (concat "Customize `use-package-compute-statistics'" + " to enable reporting")))) + (with-current-buffer (get-buffer-create "*use-package statistics*") + (setq tabulated-list-entries + (mapcar #'use-package-statistics-convert statistics)) + (use-package-statistics-mode) + (tabulated-list-print) + (display-buffer (current-buffer))))) (defvar use-package-statistics-status-order '(("Declared" . 0) commit 47b4f4cf7839cda8e12ad5f808444cd0cf8f8824 Author: Daniel Martín Date: Sat Aug 12 10:32:45 2023 +0200 ; * doc/emacs/mini.texi (Completion Commands): Fix a typo (bug#65242). diff --git a/doc/emacs/mini.texi b/doc/emacs/mini.texi index 201d4e5941d..496ede531ac 100644 --- a/doc/emacs/mini.texi +++ b/doc/emacs/mini.texi @@ -412,7 +412,7 @@ Completion Commands Typing @kbd{M-v}, while in the minibuffer, selects the window showing the completion list (@code{switch-to-completions}). This paves the way for using the commands below. @key{PageUp}, @key{prior} and -@kbd{M-g M-c} does the same. You can also select the window in other +@kbd{M-g M-c} do the same. You can also select the window in other ways (@pxref{Windows}). @findex choose-completion commit 7b578863462f8fda7a02cf5f7acb39f6362e5419 Author: Eli Zaretskii Date: Sat Aug 12 11:02:52 2023 +0300 Fix 'insert-file-contents' with non-regular files * src/fileio.c (Finsert_file_contents): Fix the doc string. Erase only the accessible portion of the buffer, not the entire buffer. (Bug#65156) diff --git a/src/fileio.c b/src/fileio.c index 52bbaa61fc2..75bcdf091d8 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4021,20 +4021,22 @@ DEFUN ("insert-file-contents", Finsert_file_contents, Sinsert_file_contents, When inserting data from a special file (e.g., /dev/urandom), you can't specify VISIT or BEG, and END should be specified to avoid -inserting unlimited data into the buffer. - -If optional fifth argument REPLACE is non-nil, replace the current -buffer contents (in the accessible portion) with the file contents. -This is better than simply deleting and inserting the whole thing -because (1) it preserves some marker positions (in unchanged portions -at the start and end of the buffer) and (2) it puts less data in the -undo list. When REPLACE is non-nil, the second return value is the -number of characters that replace previous buffer contents. - -If REPLACE is the symbol `if-regular', then eschew preserving marker -positions or the undo list if REPLACE is nil if FILENAME is not a -regular file. Otherwise, signal an error if REPLACE is non-nil and -FILENAME is not a regular file. +inserting unlimited data into the buffer from some special files +which otherwise could supply infinite amounts of data. + +If optional fifth argument REPLACE is non-nil and FILENAME names a +regular file, replace the current buffer contents (in the accessible +portion) with the file's contents. This is better than simply +deleting and inserting the whole thing because (1) it preserves some +marker positions (in unchanged portions at the start and end of the +buffer) and (2) it puts less data in the undo list. When REPLACE is +non-nil, the second element of the return value is the number of +characters that replace the previous buffer contents. + +If FILENAME is not a regular file and REPLACE is `if-regular', erase +the accessible portion of the buffer and insert the new contents. Any +other non-nil value of REPLACE will signal an error if FILENAME is not +a regular file. This function does code conversion according to the value of `coding-system-for-read' or `file-coding-system-alist', and sets the @@ -4737,10 +4739,11 @@ because (1) it preserves some marker positions (in unchanged portions } /* If REPLACE is Qunbound, buffer contents are being replaced with - text read from a FIFO. Erase the entire buffer. */ + text read from a FIFO or a device. Erase the entire accessible + portion of the buffer. */ if (BASE_EQ (replace, Qunbound)) - del_range (BEG, Z); + del_range (BEGV, ZV); move_gap_both (PT, PT_BYTE); commit 78999d52136ef5798ec06819c2546753e65e8ff4 Author: J M Date: Thu Aug 10 23:23:20 2023 +0100 Update csharp tree-sitter support due to upstream changes A change in tree-sitter-c-sharp grammar for csharp (commit 18a531), has removed the keyword void_keyword and advised we should use predefined_type. * lisp/progmodes/csharp-mode.el (csharp-ts-mode--font-lock-settings): Support both old and new style of keywords in tree-sitter-c-sharp grammar. (Bug#65113) diff --git a/lisp/progmodes/csharp-mode.el b/lisp/progmodes/csharp-mode.el index 00587a8a515..74968f3a06a 100644 --- a/lisp/progmodes/csharp-mode.el +++ b/lisp/progmodes/csharp-mode.el @@ -816,7 +816,7 @@ csharp-ts-mode--font-lock-settings :language 'c-sharp :feature 'definition :override t - '((qualified_name (identifier) @font-lock-type-face) + `((qualified_name (identifier) @font-lock-type-face) (using_directive (identifier) @font-lock-type-face) (using_directive (name_equals (identifier) @font-lock-type-face)) @@ -843,8 +843,13 @@ csharp-ts-mode--font-lock-settings (class_declaration (identifier) @font-lock-type-face) (constructor_declaration name: (_) @font-lock-type-face) - - (method_declaration type: [(identifier) (void_keyword)] @font-lock-type-face) + ;;; Handle different releases of tree-sitter-c-sharp. + ;;; Check if keyword void_keyword is available, then return the correct rule." + ,@(condition-case nil + (progn (treesit-query-capture 'csharp '((void_keyword) @capture)) + `((method_declaration type: [(identifier) (void_keyword)] @font-lock-type-face))) + (error + `((method_declaration type: [(identifier) (predefined_type)] @font-lock-type-face)))) (method_declaration type: (generic_name (identifier) @font-lock-type-face)) (method_declaration name: (_) @font-lock-function-name-face) commit 556cb01b48e2db6baa6c8793c45358b578724bfe Author: Matthew Tromp Date: Thu Aug 10 10:41:24 2023 -0400 Substitute command keys in 'ielm-header' at use time Before, command keys were substituted into the ielm-header when ielm.el was loaded, which resulted in the substitutions depending on the user's current buffer instead of the ielm buffer. For example, if the user was in an info-mode buffer, the key would appear as 'H' instead of 'C-h m'. Now, the command key is substituted after the ielm buffer has been created. * lisp/ielm.el (ielm-header): Remove substitute-command-keys. (inferior-emacs-lisp-mode): Add substitute-command-keys. (Bug#65213) Copyright-paperwork-exempt: yes diff --git a/lisp/ielm.el b/lisp/ielm.el index 5c370733c05..578422001a5 100644 --- a/lisp/ielm.el +++ b/lisp/ielm.el @@ -148,9 +148,8 @@ ielm-working-buffer This variable is buffer-local.") (defvar ielm-header - (substitute-command-keys "*** Welcome to IELM *** Type (describe-mode) or press \ -\\[describe-mode] for help.\n") +\\[describe-mode] for help.\n" "Message to display when IELM is started.") (defvaralias 'inferior-emacs-lisp-mode-map 'ielm-map) @@ -615,7 +614,7 @@ inferior-emacs-lisp-mode (setq-local comint-inhibit-carriage-motion t) ;; Add a silly header - (insert ielm-header) + (insert (substitute-command-keys ielm-header)) (ielm-set-pm (point-max)) (unless comint-use-prompt-regexp (let ((inhibit-read-only t)) commit 99367078e50fd7bc419183f20b118793ddffb039 Author: Eli Zaretskii Date: Sat Aug 12 10:04:55 2023 +0300 ; * etc/PROBLEMS: Add entry about XIM problems (bug#65205). diff --git a/etc/PROBLEMS b/etc/PROBLEMS index 418962174ec..f3d7eb303e3 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -1177,43 +1177,6 @@ do anything about it. ** International characters aren't displayed under X. -*** Missing X fonts - -XFree86 4 contains many fonts in iso10646-1 encoding which have -minimal character repertoires (whereas the encoding part of the font -name is meant to be a reasonable indication of the repertoire -according to the XLFD spec). Emacs may choose one of these to display -characters from the mule-unicode charsets and then typically won't be -able to find the glyphs to display many characters. (Check with C-u -C-x = .) To avoid this, you may need to use a fontset which sets the -font for the mule-unicode sets explicitly. E.g. to use GNU unifont, -include in the fontset spec: - -mule-unicode-2500-33ff:-gnu-unifont-*-iso10646-1,\ -mule-unicode-e000-ffff:-gnu-unifont-*-iso10646-1,\ -mule-unicode-0100-24ff:-gnu-unifont-*-iso10646-1 - -** The UTF-8/16/7 coding systems don't encode CJK (Far Eastern) characters. - -Emacs directly supports the Unicode BMP whose code points are in the -ranges 0000-33ff and e000-ffff, and indirectly supports the parts of -CJK characters belonging to these legacy charsets: - - GB2312, Big5, JISX0208, JISX0212, JISX0213-1, JISX0213-2, KSC5601 - -The latter support is done in Utf-Translate-Cjk mode (turned on by -default). Which Unicode CJK characters are decoded into which Emacs -charset is decided by the current language environment. For instance, -in Chinese-GB, most of them are decoded into chinese-gb2312. - -If you read UTF-8 data with code points outside these ranges, the -characters appear in the buffer as raw bytes of the original UTF-8 -(composed into a single quasi-character) and they will be written back -correctly as UTF-8, assuming you don't break the composed sequences. -If you read such characters from UTF-16 or UTF-7 data, they are -substituted with the Unicode 'replacement character', and you lose -information. - ** Accented ISO-8859-1 characters are displayed as | or _. Try other font set sizes (S-mouse-1). If the problem persists with @@ -1251,6 +1214,16 @@ In your ~/.Xresources file, then run And restart Emacs. +** Emacs hangs when using XIM + +This is due to an old bug in the implementation of the X protocol's +XIM transport: when an input method crashes for some reason, Xlib +cannot recover. Emacs cannot do anything about this except wait for +the I-Bux developers to fix their crashes. You can work around these +problems by disabling XIM in your X resources: + + Emacs.useXIM: false + ** On Haiku, BeCJK doesn't work properly with Emacs Some popular Haiku input methods such BeCJK are known to behave badly commit 03e897c08facf8e920ee04cd505b72f5aa5a30cf Author: Eli Zaretskii Date: Sat Aug 12 09:38:28 2023 +0300 Fix rare crashes in redisplay due to problems with fontsets * src/xdisp.c (get_next_display_element): If we have no usable face to display a character/composition, treat that as glyphless. (produce_glyphless_glyph): If neither it->face nor its ASCII face are usable, fall back to the frame's default font. (Bug#65198) diff --git a/src/xdisp.c b/src/xdisp.c index 9cddcfeda27..22508978298 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -8312,9 +8312,17 @@ get_next_display_element (struct it *it) && success_p && FRAME_WINDOW_P (it->f)) { - struct face *face = FACE_FROM_ID (it->f, it->face_id); + struct face *face = FACE_FROM_ID_OR_NULL (it->f, it->face_id); - if (it->what == IT_COMPOSITION && it->cmp_it.ch >= 0) + /* It shouldn't happen, ever, that FACE is NULL here, but + evidently some faulty fonts/fontsets can sometimes cause it. + In that case, we punt and consider the stuff undisplayable. */ + if (!face) + { + it->what = IT_GLYPHLESS; + it->glyphless_method = GLYPHLESS_DISPLAY_EMPTY_BOX; + } + else if (it->what == IT_COMPOSITION && it->cmp_it.ch >= 0) { /* Automatic composition with glyph-string. */ Lisp_Object gstring = composition_gstring_from_id (it->cmp_it.id); @@ -31781,9 +31789,12 @@ produce_glyphless_glyph (struct it *it, bool for_no_font, Lisp_Object acronym) int len; /* Get the metrics of the base font. We always refer to the current - ASCII face. */ - face = FACE_FROM_ID (it->f, it->face_id)->ascii_face; - font = face->font ? face->font : FRAME_FONT (it->f); + ASCII face, but if some faulty setup of fontsets causes that to + be NULL, we fall back to the frame's default font. */ + face = FACE_FROM_ID_OR_NULL (it->f, it->face_id); + if (face) + face = face->ascii_face; + font = (face && face->font) ? face->font : FRAME_FONT (it->f); normal_char_ascent_descent (font, -1, &it->ascent, &it->descent); it->ascent += font->baseline_offset; it->descent -= font->baseline_offset; commit b1ba06a1e4bac45cecddeb961ec3e56afa390c3f Author: Eli Zaretskii Date: Sat Aug 12 09:27:23 2023 +0300 Fix a typo in 'leuven-dark-theme.el' * etc/themes/leuven-dark-theme.el (leuven-dark): Fix a typo. Reported by John Poole . (Bug#65239) diff --git a/etc/themes/leuven-dark-theme.el b/etc/themes/leuven-dark-theme.el index fda00f1282f..e45f98d9cc1 100644 --- a/etc/themes/leuven-dark-theme.el +++ b/etc/themes/leuven-dark-theme.el @@ -95,7 +95,7 @@ leuven-dark-scale-font ;;;###theme-autoload (deftheme leuven-dark - "Face colors with a light background. + "Face colors with a dark background. Basic, Font Lock, Isearch, Gnus, Message, Org mode, Diff, Ediff, Flyspell, Semantic, and Ansi-Color faces are included -- and much more..." commit e80a9dcabb9c7d7c35d97ced89209d072166dace Author: Gregory Heytings Date: Fri Aug 11 14:22:17 2023 +0000 ; * admin/git-bisect-start: Update failing commits diff --git a/admin/git-bisect-start b/admin/git-bisect-start index 2ffb79088bc..8eb5328a1a1 100755 --- a/admin/git-bisect-start +++ b/admin/git-bisect-start @@ -82,7 +82,7 @@ done # SKIP-BRANCH 58cc931e92ece70c3e64131ee12a799d65409100 ## The list below is the exhaustive list of all commits between Dec 1 -## 2016 and Aug 9 2023 on which building Emacs with the default +## 2016 and Aug 10 2023 on which building Emacs with the default ## options, on a GNU/Linux computer and with GCC, fails. It is ## possible (though unlikely) that building Emacs with non-default ## options, with other compilers, or on other platforms, would succeed @@ -1757,12 +1757,10 @@ $REAL_GIT bisect skip $(cat $0 | grep '^# SKIP-SINGLE ' | sed 's/^# SKIP-SINGLE # SKIP-SINGLE 0a35c991c19a6dd0a707f2baa868f8989242c3ab # SKIP-SINGLE e2ee646b162b87e832c8032b9d90577bd21f21f8 # SKIP-SINGLE 35d2fe176cb438d55552cacbdf25c3692c054d51 -# SKIP-SINGLE 50649a6d1a208b7814a5fc9f0c882442502311e6 # SKIP-SINGLE de3d8ae71b43f80244c4d813ff1503b8551f0026 # SKIP-SINGLE a496509cedb17109d0e6297a74e2ff8ed526333c # SKIP-SINGLE a6a586ffc1bd302e30d80cb88b06e1e7e1573f63 # SKIP-SINGLE f5d142f66370b29af58360faeea90d1112756bc5 -# SKIP-SINGLE e1874c4e8bff165abc5c2e842b4381b65da67ebb # SKIP-SINGLE 46e8ab23eaeb5e453042f430fc016cf9ffc2ac37 # SKIP-SINGLE eb72569dbef91862a765cd4d9f380220244b4549 # SKIP-SINGLE c4b77b82decb757af0aff1b7420203fa0805b483 @@ -1770,12 +1768,9 @@ $REAL_GIT bisect skip $(cat $0 | grep '^# SKIP-SINGLE ' | sed 's/^# SKIP-SINGLE # SKIP-SINGLE 6c68d9bd3a18c74384fc764179fd92a024d6c35d # SKIP-SINGLE a46e231a5f27c46933cc53865cee452ad1a0c0d3 # SKIP-SINGLE c045d5322c2c1658f215bf59d431fcc8f96ffc12 -# SKIP-SINGLE f4acae842c02525c41b0e077482206658b14624d # SKIP-SINGLE dabb713eb05aff62deb6872a3498327934f18c8d # SKIP-SINGLE b8c05636ca4b28a7adc62e82a5fed528b402396d -# SKIP-SINGLE dd1d8414b33fe620cf21361a4f089d5f14ff454c # SKIP-SINGLE e72afa9dbf92f45d00c87c90ead364d52f73024f -# SKIP-SINGLE f0dda682ffbc84bd36a9543d23cc3151066e7e49 # SKIP-SINGLE 9d3aacedf0c217af207d39e390f376914160396b # SKIP-SINGLE 6bdbb4cbfc2deb7d3a02e1428768e101f3dbd265 # SKIP-SINGLE 2752573dfb76873dbe783e89a1fbf01d157c54e3 commit 9091f42784b35fabb947621f665feb5a58e03eeb Author: Gregory Heytings Date: Thu Aug 10 17:34:40 2023 +0000 ; * admin/git-bisect-start: Update failing commits diff --git a/admin/git-bisect-start b/admin/git-bisect-start index 9de4d547323..2ffb79088bc 100755 --- a/admin/git-bisect-start +++ b/admin/git-bisect-start @@ -82,7 +82,7 @@ done # SKIP-BRANCH 58cc931e92ece70c3e64131ee12a799d65409100 ## The list below is the exhaustive list of all commits between Dec 1 -## 2016 and Jul 8 2023 on which building Emacs with the default +## 2016 and Aug 9 2023 on which building Emacs with the default ## options, on a GNU/Linux computer and with GCC, fails. It is ## possible (though unlikely) that building Emacs with non-default ## options, with other compilers, or on other platforms, would succeed @@ -1757,3 +1757,27 @@ $REAL_GIT bisect skip $(cat $0 | grep '^# SKIP-SINGLE ' | sed 's/^# SKIP-SINGLE # SKIP-SINGLE 0a35c991c19a6dd0a707f2baa868f8989242c3ab # SKIP-SINGLE e2ee646b162b87e832c8032b9d90577bd21f21f8 # SKIP-SINGLE 35d2fe176cb438d55552cacbdf25c3692c054d51 +# SKIP-SINGLE 50649a6d1a208b7814a5fc9f0c882442502311e6 +# SKIP-SINGLE de3d8ae71b43f80244c4d813ff1503b8551f0026 +# SKIP-SINGLE a496509cedb17109d0e6297a74e2ff8ed526333c +# SKIP-SINGLE a6a586ffc1bd302e30d80cb88b06e1e7e1573f63 +# SKIP-SINGLE f5d142f66370b29af58360faeea90d1112756bc5 +# SKIP-SINGLE e1874c4e8bff165abc5c2e842b4381b65da67ebb +# SKIP-SINGLE 46e8ab23eaeb5e453042f430fc016cf9ffc2ac37 +# SKIP-SINGLE eb72569dbef91862a765cd4d9f380220244b4549 +# SKIP-SINGLE c4b77b82decb757af0aff1b7420203fa0805b483 +# SKIP-SINGLE 0ee01457a84e031d490553949a2deacd4865a5bb +# SKIP-SINGLE 6c68d9bd3a18c74384fc764179fd92a024d6c35d +# SKIP-SINGLE a46e231a5f27c46933cc53865cee452ad1a0c0d3 +# SKIP-SINGLE c045d5322c2c1658f215bf59d431fcc8f96ffc12 +# SKIP-SINGLE f4acae842c02525c41b0e077482206658b14624d +# SKIP-SINGLE dabb713eb05aff62deb6872a3498327934f18c8d +# SKIP-SINGLE b8c05636ca4b28a7adc62e82a5fed528b402396d +# SKIP-SINGLE dd1d8414b33fe620cf21361a4f089d5f14ff454c +# SKIP-SINGLE e72afa9dbf92f45d00c87c90ead364d52f73024f +# SKIP-SINGLE f0dda682ffbc84bd36a9543d23cc3151066e7e49 +# SKIP-SINGLE 9d3aacedf0c217af207d39e390f376914160396b +# SKIP-SINGLE 6bdbb4cbfc2deb7d3a02e1428768e101f3dbd265 +# SKIP-SINGLE 2752573dfb76873dbe783e89a1fbf01d157c54e3 +# SKIP-SINGLE 62e990db7a2fad16756e019b331c28ad5a5a89fe +# SKIP-SINGLE 6253e7e74249c7cdfa86723f0b91a1d207cb143e commit 344ac529ca9a452cbb01c120fb70410f56b459f7 Author: Robert Pluim Date: Thu Aug 10 12:42:30 2023 +0200 ; * etc/PROBLEMS: Fix typo. diff --git a/etc/PROBLEMS b/etc/PROBLEMS index a2d3d687998..418962174ec 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -516,7 +516,7 @@ directory copy is ineffective. This is due to an arbitrary limit in certain versions of awk. The solution is to use gawk (GNU awk). -*** Saving a file encrypted with GnuPG via EasyPG hangs +*** Saving a file encrypted with GnuPG via EasyPG hangs. This is known to happen with GnuPG v2.4.1. The only known workaround is to downgrade to a version of GnuPG older than 2.4.1 (or, in the commit bccf848b26c2f3d899e739c78ce1bb195ef6cfaf Author: Michael Albinus Date: Thu Aug 10 12:17:59 2023 +0200 Adapt Tramp test * test/lisp/net/tramp-tests.el (tramp-test41-special-characters): Skip for macOS. diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index c090fec8b2a..6021eda8dff 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -7404,6 +7404,7 @@ tramp-test41-special-characters (skip-unless (not (getenv "EMACS_HYDRA_CI"))) ; SLOW ~ 245s (skip-unless (not (tramp--test-rsync-p))) (skip-unless (not (tramp--test-rclone-p))) + (skip-unless (not (or (eq system-type 'darwin) (tramp--test-macos-p)))) ;; Newlines, slashes and backslashes in file names are not ;; supported. So we don't test. And we don't test the tab commit 71bc060e4031bee83ec79354bff526fa8a27a81d Author: dannyfreeman Date: Mon Aug 7 15:55:27 2023 -0400 Properly expand the JSX indentation rules in 'js-ts-mode' * lisp/progmodes/js.el (js--treesit-indent-rules): Fix 'js-ts-mode' indent bug in JSX expressions. Before this change, treesit indent mechanisms were trying to call this compatibility function like a matching or anchor rule. This resulted in an error when running `indent-for-tab-command` while the cursor was in a JSX expression: treesit--simple-indent-eval: Wrong number of arguments: ((cl-struct-js--pitem-tags ido-cur-list t) nil "Indent rules helper, to handle different releases of tree-sitter-javascript." (Bug#65134) diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index bbe3159f596..c583b6f6191 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3474,7 +3474,7 @@ js--treesit-indent-rules ((parent-is "statement_block") parent-bol js-indent-level) ;; JSX - (js-jsx--treesit-indent-compatibility-bb1f97b) + ,@(js-jsx--treesit-indent-compatibility-bb1f97b) ((node-is "jsx_closing_element") parent 0) ((match "jsx_element" "statement") parent js-indent-level) ((parent-is "jsx_element") parent js-indent-level) commit 889cfb42ed9e857a7e813bb356bb2b5e588c89c0 Author: Andrea Corallo Date: Thu Aug 10 10:39:14 2023 +0200 * Add `emacs-lisp-native-compile' to easy-menu. * lisp/progmodes/elisp-mode.el (emacs-lisp-mode-menu): Add menu item for emacs-lisp-native-compile. diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 4d92bed6236..bd3916ce108 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -84,6 +84,9 @@ emacs-lisp-mode-menu :help "Byte-compile the current file (if it has changed), then load compiled code"] ["Byte-recompile Directory..." byte-recompile-directory :help "Recompile every `.el' file in DIRECTORY that needs recompilation"] + ["Native-compile This File" emacs-lisp-native-compile + :help "Compile the current file containing the current buffer to native code" + :active (native-comp-available-p)] ["Native-compile and Load" emacs-lisp-native-compile-and-load :help "Compile the current file to native code, then load compiled native code" :active (native-comp-available-p)] commit 4535aafa8a34b659e4ab18f0188fc8d9332f4e97 Author: Andrea Corallo Date: Thu Aug 10 10:34:01 2023 +0200 * lisp/progmodes/elisp-mode.el (emacs-lisp-mode-menu): Simplify condition. diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 5343be48bb3..4d92bed6236 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -86,8 +86,7 @@ emacs-lisp-mode-menu :help "Recompile every `.el' file in DIRECTORY that needs recompilation"] ["Native-compile and Load" emacs-lisp-native-compile-and-load :help "Compile the current file to native code, then load compiled native code" - :active (and (featurep 'native-compile) - (native-comp-available-p))] + :active (native-comp-available-p)] ["Disassemble Byte Compiled Object..." disassemble :help "Print disassembled code for OBJECT in a buffer"] "---" commit 173af0cad514b7fa7b71580caa5e59241098607f Author: Andrea Corallo Date: Thu Aug 10 10:18:43 2023 +0200 * Introduce `emacs-lisp-native-compile'. * lisp/progmodes/elisp-mode.el (emacs-lisp-native-compile): New command. (emacs-lisp-native-compile-and-load): Make use of. diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 1b42051ab93..5343be48bb3 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -221,6 +221,16 @@ emacs-lisp-byte-compile-and-load (declare-function native-compile "comp") (declare-function comp-write-bytecode-file "comp") +(defun emacs-lisp-native-compile () + "Native-compile synchronously the current file (if it has changed)." + (interactive nil emacs-lisp-mode) + (emacs-lisp--before-compile-buffer) + (let* ((byte+native-compile t) + (byte-to-native-output-buffer-file nil) + (eln (native-compile buffer-file-name))) + (when eln + (comp-write-bytecode-file eln)))) + (defun emacs-lisp-native-compile-and-load () "Native-compile synchronously the current file (if it has changed). Load the compiled code when finished. @@ -229,11 +239,8 @@ emacs-lisp-native-compile-and-load `native-comp-jit-compilation' set to t to achieve asynchronous native compilation." (interactive nil emacs-lisp-mode) - (emacs-lisp--before-compile-buffer) - (let ((byte+native-compile t) - (byte-to-native-output-buffer-file nil)) - (when-let ((eln (native-compile buffer-file-name))) - (load (file-name-sans-extension (comp-write-bytecode-file eln)))))) + (when-let ((byte-file (emacs-lisp-native-compile))) + (load (file-name-sans-extension byte-file)))) (defun emacs-lisp-macroexpand () "Macroexpand the form after point. commit 063d7d89d7ae053be611f4de3d1baceddf407c07 Author: Eli Zaretskii Date: Thu Aug 10 10:50:35 2023 +0300 Fix the -x switch in non-X builds * src/emacs.c (main): Move the handling of the -x switch out of the HAVE_X_WINDOWS condition, and simplify the rest of the code by avoiding code duplication in HAVE_X_WINDOWS and !HAVE_X_WINDOWS cases. (Bug#65048) diff --git a/src/emacs.c b/src/emacs.c index e63b0924282..dde305edbc2 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -2007,15 +2007,16 @@ main (int argc, char **argv) } #endif /* HAVE_NS */ -#ifdef HAVE_X_WINDOWS /* Stupid kludge to catch command-line display spec. We can't handle this argument entirely in window system dependent code because we don't even know which window system dependent code to run until we've recognized this argument. */ { - char *displayname = 0; int count_before = skip_args; +#ifdef HAVE_X_WINDOWS + char *displayname = 0; + /* Skip any number of -d options, but only use the last one. */ while (!only_version) { @@ -2045,12 +2046,15 @@ main (int argc, char **argv) } argv[count_before + 1] = (char *) "-d"; } +#endif /* HAVE_X_WINDOWS */ if (! no_site_lisp) { - if (argmatch (argv, argc, "-Q", "--quick", 3, NULL, &skip_args) + + if (argmatch (argv, argc, "-Q", "--quick", 3, NULL, &skip_args) || argmatch (argv, argc, "-quick", 0, 2, NULL, &skip_args)) - no_site_lisp = 1; + no_site_lisp = 1; + } if (argmatch (argv, argc, "-x", 0, 1, &junk, &skip_args)) @@ -2066,18 +2070,6 @@ main (int argc, char **argv) /* Don't actually discard this arg. */ skip_args = count_before; } -#else /* !HAVE_X_WINDOWS */ - if (! no_site_lisp) - { - int count_before = skip_args; - - if (argmatch (argv, argc, "-Q", "--quick", 3, NULL, &skip_args) - || argmatch (argv, argc, "-quick", 0, 2, NULL, &skip_args)) - no_site_lisp = 1; - - skip_args = count_before; - } -#endif /* argmatch must not be used after here, except when building temacs commit 5a7fb0b39b7059e6ff3c9a9e7641c80cf3a48459 Author: Po Lu Date: Thu Aug 10 15:31:03 2023 +0800 Document that `set-mouse-color' does not work everywhere * etc/PROBLEMS (Miscellaneous Problems): Mention where `set-mouse-color' does not work. diff --git a/etc/PROBLEMS b/etc/PROBLEMS index 4c75cfc62b6..a2d3d687998 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -575,6 +575,20 @@ This can happen with CVS versions 1.12.8 and 1.12.9. Upgrade to CVS ** Miscellaneous problems +*** 'set-mouse-color' and the '-ms' command line argument do not work. + +Systems where the default cursors are not simple 1 bit-per-pixel +bitmaps usually forbid recoloring the cursor, since it is unclear +which colors should replace those already present within each cursor +image. For example, 'set-mouse-color' and '-ms' have no function on X +systems with GNOME, KDE, and other recent desktop environments +employing cursor images containing colors and partial transparency. + +Changing the cursor color is also impossible on MS-Windows and PGTK +systems. In the former case, it is because the prerequisite code has +yet to be written. In the latter, it is because GTK does not provide +for changing the color of cursor images. + *** Display artifacts on GUI frames on X-based systems. This is known to be caused by using double-buffering (which is enabled commit d06fc72496f7ec6dc166b264e9a2acae88488733 Author: Eli Zaretskii Date: Thu Aug 10 09:08:59 2023 +0300 ; * doc/emacs/dired.texi (Misc Dired Features): Fix last change. diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi index c20c221cd78..05e12f56668 100644 --- a/doc/emacs/dired.texi +++ b/doc/emacs/dired.texi @@ -1727,7 +1727,7 @@ Misc Dired Features @vindex dired-free-space By default, Dired displays the available space on the directory's -disk on the first line of that directory listing, following the +disk on the first line of that directory's listing, following the directory name. You can control this display by customizing the variable @code{dired-free-space}. Its default value is @code{first}, which produces the available space after the directory name. If you commit 97b8ac376b55ad48a8e66e9ab3975f81bc8cbfab Author: Eli Zaretskii Date: Thu Aug 10 09:06:05 2023 +0300 Fix the effects and documentation of 'dired-free-space' * lisp/dired.el (dired-free-space): Fix doc string and Custom tags. (dired--insert-disk-space): When 'dired-free-space' is 'separate', return the position of the beginning of the disk-space line, to be compatible with pre-Emacs 29 behavior under 'dired-hide-details-mode'. (Bug#65186) * doc/emacs/dired.texi (Misc Dired Features): Fix wording in documentation of 'dired-free-space'. diff --git a/doc/emacs/dired.texi b/doc/emacs/dired.texi index 77c4e09c826..c20c221cd78 100644 --- a/doc/emacs/dired.texi +++ b/doc/emacs/dired.texi @@ -1726,12 +1726,17 @@ Misc Dired Features @section Other Dired Features @vindex dired-free-space - By default, Dired will display the available space on the disk in -the first line. This is the @code{first} value of the -@code{dired-free-space} variable. If you set this to -@code{separate} instead, Dired will display this on a separate line -(including the space the files in the current directory takes). If -you set this to @code{nil}, the free space isn't displayed at all. + By default, Dired displays the available space on the directory's +disk on the first line of that directory listing, following the +directory name. You can control this display by customizing the +variable @code{dired-free-space}. Its default value is @code{first}, +which produces the available space after the directory name. If you +customize it to the value @code{separate} instead, Dired will display +the disk space information on a separate line, following the line with +the directory name, and will include in that line the space used by +the files in the current directory as well as the available disk +space. If you set this to @code{nil}, the available disk space +information will not be displayed at all. @kindex + @r{(Dired)} @findex dired-create-directory diff --git a/lisp/dired.el b/lisp/dired.el index d0af05d68f0..4c2e9243e6c 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -218,14 +218,19 @@ dired-keep-marker-relsymlink If a character, new links are unconditionally marked with that character.") (defcustom dired-free-space 'first - "Whether and how to display the amount of free disk space in Dired buffers. + "Whether and how to display the disk space usage info in Dired buffers. If nil, don't display. -If `separate', display on a separate line (along with used count). -If `first', display only the free disk space on the first line, -following the directory name." - :type '(choice (const :tag "On a separate line" separate) - (const :tag "On the first line, after directory name" first) - (const :tag "Don't display" nil)) +If `separate', display on a separate line, and include both the used +and the free disk space. +If `first', the default, display only the free disk space on the first +line, following the directory name." + :type '(choice (const + :tag + "On separate line, display both used and free space" separate) + (const + :tag + "On first line, after directory name, display only free space" first) + (const :tag "Don't display disk space usage" nil)) :version "29.1" :group 'dired) @@ -1769,7 +1774,10 @@ dired--insert-disk-space ((eq dired-free-space 'separate) (end-of-line) (insert " available " available) - (forward-line 1) + ;; The separate free-space line is considered part of the + ;; directory content, for the purposes of + ;; 'dired-hide-details-mode'. + (beginning-of-line) (point)) ((eq dired-free-space 'first) (goto-char beg) commit 75c72e59f69c07948d367d09c11392981f30c488 Author: Stefan Kangas Date: Thu Aug 10 07:21:22 2023 +0200 ; Fix typo diff --git a/lisp/help.el b/lisp/help.el index 6eac037df2c..7902a5c5b14 100644 --- a/lisp/help.el +++ b/lisp/help.el @@ -1479,7 +1479,7 @@ describe-map-tree If TITLE is non-nil, is a string to insert at the beginning. TITLE should not end with a colon or a newline; we supply that. -If NOMENU is non-nil, then omit menu-bar commands. +If NO-MENU is non-nil, then omit menu-bar commands. If TRANSL is non-nil, the definitions are actually key translations so print strings and vectors differently. commit 6d11b7b30076977fd30d4df6cd9ae16cc0a618e8 Author: Stefan Kangas Date: Wed Aug 9 16:48:54 2023 +0200 Fix cross-reference to eldoc in eglot manual * doc/misc/eglot.texi (Eglot Features): Fix cross-reference to eldoc node in the Emacs manual. diff --git a/doc/misc/eglot.texi b/doc/misc/eglot.texi index 962e6c914ce..6eb212ca841 100644 --- a/doc/misc/eglot.texi +++ b/doc/misc/eglot.texi @@ -406,9 +406,10 @@ Eglot Features At-point documentation: when point is at or near a symbol or an identifier, the information about the symbol/identifier, such as the signature of a function or class method and server-generated -diagnostics, is made available via the ElDoc package (@pxref{Lisp -Doc,,, emacs, GNU Emacs Manual}). This allows major modes to provide -extensive help and documentation about the program identifiers. +diagnostics, is made available via the ElDoc package +(@pxref{Programming Language Doc,,, emacs, GNU Emacs Manual}). This +allows major modes to provide extensive help and documentation about +the program identifiers. @item On-the-fly diagnostic annotations with server-suggested fixes, via the commit 913e50aba6c8be86c762ad2f6491b81ca7e9cdfd Author: Eli Zaretskii Date: Wed Aug 9 17:35:24 2023 +0300 Add native-compilation to Emacs Lisp mode menu * lisp/progmodes/elisp-mode.el (emacs-lisp-mode-menu): Add menu item for emacs-lisp-native-compile-and-load. diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 956e3d30bce..1b42051ab93 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -84,6 +84,10 @@ emacs-lisp-mode-menu :help "Byte-compile the current file (if it has changed), then load compiled code"] ["Byte-recompile Directory..." byte-recompile-directory :help "Recompile every `.el' file in DIRECTORY that needs recompilation"] + ["Native-compile and Load" emacs-lisp-native-compile-and-load + :help "Compile the current file to native code, then load compiled native code" + :active (and (featurep 'native-compile) + (native-comp-available-p))] ["Disassemble Byte Compiled Object..." disassemble :help "Print disassembled code for OBJECT in a buffer"] "---" commit b93107c20b2899612c5c7a15187fc69866905e47 Author: Andrea Corallo Date: Wed Aug 9 15:13:48 2023 +0200 Fix emacs-lisp-native-compile-and-load eln target directory (bug#64226) * lisp/emacs-lisp/comp.el (comp-spill-lap-function): Don't use `byte+native-compile' to select output directory but always axpect it explicit through `native-compile-target-directory'. (batch-byte+native-compile): Set `native-compile-target-directory'. * test/src/comp-tests.el (comp-tests-bootstrap): Set `native-compile-target-directory'. diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el index 322df0e86a1..bdce4254bf5 100644 --- a/lisp/emacs-lisp/comp.el +++ b/lisp/emacs-lisp/comp.el @@ -1392,11 +1392,8 @@ comp-spill-lap-function (unless byte-to-native-top-level-forms (signal 'native-compiler-error-empty-byte filename)) (unless (comp-ctxt-output comp-ctxt) - (setf (comp-ctxt-output comp-ctxt) (comp-el-to-eln-filename - filename - (or native-compile-target-directory - (when byte+native-compile - (car (last native-comp-eln-load-path))))))) + (setf (comp-ctxt-output comp-ctxt) + (comp-el-to-eln-filename filename native-compile-target-directory))) (setf (comp-ctxt-speed comp-ctxt) (alist-get 'native-comp-speed byte-native-qualities) (comp-ctxt-debug comp-ctxt) (alist-get 'native-comp-debug @@ -4351,6 +4348,8 @@ batch-byte+native-compile (batch-byte-compile) (cl-assert (length= command-line-args-left 1)) (let* ((byte+native-compile t) + (native-compile-target-directory + (car (last native-comp-eln-load-path))) (byte-to-native-output-buffer-file nil) (eln-file (car (batch-native-compile)))) (comp-write-bytecode-file eln-file) diff --git a/test/src/comp-tests.el b/test/src/comp-tests.el index b0d865292b7..0e36a3c9190 100644 --- a/test/src/comp-tests.el +++ b/test/src/comp-tests.el @@ -70,9 +70,11 @@ comp-tests-bootstrap :suffix "-comp-stage1.el" (ert-with-temp-file comp2-src :suffix "-comp-stage2.el" - (let* ((byte+native-compile t) ; FIXME HACK + (let* ((byte+native-compile t) + (native-compile-target-directory + (car (last native-comp-eln-load-path))) (comp-src (expand-file-name "../../../lisp/emacs-lisp/comp.el" - (ert-resource-directory))) + (ert-resource-directory))) ;; Can't use debug symbols. (native-comp-debug 0)) (copy-file comp-src comp1-src t) commit 7e3c616086014e883490d537509c9641a87ed19b Author: Eli Zaretskii Date: Wed Aug 9 07:25:59 2023 -0400 ; Re-enable the /dev/urandom test * test/src/fileio-tests.el (fileio-tests--non-regular-insert): Remove :unstable, but comment out the single failing test. diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el index 5bc1024ff20..fea7eeea753 100644 --- a/test/src/fileio-tests.el +++ b/test/src/fileio-tests.el @@ -194,12 +194,11 @@ fileio-tests/file-name-concat (should (equal (file-name-concat "" "") ""))) (ert-deftest fileio-tests--non-regular-insert () - ;; Fails in Emacs 29 because /dev/urandom is typically seekable (bug#65156) - :tags '(:unstable) (skip-unless (file-exists-p "/dev/urandom")) (with-temp-buffer (set-buffer-multibyte nil) - (should-error (insert-file-contents "/dev/urandom" nil 5 10)) + ;; Fails in Emacs 29 because /dev/urandom is typically seekable (bug#65156) + ;(should-error (insert-file-contents "/dev/urandom" nil 5 10)) (insert-file-contents "/dev/urandom" nil nil 10) (should (= (buffer-size) 10)))) commit ff21717f05b2cd48015a8c05d89a1ff7e598fb1b Author: Mattias Engdegård Date: Wed Aug 9 10:23:28 2023 +0200 Disable failing test (bug#65156) * test/src/fileio-tests.el (fileio-tests--non-regular-insert): Mark as :unstable, since /dev/urandom is seekable. Do not merge to master. diff --git a/test/src/fileio-tests.el b/test/src/fileio-tests.el index 50642420ce9..5bc1024ff20 100644 --- a/test/src/fileio-tests.el +++ b/test/src/fileio-tests.el @@ -194,6 +194,8 @@ fileio-tests/file-name-concat (should (equal (file-name-concat "" "") ""))) (ert-deftest fileio-tests--non-regular-insert () + ;; Fails in Emacs 29 because /dev/urandom is typically seekable (bug#65156) + :tags '(:unstable) (skip-unless (file-exists-p "/dev/urandom")) (with-temp-buffer (set-buffer-multibyte nil) commit 4767f5eaeedef57dfcc119399e5855f41c2e1be5 Author: Po Lu Date: Wed Aug 9 10:49:44 2023 +0800 Better fix for bug#65156 * src/fileio.c (Finsert_file_contents): Correct seek-ability test, since lseek returns -1 upon failure. (Bug#65156) diff --git a/src/fileio.c b/src/fileio.c index 55132f1ec38..4d5365bff9a 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4023,7 +4023,7 @@ because (1) it preserves some marker positions (in unchanged portions if (!S_ISREG (st.st_mode)) { regular = false; - seekable = lseek (fd, 0, SEEK_CUR) < 0; + seekable = lseek (fd, 0, SEEK_CUR) != (off_t) -1; if (! NILP (visit)) { @@ -4581,7 +4581,7 @@ because (1) it preserves some marker positions (in unchanged portions goto handled; } - if ((seekable && regular) || !NILP (end)) + if (seekable || !NILP (end)) total = end_offset - beg_offset; else /* For a special file, all we can do is guess. */ @@ -4678,7 +4678,7 @@ because (1) it preserves some marker positions (in unchanged portions For a special file, where TOTAL is just a buffer size, so don't bother counting in HOW_MUCH. (INSERTED is where we count the number of characters inserted.) */ - if ((seekable && regular) || !NILP (end)) + if (seekable || !NILP (end)) how_much += this; inserted += this; } commit dd1d8414b33fe620cf21361a4f089d5f14ff454c Author: Eli Zaretskii Date: Tue Aug 8 22:18:46 2023 +0300 Fix insert-file-contents with pipes and /dev/stdin * src/fileio.c (Finsert_file_contents): Restore logic of non-regular but seekable files. (Bug#65156) diff --git a/src/fileio.c b/src/fileio.c index 995e4142f58..55132f1ec38 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -4581,7 +4581,7 @@ because (1) it preserves some marker positions (in unchanged portions goto handled; } - if (seekable || !NILP (end)) + if ((seekable && regular) || !NILP (end)) total = end_offset - beg_offset; else /* For a special file, all we can do is guess. */ @@ -4678,7 +4678,7 @@ because (1) it preserves some marker positions (in unchanged portions For a special file, where TOTAL is just a buffer size, so don't bother counting in HOW_MUCH. (INSERTED is where we count the number of characters inserted.) */ - if (seekable || !NILP (end)) + if ((seekable && regular) || !NILP (end)) how_much += this; inserted += this; } commit 50649a6d1a208b7814a5fc9f0c882442502311e6 Author: Eli Zaretskii Date: Tue Aug 8 15:22:14 2023 +0300 ; * etc/PROBLEMS: Fix wording. diff --git a/etc/PROBLEMS b/etc/PROBLEMS index 30769e68f11..4c75cfc62b6 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -516,7 +516,7 @@ directory copy is ineffective. This is due to an arbitrary limit in certain versions of awk. The solution is to use gawk (GNU awk). -*** Saving, via EasyPG, a file encrypted with GnuPG hangs +*** Saving a file encrypted with GnuPG via EasyPG hangs This is known to happen with GnuPG v2.4.1. The only known workaround is to downgrade to a version of GnuPG older than 2.4.1 (or, in the commit f0dda682ffbc84bd36a9543d23cc3151066e7e49 Author: Stefan Kangas Date: Tue Aug 8 04:46:03 2023 +0200 ; * etc/NEWS.28: Add deletion of levents.el. diff --git a/etc/NEWS.28 b/etc/NEWS.28 index 6ffe39b9493..89ef25bb32d 100644 --- a/etc/NEWS.28 +++ b/etc/NEWS.28 @@ -3150,7 +3150,7 @@ declared obsolete. This was a compatibility kludge which is no longer needed. ** Some libraries obsolete since Emacs 23 have been removed: -ledit.el, lmenu.el, lucid.el and old-whitespace.el. +ledit.el, levents.el, lmenu.el, lucid.el and old-whitespace.el. ** Some functions and variables obsolete since Emacs 23 have been removed: 'GOLD-map', 'advertised-xscheme-send-previous-expression', commit f4acae842c02525c41b0e077482206658b14624d Author: Po Lu Date: Tue Aug 8 09:01:59 2023 +0800 Fix bug#65042 * src/pgtkterm.c (fill_background_by_face): Respect the frame's background alpha property. diff --git a/src/pgtkterm.c b/src/pgtkterm.c index f4b05ef9903..e767e15cc07 100644 --- a/src/pgtkterm.c +++ b/src/pgtkterm.c @@ -1328,14 +1328,17 @@ fill_background_by_face (struct frame *f, struct face *face, int x, int y, int width, int height) { cairo_t *cr = pgtk_begin_cr_clip (f); + double r, g, b, a; + cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_rectangle (cr, x, y, width, height); cairo_clip (cr); - double r = ((face->background >> 16) & 0xff) / 255.0; - double g = ((face->background >> 8) & 0xff) / 255.0; - double b = ((face->background >> 0) & 0xff) / 255.0; - cairo_set_source_rgb (cr, r, g, b); + r = ((face->background >> 16) & 0xff) / 255.0; + g = ((face->background >> 8) & 0xff) / 255.0; + b = ((face->background >> 0) & 0xff) / 255.0; + a = f->alpha_background; + cairo_set_source_rgba (cr, r, g, b, a); cairo_paint (cr); if (face->stipple != 0) @@ -1343,10 +1346,10 @@ fill_background_by_face (struct frame *f, struct face *face, int x, int y, cairo_pattern_t *mask = FRAME_DISPLAY_INFO (f)->bitmaps[face->stipple - 1].pattern; - double r = ((face->foreground >> 16) & 0xff) / 255.0; - double g = ((face->foreground >> 8) & 0xff) / 255.0; - double b = ((face->foreground >> 0) & 0xff) / 255.0; - cairo_set_source_rgb (cr, r, g, b); + r = ((face->foreground >> 16) & 0xff) / 255.0; + g = ((face->foreground >> 8) & 0xff) / 255.0; + b = ((face->foreground >> 0) & 0xff) / 255.0; + cairo_set_source_rgba (cr, r, g, b, a); cairo_mask (cr, mask); } commit e1874c4e8bff165abc5c2e842b4381b65da67ebb Author: Eli Zaretskii Date: Mon Aug 7 16:20:17 2023 +0300 * configure.ac (HAVE_TREE_SITTER): Set NEED_DYNLIB=yes (bug#65123). diff --git a/configure.ac b/configure.ac index 5dd182ae6e5..ab401be1c1d 100644 --- a/configure.ac +++ b/configure.ac @@ -3254,6 +3254,7 @@ AC_DEFUN [HAVE_TREE_SITTER=yes], [HAVE_TREE_SITTER=no]) if test "${HAVE_TREE_SITTER}" = yes; then AC_DEFINE(HAVE_TREE_SITTER, 1, [Define if using tree-sitter.]) + NEED_DYNLIB=yes else EMACS_CHECK_MODULES([TREE_SITTER], [tree-sitter >= 0.6.3], [HAVE_TREE_SITTER=yes], [HAVE_TREE_SITTER=no]) commit ef8838c3a5f041769f72758b831eb3fa7a130fb9 Author: Michael Albinus Date: Sun Aug 6 21:25:40 2023 +0200 * etc/NEWS: Mention tramp-show-ad-hoc-proxies. diff --git a/etc/NEWS b/etc/NEWS index 922938f13a1..e74cbee4a53 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -15,6 +15,12 @@ in older Emacs versions. You can narrow news to a specific version by calling 'view-emacs-news' with a prefix argument or by typing 'C-u C-h C-n'. +Temporary note: ++++ indicates that all relevant manuals in doc/ have been updated. +--- means no change in the manuals is needed. +When you add a new item, use the appropriate mark if you are sure it +applies, and please also update docstrings as needed. + * Installation Changes in Emacs 29.2 @@ -30,6 +36,13 @@ with a prefix argument or by typing 'C-u C-h C-n'. * Changes in Specialized Modes and Packages in Emacs 29.2 +** Tramp + ++++ +*** New user option 'tramp-show-ad-hoc-proxies'. +When non-nil, ad-hoc definitions are kept in remote file names instead +of showing the shortcuts. + * New Modes and Packages in Emacs 29.2 commit 495bee253fc354042c1decc37805832a73f935d8 Author: Michael Albinus Date: Sun Aug 6 16:24:19 2023 +0200 * test/lisp/net/tramp-tests.el (tramp-test42-utf8): Skip for macOS. diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 31d36f81740..c090fec8b2a 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -2689,7 +2689,7 @@ tramp-test10-write-region (should (null (save-buffer))) (should (eq (coding-system-get buffer-file-coding-system :mime-charset) - (coding-system-get bfcs :mime-charset)))))) + (coding-system-get bfcs :mime-charset)))))) ;; Cleanup. (ignore-errors (delete-file tmp-name)))))) @@ -7478,14 +7478,12 @@ tramp-test42-utf8 (skip-unless (not (tramp--test-gdrive-p))) (skip-unless (not (tramp--test-crypt-p))) (skip-unless (not (tramp--test-rclone-p))) + (skip-unless (not (or (eq system-type 'darwin) (tramp--test-macos-p)))) - (let* ((utf8 (if (and (eq system-type 'darwin) - (memq 'utf-8-hfs (coding-system-list))) - 'utf-8-hfs 'utf-8)) - (coding-system-for-read utf8) - (coding-system-for-write utf8) - (file-name-coding-system - (coding-system-change-eol-conversion utf8 'unix))) + (let ((coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8) + (file-name-coding-system + (coding-system-change-eol-conversion 'utf-8 'unix))) (apply #'tramp--test-check-files (append commit de1effd73b4dfa8891e35905d20803b9e76c4a13 Author: Michael Albinus Date: Sun Aug 6 15:46:58 2023 +0200 ; Fix last change diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index f4938cf9a01..31d36f81740 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -2687,7 +2687,9 @@ tramp-test10-write-region (let ((bfcs buffer-file-coding-system)) (should (buffer-modified-p)) (should (null (save-buffer))) - (should (eq buffer-file-coding-system bfcs))))) + (should + (eq (coding-system-get buffer-file-coding-system :mime-charset) + (coding-system-get bfcs :mime-charset)))))) ;; Cleanup. (ignore-errors (delete-file tmp-name)))))) commit 7c7966862bc0126e0f5b8c6d760cc3293d9a87f6 Author: Michael Albinus Date: Sun Aug 6 14:30:57 2023 +0200 * test/lisp/net/tramp-tests.el (tramp-test10-write-region): Extend test. diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index 00e368abe4d..f4938cf9a01 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el @@ -2676,7 +2676,18 @@ tramp-test10-write-region :type 'file-already-exists) (should-error (write-region "foo" nil tmp-name nil nil nil 'excl) - :type 'file-already-exists)) + :type 'file-already-exists) + (delete-file tmp-name) + + ;; Check `buffer-file-coding-system'. Bug#65022. + (with-temp-buffer + (setq buffer-file-name tmp-name) + (insert "foo") + (set-buffer-file-coding-system 'cp1251) + (let ((bfcs buffer-file-coding-system)) + (should (buffer-modified-p)) + (should (null (save-buffer))) + (should (eq buffer-file-coding-system bfcs))))) ;; Cleanup. (ignore-errors (delete-file tmp-name)))))) commit 16205e8db65446845967f7ad95f3bbe417b67cbd Author: Eli Zaretskii Date: Sun Aug 6 10:02:11 2023 +0300 ; Improve help-echo in package.el * lisp/emacs-lisp/package.el (package-menu-mode-menu): Improve help-echo descriptions. (Bug#65094) diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index 83c7380553d..5fe018700a4 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el @@ -3083,20 +3083,36 @@ package-menu-mode-menu "--" ("Filter Packages" - ["Filter by Archive" package-menu-filter-by-archive :help "Filter packages by archive"] - ["Filter by Description" package-menu-filter-by-description :help "Filter packages by description"] - ["Filter by Keyword" package-menu-filter-by-keyword :help "Filter packages by keyword"] - ["Filter by Name" package-menu-filter-by-name :help "Filter packages by name"] + ["Filter by Archive" package-menu-filter-by-archive + :help + "Prompt for archive(s), display only packages from those archives"] + ["Filter by Description" package-menu-filter-by-description + :help + "Prompt for regexp, display only packages with matching description"] + ["Filter by Keyword" package-menu-filter-by-keyword + :help + "Prompt for keyword(s), display only packages with matching keywords"] + ["Filter by Name" package-menu-filter-by-name + :help + "Prompt for regexp, display only packages whose names match the regexp"] ["Filter by Name or Description" package-menu-filter-by-name-or-description - :help "Filter packages by name or description"] - ["Filter by Status" package-menu-filter-by-status :help "Filter packages by status"] + :help + "Prompt for regexp, display only packages whose name or description matches"] + ["Filter by Status" package-menu-filter-by-status + :help + "Prompt for status(es), display only packages with those statuses"] ["Filter by Upgrades available" package-menu-filter-upgradable - :help "Filter packages by available upgrades"] - ["Filter by Version" package-menu-filter-by-version :help "Filter packages by version"] - ["Filter Marked" package-menu-filter-marked :help "Filter packages marked for upgrade"] - ["Clear Filter" package-menu-clear-filter :help "Clear package list filter"]) - - ["Hide by Regexp" package-menu-hide-package :help "Hide all packages matching a regexp"] + :help "Display only installed packages for which upgrades are available"] + ["Filter by Version" package-menu-filter-by-version + :help + "Prompt for version and comparison operator, display only packages of matching versions"] + ["Filter Marked" package-menu-filter-marked + :help "Display only packages marked for installation or deletion"] + ["Clear Filter" package-menu-clear-filter + :help "Clear package list filtering, display the entire list again"]) + + ["Hide by Regexp" package-menu-hide-package + :help "Toggle visibility of obsolete and unwanted packages"] ["Display Older Versions" package-menu-toggle-hiding :style toggle :selected (not package-menu--hide-packages) :help "Display package even if a newer version is already installed"] @@ -4294,7 +4310,7 @@ package-menu-filter-by-status If STATUS is nil or the empty string, show all packages. When called interactively, prompt for STATUS. To specify -several possible status values, type them seperated by commas." +several possible status values, type them separated by commas." (interactive (list (completing-read "Filter by status: " '("avail-obso" "available" commit a95e7006989b4bcce2196e73b342d6d30a7cd515 Author: Arash Esbati Date: Sat Aug 5 17:39:32 2023 +0200 ; Filter packages available for upgrade via menu bar * lisp/emacs-lisp/package.el (package-menu-mode-menu): Add entry to filter packages available for upgrade via menu bar. (bug#65094) diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index 58ca19f7fe2..83c7380553d 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el @@ -3090,6 +3090,8 @@ package-menu-mode-menu ["Filter by Name or Description" package-menu-filter-by-name-or-description :help "Filter packages by name or description"] ["Filter by Status" package-menu-filter-by-status :help "Filter packages by status"] + ["Filter by Upgrades available" package-menu-filter-upgradable + :help "Filter packages by available upgrades"] ["Filter by Version" package-menu-filter-by-version :help "Filter packages by version"] ["Filter Marked" package-menu-filter-marked :help "Filter packages marked for upgrade"] ["Clear Filter" package-menu-clear-filter :help "Clear package list filter"]) commit adff72dd1d2064101f49d7c87582fc434bbc15c3 Author: Eli Zaretskii Date: Sun Aug 6 09:33:44 2023 +0300 Fix reverting Rmail buffers This bug happened because rmail.el relied on 'revert-buffer' to return non-nil when it succeeds to revert, but a recent change in 'revert-buffer' broke that promise in Emacs 29.1. * lisp/files.el (revert-buffer--default, revert-buffer): Doc fix. (revert-buffer): Return whatever 'revert-buffer-function' returns. (Bug#65071) diff --git a/lisp/files.el b/lisp/files.el index d325729bf4d..29d109ab385 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -6668,7 +6668,10 @@ revert-buffer This function calls the function that `revert-buffer-function' specifies to do the work, with arguments IGNORE-AUTO and NOCONFIRM. The default function runs the hooks `before-revert-hook' and -`after-revert-hook' +`after-revert-hook'. +Return value is whatever `revert-buffer-function' returns. For historical +reasons, that return value is non-nil when `revert-buffer-function' +succeeds in its job and returns non-nil. Reverting a buffer will try to preserve markers in the buffer, but it cannot always preserve all of them. For better results, @@ -6685,17 +6688,20 @@ revert-buffer (revert-buffer-preserve-modes preserve-modes) (state (and (boundp 'read-only-mode--state) (list read-only-mode--state)))) - (funcall (or revert-buffer-function #'revert-buffer--default) - ignore-auto noconfirm) - (when state - (setq buffer-read-only (car state)) - (setq-local read-only-mode--state (car state))))) + ;; Return whatever 'revert-buffer-function' returns. + (prog1 (funcall (or revert-buffer-function #'revert-buffer--default) + ignore-auto noconfirm) + (when state + (setq buffer-read-only (car state)) + (setq-local read-only-mode--state (car state)))))) (defun revert-buffer--default (ignore-auto noconfirm) "Default function for `revert-buffer'. The arguments IGNORE-AUTO and NOCONFIRM are as described for `revert-buffer'. Runs the hooks `before-revert-hook' and `after-revert-hook' at the start and end. +The function returns non-nil if it reverts the buffer; signals +an error if the buffer is not associated with a file. Calls `revert-buffer-insert-file-contents-function' to reread the contents of the visited file, with two arguments: the first is the file