commit 08007a030e03762b888dcfcd64e84f03e5a2d54f (HEAD, refs/remotes/origin/master) Author: Stefan Monnier Date: Fri Jan 26 23:15:57 2024 -0500 pdumper.c: Minor improvements found while debugging * src/pdumper.c (dump_hash_table_list): Remove unused return value; and simplify with `vconcat`. (dump_charset): Don't copy uninitialized fields. diff --git a/src/pdumper.c b/src/pdumper.c index 6b0178227bd..6d0abc5d835 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -2659,13 +2659,11 @@ hash_table_contents (struct Lisp_Hash_Table *h) return key_and_value; } -static dump_off +static void dump_hash_table_list (struct dump_context *ctx) { if (!NILP (ctx->hash_tables)) - return dump_object (ctx, CALLN (Fapply, Qvector, ctx->hash_tables)); - else - return 0; + dump_object (ctx, CALLN (Fvconcat, ctx->hash_tables)); } static hash_table_std_test_t @@ -3222,30 +3220,33 @@ dump_charset (struct dump_context *ctx, int cs_i) const struct charset *cs = charset_table + cs_i; struct charset out; dump_object_start (ctx, &out, sizeof (out)); - DUMP_FIELD_COPY (&out, cs, id); - dump_field_lv (ctx, &out, cs, &cs->attributes, WEIGHT_NORMAL); - DUMP_FIELD_COPY (&out, cs, dimension); - memcpy (out.code_space, &cs->code_space, sizeof (cs->code_space)); - if (cs_i < charset_table_used && cs->code_space_mask) - dump_field_fixup_later (ctx, &out, cs, &cs->code_space_mask); - DUMP_FIELD_COPY (&out, cs, code_linear_p); - DUMP_FIELD_COPY (&out, cs, iso_chars_96); - DUMP_FIELD_COPY (&out, cs, ascii_compatible_p); - DUMP_FIELD_COPY (&out, cs, supplementary_p); - DUMP_FIELD_COPY (&out, cs, compact_codes_p); - DUMP_FIELD_COPY (&out, cs, unified_p); - DUMP_FIELD_COPY (&out, cs, iso_final); - DUMP_FIELD_COPY (&out, cs, iso_revision); - DUMP_FIELD_COPY (&out, cs, emacs_mule_id); - DUMP_FIELD_COPY (&out, cs, method); - DUMP_FIELD_COPY (&out, cs, min_code); - DUMP_FIELD_COPY (&out, cs, max_code); - DUMP_FIELD_COPY (&out, cs, char_index_offset); - DUMP_FIELD_COPY (&out, cs, min_char); - DUMP_FIELD_COPY (&out, cs, max_char); - DUMP_FIELD_COPY (&out, cs, invalid_code); - memcpy (out.fast_map, &cs->fast_map, sizeof (cs->fast_map)); - DUMP_FIELD_COPY (&out, cs, code_offset); + if (cs_i < charset_table_used) /* Don't look at uninitialized data. */ + { + DUMP_FIELD_COPY (&out, cs, id); + dump_field_lv (ctx, &out, cs, &cs->attributes, WEIGHT_NORMAL); + DUMP_FIELD_COPY (&out, cs, dimension); + memcpy (out.code_space, &cs->code_space, sizeof (cs->code_space)); + if (cs->code_space_mask) + dump_field_fixup_later (ctx, &out, cs, &cs->code_space_mask); + DUMP_FIELD_COPY (&out, cs, code_linear_p); + DUMP_FIELD_COPY (&out, cs, iso_chars_96); + DUMP_FIELD_COPY (&out, cs, ascii_compatible_p); + DUMP_FIELD_COPY (&out, cs, supplementary_p); + DUMP_FIELD_COPY (&out, cs, compact_codes_p); + DUMP_FIELD_COPY (&out, cs, unified_p); + DUMP_FIELD_COPY (&out, cs, iso_final); + DUMP_FIELD_COPY (&out, cs, iso_revision); + DUMP_FIELD_COPY (&out, cs, emacs_mule_id); + DUMP_FIELD_COPY (&out, cs, method); + DUMP_FIELD_COPY (&out, cs, min_code); + DUMP_FIELD_COPY (&out, cs, max_code); + DUMP_FIELD_COPY (&out, cs, char_index_offset); + DUMP_FIELD_COPY (&out, cs, min_char); + DUMP_FIELD_COPY (&out, cs, max_char); + DUMP_FIELD_COPY (&out, cs, invalid_code); + memcpy (out.fast_map, &cs->fast_map, sizeof (cs->fast_map)); + DUMP_FIELD_COPY (&out, cs, code_offset); + } dump_off offset = dump_object_finish (ctx, &out, sizeof (out)); if (cs_i < charset_table_used && cs->code_space_mask) dump_remember_cold_op (ctx, COLD_OP_CHARSET, commit 89734c4f1d2fb9aa18e44481174eb595134f497b Author: Stefan Monnier Date: Fri Jan 26 23:03:00 2024 -0500 pdumder.c: Fix bug#68690 The problem was that the offset computed for the `charset_table` array was a multiple of 4 but the `struct charset` needed an alignment on a multiple of 8, so `dump_charset` inserted 4 bytes of padding, whereas you can't have padding at the beginning of an array. * src/pdumper.c (dump_charset): Don't set alignment here. (dump_charset_table): Set it here instead. diff --git a/src/pdumper.c b/src/pdumper.c index f42d1777371..6b0178227bd 100644 --- a/src/pdumper.c +++ b/src/pdumper.c @@ -3216,7 +3216,9 @@ dump_charset (struct dump_context *ctx, int cs_i) #if CHECK_STRUCTS && !defined (HASH_charset_E31F4B5D96) # error "charset changed. See CHECK_STRUCTS comment in config.h." #endif - dump_align_output (ctx, alignof (struct charset)); + /* We can't change the alignment here, because `offset` is what + will be used for the whole array. */ + eassert (ctx->offset % alignof (struct charset) == 0); const struct charset *cs = charset_table + cs_i; struct charset out; dump_object_start (ctx, &out, sizeof (out)); @@ -3257,7 +3259,7 @@ dump_charset_table (struct dump_context *ctx) { struct dump_flags old_flags = ctx->flags; ctx->flags.pack_objects = true; - dump_align_output (ctx, DUMP_ALIGNMENT); + dump_align_output (ctx, alignof (struct charset)); dump_off offset = ctx->offset; if (dump_set_referrer (ctx)) ctx->current_referrer = build_string ("charset_table"); commit c37b50ad417c6cb340f54ffe218f5d889345451a Author: Po Lu Date: Sat Jan 27 10:36:30 2024 +0800 Intercept calls to `openat' under Android * exec/configure.ac (OPEN_SYSCALL, OPENAT_SYSCALL): Define new macros. * exec/exec.h (struct exec_tracee): New field `sp'. * exec/trace.c (handle_openat): New function. (process_system_call): If handle_openat executes successfully, save the unmodified stack pointer within the tracee structure to be restored once the system call completes. diff --git a/exec/configure.ac b/exec/configure.ac index 9008c84f6a6..d70dbea3477 100644 --- a/exec/configure.ac +++ b/exec/configure.ac @@ -131,6 +131,8 @@ AH_TEMPLATE([CLONE_SYSCALL], [Define to number of the `clone' system call.]) AH_TEMPLATE([CLONE3_SYSCALL], [Define to number of the `clone3' system call.]) AH_TEMPLATE([READLINK_SYSCALL], [Define to number of the `readlink' system call.]) AH_TEMPLATE([READLINKAT_SYSCALL], [Define to number of the `readlinkat' system call.]) +AH_TEMPLATE([OPEN_SYSCALL], [Define to number of the `open' system call.]) +AH_TEMPLATE([OPENAT_SYSCALL], [Define to number of the `openat' system call.]) AH_TEMPLATE([REENTRANT], [Define to 1 if the library is used within a signal handler.]) AC_CANONICAL_HOST @@ -257,6 +259,8 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) + AC_DEFINE([OPEN_SYSCALL], [__NR_open]) + AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) exec_CHECK_LINUX_CLONE3 # Make sure the loader doesn't conflict with other position # dependent code. @@ -285,6 +289,8 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) + AC_DEFINE([OPEN_SYSCALL], [__NR_open]) + AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) exec_CHECK_LINUX_CLONE3 # Make sure the loader doesn't conflict with other position # dependent code. @@ -312,8 +318,9 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([INTERPRETER_BASE], [0x3f00000000]) AC_DEFINE([STACK_GROWS_DOWNWARDS], [1]) AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) - # Note that aarch64 has no `readlink'. + # Note that aarch64 has neither `readlink' nor `open'. AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) + AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) exec_CHECK_LINUX_CLONE3 # Make sure the loader doesn't conflict with other position # dependent code. ARM places rather significant restrictions on @@ -343,6 +350,8 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) + AC_DEFINE([OPEN_SYSCALL], [__NR_open]) + AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) exec_CHECK_LINUX_CLONE3 LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" exec_loader=loader-armeabi.s], @@ -365,6 +374,8 @@ AS_CASE([$host], [x86_64-*linux*], AC_DEFINE([CLONE_SYSCALL], [__NR_clone]) AC_DEFINE([READLINK_SYSCALL], [__NR_readlink]) AC_DEFINE([READLINKAT_SYSCALL], [__NR_readlinkat]) + AC_DEFINE([OPEN_SYSCALL], [__NR_open]) + AC_DEFINE([OPENAT_SYSCALL], [__NR_openat]) exec_CHECK_LINUX_CLONE3 LOADERFLAGS="$LOADERFLAGS $LDPREFIX-Ttext=0x20000000" exec_loader=loader-armeabi.s], diff --git a/exec/exec.h b/exec/exec.h index bed5edc9bab..ad1b50276c8 100644 --- a/exec/exec.h +++ b/exec/exec.h @@ -148,6 +148,10 @@ struct exec_tracee /* The next process being traced. */ struct exec_tracee *next; + /* Address of any stack pointer to restore after system call + completion. */ + USER_WORD sp; + /* The thread ID of this process. */ pid_t pid; diff --git a/exec/trace.c b/exec/trace.c index 8e190c94f79..a7cbda54d68 100644 --- a/exec/trace.c +++ b/exec/trace.c @@ -961,7 +961,7 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs, return 0; /* Copy over tracee->exec_file. Truncate it to PATH_MAX, length, or - size, whichever is less. */ + size, whichever is smaller. */ length = strlen (tracee->exec_file); length = MIN (size, MIN (PATH_MAX, length)); @@ -979,6 +979,98 @@ handle_readlinkat (USER_WORD callno, USER_REGS_STRUCT *regs, #endif /* REENTRANT */ } +/* Handle an `open' or `openat' system call. + + CALLNO is the system call number, and REGS are the current user + registers of the TRACEE. + + If the file name specified in such system call is `/proc/self/exe', + replace the file name with the executable loaded into the process + issuing this system call. + + Value is 0 upon success and 1 upon failure. */ + +static int +handle_openat (USER_WORD callno, USER_REGS_STRUCT *regs, + struct exec_tracee *tracee, USER_WORD *result) +{ +#ifdef REENTRANT + /* readlinkat cannot be handled specially when the library is built + to be reentrant, as the file name information cannot be + recorded. */ + return 0; +#else /* !REENTRANT */ + char buffer[PATH_MAX + 1]; + USER_WORD address; + size_t length; + USER_REGS_STRUCT original; + + /* Read the file name. */ + +#ifdef OPEN_SYSCALL + if (callno == OPEN_SYSCALL) + address = regs->SYSCALL_ARG_REG; + else +#endif /* OPEN_SYSCALL */ + address = regs->SYSCALL_ARG1_REG; + + /* Read the file name into the buffer and verify that it is NULL + terminated. */ + read_memory (tracee, buffer, PATH_MAX, address); + + if (!memchr (buffer, '\0', PATH_MAX)) + { + errno = ENAMETOOLONG; + return 1; + } + + /* Now check if the caller is looking for /proc/self/exe. + + dirfd can be ignored, as for now only absolute file names are + handled. FIXME. */ + + if (strcmp (buffer, "/proc/self/exe") || !tracee->exec_file) + return 0; + + /* Copy over tracee->exec_file. This doesn't correctly handle the + scenario where tracee->exec_file is longer than PATH_MAX, but + that has yet to be encountered in practice. */ + + original = *regs; + length = strlen (tracee->exec_file); + address = user_alloca (tracee, &original, regs, length + 1); + + if (!address + || user_copy (tracee, (unsigned char *) tracee->exec_file, + address, length)) + goto fail; + + /* Replace the file name buffer with ADDRESS. */ + +#ifdef OPEN_SYSCALL + if (callno == OPEN_SYSCALL) + regs->SYSCALL_ARG_REG = address; + else +#endif /* OPEN_SYSCALL */ + regs->SYSCALL_ARG1_REG = address; + +#ifdef __aarch64__ + if (aarch64_set_regs (tracee->pid, regs, false)) + goto fail; +#else /* !__aarch64__ */ + if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, regs)) + goto fail; +#endif /* __aarch64__ */ + + /* Resume the system call. */ + return 0; + + fail: + errno = EIO; + return 1; +#endif /* REENTRANT */ +} + /* Process the system call at which TRACEE is stopped. If the system call is not known or not exec, send TRACEE on its way. Otherwise, rewrite it to load the loader and perform an appropriate action. */ @@ -1056,9 +1148,50 @@ process_system_call (struct exec_tracee *tracee) goto emulate_syscall; } + goto continue_syscall; + +#ifdef OPEN_SYSCALL + case OPEN_SYSCALL: +#endif /* OPEN_SYSCALL */ + case OPENAT_SYSCALL: + + /* This system call is already in progress if + TRACEE->waiting_for_syscall is true. */ + + if (!tracee->waiting_for_syscall) + { + /* Handle this open system call. */ + rc = handle_openat (callno, ®s, tracee, &result); + + /* rc means the same as in `handle_exec', except that `open' + is never emulated. */ + + if (rc == 1) + goto report_syscall_error; + + /* The stack pointer must be restored after it was modified + by `user_alloca'; record sp in TRACEE, which will be + restored after this system call completes. */ + tracee->sp = sp; + } + else + { + /* Restore that stack pointer. */ + regs.STACK_POINTER = tracee->sp; + +#ifdef __aarch64__ + if (aarch64_set_regs (tracee->pid, ®s, true)) + return; +#else /* !__aarch64__ */ + if (ptrace (PTRACE_SETREGS, tracee->pid, NULL, ®s)) + return; +#endif /* __aarch64__ */ + } + /* Fallthrough. */ default: + continue_syscall: /* Don't wait for the system call to finish; instead, the system will DTRT upon the next call to PTRACE_SYSCALL after the syscall-trap signal is delivered. */ commit 55f0b3e561034a1ad4235770d1c0685439a64fe5 Author: Po Lu Date: Sat Jan 27 09:38:46 2024 +0800 Move Visual Wrap menu to menu-bar.el * lisp/menu-bar.el (menu-bar-line-wrapping-menu): Insert new menu item controlling visual-wrap-prefix-mode. * lisp/visual-wrap.el: Remove menu bar autoload. diff --git a/lisp/menu-bar.el b/lisp/menu-bar.el index 477e3036b47..761f0603c75 100644 --- a/lisp/menu-bar.el +++ b/lisp/menu-bar.el @@ -1467,6 +1467,13 @@ mail status in mode line")) (not word-wrap))) :visible (menu-bar-menu-frame-live-and-visible-p) :enable (not (truncated-partial-width-window-p)))) + + (bindings--define-key menu [visual-wrap] + '(menu-item "Visual Wrap Prefix" visual-wrap-prefix-mode + :help "Display continuation lines with contextual prefix" + :visible (menu-bar-menu-frame-live-and-visible-p) + :button (:toggle . (bound-and-true-p visual-wrap-prefix-mode)) + :enable t)) menu)) (defvar menu-bar-search-options-menu diff --git a/lisp/visual-wrap.el b/lisp/visual-wrap.el index 6e6e45a480c..f8e00b9c685 100644 --- a/lisp/visual-wrap.el +++ b/lisp/visual-wrap.el @@ -1,4 +1,4 @@ -;;; visual-wrap.el --- Smart line-wrapping with wrap-prefix +;;; visual-wrap.el --- Smart line-wrapping with wrap-prefix -*- lexical-binding: t -*- ;; Copyright (C) 2011-2021, 2024 Free Software Foundation, Inc. @@ -190,14 +190,5 @@ extra indent = 2 (widen) (remove-text-properties (point-min) (point-max) '(wrap-prefix nil)))))) -;;;###autoload -(define-key-after (lookup-key menu-bar-options-menu [line-wrapping]) - [visual-wrap] - '(menu-item "Visual Wrap" visual-wrap-prefix-mode - :visible (menu-bar-menu-frame-live-and-visible-p) - :help "Display continuation lines with prefix derived from context" - :button (:toggle . (bound-and-true-p visual-wrap-prefix-mode))) - word-wrap) - (provide 'visual-wrap) ;;; visual-wrap.el ends here commit d50300c50028a81ade1eb08405036168d5a24f00 Author: Po Lu Date: Sat Jan 27 09:31:38 2024 +0800 * lisp/visual-wrap.el (visual-wrap-prefix-mode): Improve doc string. diff --git a/lisp/visual-wrap.el b/lisp/visual-wrap.el index 9f52a1868c1..6e6e45a480c 100644 --- a/lisp/visual-wrap.el +++ b/lisp/visual-wrap.el @@ -171,7 +171,7 @@ extra indent = 2 ;;;###autoload (define-minor-mode visual-wrap-prefix-mode - "Wrap the buffer text with visual filling." + "Display continuation lines with prefixes from surrounding context." :lighter "" :group 'visual-line (if visual-wrap-prefix-mode commit 6667d6c19c3934871ed54d89dc153efc72f947de Author: Po Lu Date: Sat Jan 27 09:30:16 2024 +0800 Import ELPA package adaptive-wrap as visual-wrap * doc/emacs/basic.texi (Continuation Lines): Document visual-wrap and its applications. * etc/NEWS (Editing Changes in Emacs 30.1): Ditto. * lisp/visual-wrap.el (visual-wrap-extra-indent) (visual-wrap--face-extend-p, visual-wrap--prefix-face) (visual-wrap--prefix, visual-wrap-fill-context-prefix) (visual-wrap-prefix-function, visual-wrap-prefix-mode, lookup-key) (visual-wrap): New file. Update copyright years and rename to `visual-wrap'. diff --git a/doc/emacs/basic.texi b/doc/emacs/basic.texi index f64b3995d25..a6b71db4bea 100644 --- a/doc/emacs/basic.texi +++ b/doc/emacs/basic.texi @@ -630,6 +630,17 @@ before they get too long, by inserting newlines. If you prefer, you can make Emacs insert a newline automatically when a line gets too long, by using Auto Fill mode. @xref{Filling}. +@cindex continuation lines, wrapping with prefix +@findex visual-wrap-prefix-mode + Normally, the first character of each continuation line is +positioned at the beginning of the screen line where it is displayed. +The minor mode @code{visual-wrap-prefix-mode} arranges that +continuation lines be prefixed by slightly adjusted versions of the +fill prefixes (@pxref{Fill Prefix}) of their respective logical lines, +so that indentation characters or the prefixes of source code comments +are replicated across every continuation line, and the appearance of +such comments or indentation is not broken. + Sometimes, you may need to edit files containing many long logical lines, and it may not be practical to break them all up by adding newlines. In that case, you can use Visual Line mode, which enables diff --git a/etc/NEWS b/etc/NEWS index 37264f2f1f1..37a017c4db1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -317,6 +317,18 @@ name detection. * Editing Changes in Emacs 30.1 ++++ +** New minor mode 'visual-wrap-prefix-mode'. + +When enabled, continuation lines displayed for a folded long line will +receive a 'wrap-prefix' automatically computed from the line's +surrounding context by the function 'fill-context-prefix', which +generally indents continuation lines as if the line were filled with +'M-q', or similar. + +This minor mode is the 'adaptive-wrap' ELPA package renamed and +lightly edited for inclusion in Emacs. + +++ ** New user option 'gud-highlight-current-line'. When enabled, Gud will visually emphasize the line being executed upon diff --git a/lisp/visual-wrap.el b/lisp/visual-wrap.el new file mode 100644 index 00000000000..9f52a1868c1 --- /dev/null +++ b/lisp/visual-wrap.el @@ -0,0 +1,203 @@ +;;; visual-wrap.el --- Smart line-wrapping with wrap-prefix + +;; Copyright (C) 2011-2021, 2024 Free Software Foundation, Inc. + +;; Author: Stephen Berman +;; Stefan Monnier +;; Maintainer: emacs-devel@gnu.org +;; Keywords: convenience +;; Package: emacs + +;; This file is part of GNU Emacs. + +;; 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 . + +;;; Commentary: + +;; This package provides the `visual-wrap-prefix-mode' minor mode +;; which sets the wrap-prefix property on the fly so that +;; single-long-line paragraphs get word-wrapped in a way similar to +;; what you'd get with M-q using visual-fill-mode, but without +;; actually changing the buffer's text. + +;;; Code: + +(defcustom visual-wrap-extra-indent 0 + "Number of extra spaces to indent in `visual-wrap-prefix-mode'. + +`visual-wrap-prefix-mode' indents the visual lines to the level +of the actual line plus `visual-wrap-extra-indent'. A negative +value will do a relative de-indent. + +Examples: + +actual indent = 2 +extra indent = -1 + + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat. + +actual indent = 2 +extra indent = 2 + + Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed + do eiusmod tempor incididunt ut labore et dolore magna + aliqua. Ut enim ad minim veniam, quis nostrud exercitation + ullamco laboris nisi ut aliquip ex ea commodo consequat." + :type 'integer + :safe 'integerp + :group 'visual-line) + +(defun visual-wrap--face-extend-p (face) + ;; Before Emacs 27, faces always extended beyond EOL, so we check + ;; for a non-default background instead. + (cond + ((listp face) + (plist-get face (if (fboundp 'face-extend-p) :extend :background))) + ((symbolp face) + (if (fboundp 'face-extend-p) + (face-extend-p face nil t) + (face-background face nil t))))) + +(defun visual-wrap--prefix-face (fcp _beg end) + ;; If the fill-context-prefix already specifies a face, just use that. + (cond ((get-text-property 0 'face fcp)) + ;; Else, if the last character is a newline and has a face + ;; that extends beyond EOL, assume that this face spans the + ;; whole line and apply it to the prefix to preserve the + ;; "block" visual effect. + ;; + ;; NB: the face might not actually span the whole line: see + ;; for example removed lines in diff-mode, where the first + ;; character has the diff-indicator-removed face, while the + ;; rest of the line has the diff-removed face. + ((= (char-before end) ?\n) + (let ((eol-face (get-text-property (1- end) 'face))) + ;; `eol-face' can be a face, a "face value" + ;; (plist of face properties) or a list of one of those. + (if (or (not (consp eol-face)) (keywordp (car eol-face))) + ;; A single face. + (if (visual-wrap--face-extend-p eol-face) eol-face) + ;; A list of faces. Keep the ones that extend beyond EOL. + (delq nil (mapcar (lambda (f) + (if (visual-wrap--face-extend-p f) f)) + eol-face))))))) + +(defun visual-wrap--prefix (fcp) + (let ((fcp-len (string-width fcp))) + (cond + ((= 0 visual-wrap-extra-indent) + fcp) + ((< 0 visual-wrap-extra-indent) + (concat fcp (make-string visual-wrap-extra-indent ?\s))) + ((< 0 (+ visual-wrap-extra-indent fcp-len)) + (substring fcp + 0 + (+ visual-wrap-extra-indent fcp-len))) + (t + "")))) + +(defun visual-wrap-fill-context-prefix (beg end) + "Like `fill-context-prefix', but with length adjusted by +`visual-wrap-extra-indent'." + (let* ((fcp + ;; `fill-context-prefix' ignores prefixes that look like + ;; paragraph starts, in order to avoid inadvertently + ;; creating a new paragraph while filling, but here we're + ;; only dealing with single-line "paragraphs" and we don't + ;; actually modify the buffer, so this restriction doesn't + ;; make much sense (and is positively harmful in + ;; taskpaper-mode where paragraph-start matches everything). + (or (let ((paragraph-start "\\`\\'a")) + (fill-context-prefix beg end)) + ;; Note: fill-context-prefix may return nil; See: + ;; http://article.gmane.org/gmane.emacs.devel/156285 + "")) + (prefix (visual-wrap--prefix fcp)) + (face (visual-wrap--prefix-face fcp beg end))) + (if face + (propertize prefix 'face face) + prefix))) + +(defun visual-wrap-prefix-function (beg end) + "Indent the region between BEG and END with visual filling." + ;; Any change at the beginning of a line might change its wrap + ;; prefix, which affects the whole line. So we need to "round-up" + ;; `end' to the nearest end of line. We do the same with `beg' + ;; although it's probably not needed. + (goto-char end) + (unless (bolp) (forward-line 1)) + (setq end (point)) + (goto-char beg) + (forward-line 0) + (setq beg (point)) + (while (< (point) end) + (let ((lbp (point))) + (put-text-property + (point) (progn (search-forward "\n" end 'move) (point)) + 'wrap-prefix + (let ((pfx (visual-wrap-fill-context-prefix + lbp (point)))) + ;; Remove any `wrap-prefix' property that might have been + ;; added earlier. Otherwise, we end up with a string + ;; containing a `wrap-prefix' string containing a + ;; `wrap-prefix' string ... + (remove-text-properties + 0 (length pfx) '(wrap-prefix) pfx) + (let ((dp (get-text-property 0 'display pfx))) + (when (and dp (eq dp (get-text-property (1- lbp) 'display))) + ;; There's a `display' property which covers not just the + ;; prefix but also the previous newline. So it's not + ;; just making the prefix more pretty and could interfere + ;; or even defeat our efforts (e.g. it comes from + ;; `visual-fill-mode'). + (remove-text-properties + 0 (length pfx) '(display) pfx))) + pfx)))) + `(jit-lock-bounds ,beg . ,end)) + +;;;###autoload +(define-minor-mode visual-wrap-prefix-mode + "Wrap the buffer text with visual filling." + :lighter "" + :group 'visual-line + (if visual-wrap-prefix-mode + (progn + ;; HACK ATTACK! We want to run after font-lock (so our + ;; wrap-prefix includes the faces applied by font-lock), but + ;; jit-lock-register doesn't accept an `append' argument, so + ;; we add ourselves beforehand, to make sure we're at the end + ;; of the hook (bug#15155). + (add-hook 'jit-lock-functions + #'visual-wrap-prefix-function 'append t) + (jit-lock-register #'visual-wrap-prefix-function)) + (jit-lock-unregister #'visual-wrap-prefix-function) + (with-silent-modifications + (save-restriction + (widen) + (remove-text-properties (point-min) (point-max) '(wrap-prefix nil)))))) + +;;;###autoload +(define-key-after (lookup-key menu-bar-options-menu [line-wrapping]) + [visual-wrap] + '(menu-item "Visual Wrap" visual-wrap-prefix-mode + :visible (menu-bar-menu-frame-live-and-visible-p) + :help "Display continuation lines with prefix derived from context" + :button (:toggle . (bound-and-true-p visual-wrap-prefix-mode))) + word-wrap) + +(provide 'visual-wrap) +;;; visual-wrap.el ends here commit 972466dce268c5697f47a7f342b13dbf01f23a39 Author: João Távora Date: Fri Jan 26 17:18:55 2024 -0600 Eglot: fix eglot--dumb-tryc for "only possible completion" case * lisp/progmodes/eglot.el (eglot--dumb-tryc): Fix for "only possible completion" case. diff --git a/lisp/progmodes/eglot.el b/lisp/progmodes/eglot.el index 40837074573..beba268f923 100644 --- a/lisp/progmodes/eglot.el +++ b/lisp/progmodes/eglot.el @@ -3061,9 +3061,10 @@ for which LSP on-type-formatting should be requested." (defun eglot--dumb-allc (pat table pred _point) (funcall table pat pred t)) (defun eglot--dumb-tryc (pat table pred point) - (if-let ((probe (funcall table pat pred nil))) - (cons probe (length probe)) - (cons pat point))) + (let ((probe (funcall table pat pred nil))) + (cond ((eq probe t) t) + (probe (cons probe (length probe))) + (t (cons pat point))))) (add-to-list 'completion-category-defaults '(eglot-capf (styles eglot--dumb-flex))) (add-to-list 'completion-styles-alist '(eglot--dumb-flex eglot--dumb-tryc eglot--dumb-allc)) commit b803d10d5fa550338e184baded42cc17d01c866e Author: Jim Porter Date: Fri Jan 26 11:56:47 2024 -0800 ; Add docstring to test Note to self: coffee first, *then* push upstream. * test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test--deftest-invoke-directly): Add missing docstring. diff --git a/test/lisp/eshell/esh-cmd-tests.el b/test/lisp/eshell/esh-cmd-tests.el index 741333ff52b..ef965a896c1 100644 --- a/test/lisp/eshell/esh-cmd-tests.el +++ b/test/lisp/eshell/esh-cmd-tests.el @@ -472,7 +472,8 @@ This tests when `eshell-lisp-form-nil-is-failure' is nil." ;; Direct invocation (defmacro esh-cmd-test--deftest-invoke-directly (name command expected) - "FIXME" + "Test `eshell-invoke-directly-p' returns EXPECTED for COMMAND. +NAME is the name of the test case." (declare (indent 2)) `(ert-deftest ,(intern (concat "esh-cmd-test/invoke-directly/" (symbol-name name))) commit 9d985f24a7fe298980f9af7b9e44318d145cfae5 Author: Eli Zaretskii Date: Fri Jan 26 21:02:10 2024 +0200 ; * etc/DEBUG: Add a blurb about disabling ASLR on macOS. diff --git a/etc/DEBUG b/etc/DEBUG index 6c7f4040b8d..4eae090621f 100644 --- a/etc/DEBUG +++ b/etc/DEBUG @@ -1111,6 +1111,9 @@ edit the PE header of the Emacs executable file and reset the DYNAMIC_BASE (0x40) flag in the DllCharacteristics flags recorded by the PE header. +On macOS, there's no official way for disabling ASLR, but there are +various hacks that can be found by searching the Internet. + ** How to recover buffer contents from an Emacs core dump file The file etc/emacs-buffer.gdb defines a set of GDB commands for commit 330284e7f6d5191bc107caaa1c6e8d4b319af259 Author: Jim Porter Date: Fri Jan 26 10:26:57 2024 -0800 ; * test/lisp/eshell/esh-cmd-tests.el: Fix last change. diff --git a/test/lisp/eshell/esh-cmd-tests.el b/test/lisp/eshell/esh-cmd-tests.el index c37e6d14187..741333ff52b 100644 --- a/test/lisp/eshell/esh-cmd-tests.el +++ b/test/lisp/eshell/esh-cmd-tests.el @@ -475,9 +475,10 @@ This tests when `eshell-lisp-form-nil-is-failure' is nil." "FIXME" (declare (indent 2)) `(ert-deftest ,(intern (concat "esh-cmd-test/invoke-directly/" - (symbol-name name))) () + (symbol-name name))) + () (with-temp-eshell - (should (equal (eshell-invoke-directly + (should (equal (eshell-invoke-directly-p (eshell-parse-command ,command nil t)) ,expected))))) commit 047607f6e611709f89f6c93ae0e2fc97b25bf18f Author: Jim Porter Date: Fri Jan 26 10:17:19 2024 -0800 Fix detection of directly-invokable commands in Eshell I think this regressed partly due to eef32d13da58, so let's add some regression tests to make sure that doesn't happen again. * lisp/eshell/em-unix.el (eshell-unix-initialize): Add "compile". * lisp/eshell/esh-cmd.el (eshell--find-subcommands): Yield the second element of the subcommand. (eshell--invoke-command-directly-p): Rename and account for 'eshell-with-copied-handles'. (eshell-invoke-directly): Rename to... (eshell-invoke-directly-p): ... this, and use 'pcase' to make the logic clearer. * lisp/eshell/esh-mode.el (eshell-send-input): Always queue input if the process is running; rename some locals to be clearer. * lisp/eshell/esh-var.el (eshell-var-initialize): Add "env" as a complex command. * test/lisp/eshell/esh-cmd-tests.el (esh-cmd-test--deftest-invoke-directly): New macro. (no-args, with-args, multiple-cmds, subcmd, complex, complex-subcmd): New test cases. diff --git a/lisp/eshell/em-unix.el b/lisp/eshell/em-unix.el index b066e9eeb8e..dad02206759 100644 --- a/lisp/eshell/em-unix.el +++ b/lisp/eshell/em-unix.el @@ -166,9 +166,9 @@ Otherwise, Emacs will attempt to use rsh to invoke du on the remote machine." (add-hook 'pcomplete-try-first-hook 'eshell-complete-host-reference nil t)) (setq-local eshell-complex-commands - (append '("grep" "egrep" "fgrep" "agrep" "rgrep" - "glimpse" "locate" "cat" "time" "cp" "mv" - "make" "du" "diff") + (append '("compile" "grep" "egrep" "fgrep" "agrep" + "rgrep" "glimpse" "locate" "cat" "time" "cp" + "mv" "make" "du" "diff") eshell-complex-commands))) (defalias 'eshell/date 'current-time-string) diff --git a/lisp/eshell/esh-cmd.el b/lisp/eshell/esh-cmd.el index 2746800ea78..30494bafb48 100644 --- a/lisp/eshell/esh-cmd.el +++ b/lisp/eshell/esh-cmd.el @@ -934,48 +934,52 @@ This yields the SUBCOMMANDs when found in forms like (dolist (elem haystack) (cond ((eq (car-safe elem) 'eshell-as-subcommand) - (iter-yield (cdr elem))) + (iter-yield (cadr elem))) ((listp elem) (iter-yield-from (eshell--find-subcommands elem)))))) -(defun eshell--invoke-command-directly (command) +(defun eshell--invoke-command-directly-p (command) "Determine whether the given COMMAND can be invoked directly. COMMAND should be a non-top-level Eshell command in parsed form. A command can be invoked directly if all of the following are true: * The command is of the form - \"(eshell-trap-errors (eshell-named-command NAME ARGS))\", - where ARGS is optional. + (eshell-with-copied-handles + (eshell-trap-errors (eshell-named-command NAME [ARGS])) _). * NAME is a string referring to an alias function and isn't a complex command (see `eshell-complex-commands'). * Any subcommands in ARGS can also be invoked directly." - (when (and (eq (car command) 'eshell-trap-errors) - (eq (car (cadr command)) 'eshell-named-command)) - (let ((name (cadr (cadr command))) - (args (cdr-safe (nth 2 (cadr command))))) - (and name (stringp name) - (not (member name eshell-complex-commands)) - (catch 'simple - (dolist (pred eshell-complex-commands t) - (when (and (functionp pred) - (funcall pred name)) - (throw 'simple nil)))) - (eshell-find-alias-function name) - (catch 'indirect-subcommand - (iter-do (subcommand (eshell--find-subcommands args)) - (unless (eshell--invoke-command-directly subcommand) - (throw 'indirect-subcommand nil))) - t))))) - -(defun eshell-invoke-directly (command) + (pcase command + (`(eshell-with-copied-handles + (eshell-trap-errors (eshell-named-command ,name . ,args)) + ,_) + (and name (stringp name) + (not (member name eshell-complex-commands)) + (catch 'simple + (dolist (pred eshell-complex-commands t) + (when (and (functionp pred) + (funcall pred name)) + (throw 'simple nil)))) + (eshell-find-alias-function name) + (catch 'indirect-subcommand + (iter-do (subcommand (eshell--find-subcommands (car args))) + (unless (eshell--invoke-command-directly-p subcommand) + (throw 'indirect-subcommand nil))) + t))))) + +(defun eshell-invoke-directly-p (command) "Determine whether the given COMMAND can be invoked directly. COMMAND should be a top-level Eshell command in parsed form, as produced by `eshell-parse-command'." - (let ((base (cadr (nth 2 (nth 2 (cadr command)))))) - (eshell--invoke-command-directly base))) + (pcase command + (`(eshell-commands (progn ,_ (unwind-protect (progn ,base) . ,_))) + (eshell--invoke-command-directly-p base)))) + +(define-obsolete-function-alias 'eshell-invoke-directly + 'eshell-invoke-directly-p "30.1") (defun eshell-eval-argument (argument) "Evaluate a single Eshell ARGUMENT and return the result." diff --git a/lisp/eshell/esh-mode.el b/lisp/eshell/esh-mode.el index 21e3f00086f..fd279f61673 100644 --- a/lisp/eshell/esh-mode.el +++ b/lisp/eshell/esh-mode.el @@ -619,14 +619,14 @@ If NO-NEWLINE is non-nil, the input is sent without an implied final newline." (interactive "P") ;; Note that the input string does not include its terminal newline. - (let ((proc-running-p (and (eshell-head-process) - (not queue-p))) - (inhibit-modification-hooks t)) - (unless (and proc-running-p + (let* ((proc-running-p (eshell-head-process)) + (send-to-process-p (and proc-running-p (not queue-p))) + (inhibit-modification-hooks t)) + (unless (and send-to-process-p (not (eq (process-status (eshell-head-process)) 'run))) - (if (or proc-running-p + (if (or send-to-process-p (>= (point) eshell-last-output-end)) (goto-char (point-max)) (let ((copy (eshell-get-old-input use-region))) @@ -634,7 +634,7 @@ newline." (insert-and-inherit copy))) (unless (or no-newline (and eshell-send-direct-to-subprocesses - proc-running-p)) + send-to-process-p)) (insert-before-markers-and-inherit ?\n)) ;; Delete and reinsert input. This seems like a no-op, except ;; for the resulting entries in the undo list: undoing this @@ -644,7 +644,7 @@ newline." (inhibit-read-only t)) (delete-region eshell-last-output-end (point)) (insert text)) - (if proc-running-p + (if send-to-process-p (progn (eshell-update-markers eshell-last-output-end) (if (or eshell-send-direct-to-subprocesses @@ -673,7 +673,8 @@ newline." (run-hooks 'eshell-input-filter-functions) (and (catch 'eshell-terminal (ignore - (if (eshell-invoke-directly cmd) + (if (and (not proc-running-p) + (eshell-invoke-directly-p cmd)) (eval cmd) (eshell-eval-command cmd input)))) (eshell-life-is-too-much))))) diff --git a/lisp/eshell/esh-var.el b/lisp/eshell/esh-var.el index 627cbb17797..537bc4b0641 100644 --- a/lisp/eshell/esh-var.el +++ b/lisp/eshell/esh-var.el @@ -287,6 +287,8 @@ This is set to t in `eshell-local-variable-bindings' (which see).") (setq-local eshell-subcommand-bindings (append eshell-local-variable-bindings eshell-subcommand-bindings)) + (setq-local eshell-complex-commands + (append '("env") eshell-complex-commands)) (setq-local eshell-special-chars-inside-quoting (append eshell-special-chars-inside-quoting '(?$))) diff --git a/test/lisp/eshell/esh-cmd-tests.el b/test/lisp/eshell/esh-cmd-tests.el index be31681267b..c37e6d14187 100644 --- a/test/lisp/eshell/esh-cmd-tests.el +++ b/test/lisp/eshell/esh-cmd-tests.el @@ -468,6 +468,26 @@ This tests when `eshell-lisp-form-nil-is-failure' is nil." (eshell-command-result-equal "unless {[ foo = bar ]} {echo no} {echo yes}" "no")) + +;; Direct invocation + +(defmacro esh-cmd-test--deftest-invoke-directly (name command expected) + "FIXME" + (declare (indent 2)) + `(ert-deftest ,(intern (concat "esh-cmd-test/invoke-directly/" + (symbol-name name))) () + (with-temp-eshell + (should (equal (eshell-invoke-directly + (eshell-parse-command ,command nil t)) + ,expected))))) + +(esh-cmd-test--deftest-invoke-directly no-args "echo" t) +(esh-cmd-test--deftest-invoke-directly with-args "echo hi" t) +(esh-cmd-test--deftest-invoke-directly multiple-cmds "echo hi; echo bye" nil) +(esh-cmd-test--deftest-invoke-directly subcmd "echo ${echo hi}" t) +(esh-cmd-test--deftest-invoke-directly complex "ls ." nil) +(esh-cmd-test--deftest-invoke-directly complex-subcmd "echo {ls .}" nil) + ;; Error handling commit de020255a5cef4349d786fceb19481352c49557b Author: Eli Zaretskii Date: Fri Jan 26 15:01:51 2024 +0200 Fix crash backtraces on MS-Windows, broken by ASLR * src/w32fns.c (DEFAULT_IMAGE_BASE): Define for 64-bit and 32-bit MinGW builds. (emacs_abort): Correct the callstack addresses for potential relocation of the image base due to ASLR. This makes 'addr2line' be able to interpret emacs_backtrace.txt when ASLR is in effect, which it is on every modern version of MS-Windows. (Bug#63365) * configure.ac (LD_SWITCH_SYSTEM_TEMACS) [mingw32]: Add comment about keeping the image-base values in sync with w32fns.c. * etc/DEBUG (How to disable ASLR): New section. diff --git a/configure.ac b/configure.ac index 55f742ba8ef..fa8b04ec685 100644 --- a/configure.ac +++ b/configure.ac @@ -7463,6 +7463,8 @@ case "$opsys" in mingw32) ## Is it any better under MinGW64 to relocate emacs into higher addresses? + ## If the values of -image-base are modified, the corresponding + ## values of DEFAULT_IMAGE_BASE in w32fns.c should be kept in sync. case "$canonical" in x86_64-*-*) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x400000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;; *) LD_SWITCH_SYSTEM_TEMACS="-Wl,-stack,0x00800000 -Wl,-heap,0x00100000 -Wl,-image-base,0x01000000 -Wl,-entry,__start -Wl,-Map,./temacs.map" ;; diff --git a/etc/DEBUG b/etc/DEBUG index 1680aab4385..6c7f4040b8d 100644 --- a/etc/DEBUG +++ b/etc/DEBUG @@ -928,7 +928,10 @@ data that is modified only very rarely.) It is also useful to look at the corrupted object or data structure in a fresh Emacs session and compare its contents with a session that you -are debugging. +are debugging. This might be somewhat harder on modern systems which +randomize addresses of running executables (the so-called Address +Space Layout Randomization, or ASLR, feature). If you have this +problem, see below under "How to disable ASLR". ** Debugging the TTY (non-windowed) version @@ -1080,6 +1083,34 @@ suppresses some Valgrind false alarms during Emacs garbage collection: Unfortunately Valgrind suppression files tend to be system-dependent, so you will need to keep one around that matches your system. +** How to disable ASLR + +Modern systems use the so-called Address Space Layout Randomization, +(ASLR) feature, which randomizes the base address of running programs, +making it harder for malicious software or hackers to find the address +of some function or variable in a running program by looking at its +executable file. This causes the address of the same symbol to be +different across rerunning of the same program. Sometimes, it can be +useful to disable ASLR, for example, if you want to compare objects in +two different Emacs sessions. + +On GNU/Linux, you can disable ASLR temporarily with the following +shell command: + + echo 0 > /proc/sys/kernel/randomize_va_space + +or by running Emacs in an environment where ASLR is temporarily +disabled: + + setarch -R emacs [args...] + +To disable ASLR in Emacs on MS-Windows, you will have to rebuild Emacs +while adding '-Wl,-disable-dynamicbase' to LD_SWITCH_SYSTEM_TEMACS +variable defined in src/Makefile. Alternatively, use some tool to +edit the PE header of the Emacs executable file and reset the +DYNAMIC_BASE (0x40) flag in the DllCharacteristics flags recorded by +the PE header. + ** How to recover buffer contents from an Emacs core dump file The file etc/emacs-buffer.gdb defines a set of GDB commands for diff --git a/src/w32fns.c b/src/w32fns.c index f8de45da7c9..f44460e52c0 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -11121,12 +11121,20 @@ my_exception_handler (EXCEPTION_POINTERS * exception_data) return prev_exception_handler (exception_data); return EXCEPTION_EXECUTE_HANDLER; } -#endif +#endif /* !CYGWIN */ typedef USHORT (WINAPI * CaptureStackBackTrace_proc) (ULONG, ULONG, PVOID *, PULONG); #define BACKTRACE_LIMIT_MAX 62 +/* The below must be kept in sync with the value of the + -Wl,-image-base switch we use in LD_SWITCH_SYSTEM_TEMACS, see + configure.ac. */ +#if defined MINGW_W64 && EMACS_INT_MAX > LONG_MAX +# define DEFAULT_IMAGE_BASE (ptrdiff_t)0x400000000 +#else /* 32-bit MinGW build */ +# define DEFAULT_IMAGE_BASE (ptrdiff_t)0x01000000 +#endif static int w32_backtrace (void **buffer, int limit) @@ -11181,6 +11189,13 @@ emacs_abort (void) { void *stack[BACKTRACE_LIMIT_MAX + 1]; int i = w32_backtrace (stack, BACKTRACE_LIMIT_MAX + 1); +#ifdef CYGWIN + ptrdiff_t addr_offset = 0; +#else /* MinGW */ + /* The offset below is zero unless ASLR is in effect. */ + ptrdiff_t addr_offset + = DEFAULT_IMAGE_BASE - (ptrdiff_t)GetModuleHandle (NULL); +#endif /* MinGW */ if (i) { @@ -11231,8 +11246,13 @@ emacs_abort (void) { /* stack[] gives the return addresses, whereas we want the address of the call, so decrease each address - by approximate size of 1 CALL instruction. */ - sprintf (buf, "%p\r\n", (char *)stack[j] - sizeof(void *)); + by approximate size of 1 CALL instruction. We add + ADDR_OFFSET to account for ASLR which changes the + base address of the program's image in memory, + whereas 'addr2line' needs to see addresses relative + to the fixed base recorded in the PE header. */ + sprintf (buf, "%p\r\n", + (char *)stack[j] - sizeof(void *) + addr_offset); if (stderr_fd >= 0) write (stderr_fd, buf, strlen (buf)); if (errfile_fd >= 0)