From d7c22338d2d461e0b55a6628fed1917c08715292 Mon Sep 17 00:00:00 2001 From: Federico Tedin Date: Mon, 17 Feb 2020 21:58:43 +0100 Subject: Fix cursor-sensor--detect when current buf != selected window's buf * lisp/emacs-lisp/cursor-sensor.el (cursor-sensor--detect): Avoid trying to read text properties from position taken from another buffer. (Bug#38740) --- lisp/emacs-lisp/cursor-sensor.el | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'lisp/emacs-lisp') diff --git a/lisp/emacs-lisp/cursor-sensor.el b/lisp/emacs-lisp/cursor-sensor.el index d8e8eeb16ef..7728e78c471 100644 --- a/lisp/emacs-lisp/cursor-sensor.el +++ b/lisp/emacs-lisp/cursor-sensor.el @@ -146,9 +146,10 @@ By convention, this is a list of symbols where each symbol stands for the ;; It's often desirable to make the cursor-sensor-functions property ;; non-sticky on both ends, but that means get-pos-property might ;; never see it. - (new (or (get-char-property point 'cursor-sensor-functions) - (unless (<= (point-min) point) - (get-char-property (1- point) 'cursor-sensor-functions)))) + (new (and (eq (current-buffer) (window-buffer)) + (or (get-char-property point 'cursor-sensor-functions) + (unless (<= (point-min) point) + (get-char-property (1- point) 'cursor-sensor-functions))))) (old (window-parameter window 'cursor-sensor--last-state)) (oldposmark (car old)) (oldpos (or (if oldposmark (marker-position oldposmark)) -- cgit v1.2.3 From ba7004b2a74c69450114c12ef4521768fc165e8e Mon Sep 17 00:00:00 2001 From: Noam Postavsky Date: Sun, 23 Feb 2020 08:26:35 -0500 Subject: Shorten some ppss struct field names * lisp/emacs-lisp/syntax.el (ppss): Capitalize docstrings. (ppss-comment-depth): Renamed from ppss-comment-nesting. (ppss-quoted-p): Renamed from ppss-after-quote-p. (ppss-min-depth): Renamed from ppss-minimum-paren-depth. (ppss-open-parens): Renamed from ppss-open-paren-positions. * etc/NEWS: Announce the ppss-* accessors. --- etc/NEWS | 8 ++++++++ lisp/emacs-lisp/syntax.el | 26 +++++++++++++------------- 2 files changed, 21 insertions(+), 13 deletions(-) (limited to 'lisp/emacs-lisp') diff --git a/etc/NEWS b/etc/NEWS index 380ac71260d..e9dfd266b46 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3351,6 +3351,14 @@ brackets at the start of a line inside documentation strings with a backslash, although there is no harm in doing so to make the code easier to edit with an older Emacs version. +--- +** New symbolic accessor functions for a parse state list. +The new accessor functions 'ppss-depth', 'ppss-list-start', +'ppss-last-sexp-start', 'ppss-string-terminator', 'comment-depth', +'quoted-p', 'comment-style', 'comment-or-string-start', 'open-parens', +and 'two-character-syntax' can be used on the list value returned by +'parse-partial-sexp' and 'syntax-ppss'. + --- ** The 'server-name' and 'server-socket-dir' variables are set when a socket has been passed to Emacs. diff --git a/lisp/emacs-lisp/syntax.el b/lisp/emacs-lisp/syntax.el index 6b464bcb13a..11cc1988b1f 100644 --- a/lisp/emacs-lisp/syntax.el +++ b/lisp/emacs-lisp/syntax.el @@ -89,33 +89,33 @@ Put first the functions more likely to cause a change and cheaper to compute.") (:constructor make-ppss) (:copier nil) (:type list)) - (depth nil :documentation "depth in parens") + (depth nil :documentation "Depth in parens.") (innermost-start nil :documentation - "character address of start of innermost containing list; nil if none.") + "Character address of start of innermost containing list; nil if none.") (last-complete-sexp-start nil :documentation - "character address of start of last complete sexp terminated.") + "Character address of start of last complete sexp terminated.") (string-terminator nil :documentation "\ -non-nil if inside a string. -(it is the character that will terminate the string, or t if the +Non-nil if inside a string. +\(it is the character that will terminate the string, or t if the string should be terminated by a generic string delimiter.)") - (comment-nesting nil :documentation "\ + (comment-depth nil :documentation "\ nil if outside a comment, t if inside a non-nestable comment, else an integer (the current comment nesting).") - (after-quote-p nil :documentation "t if following a quote character.") - (minimum-paren-depth - nil :documentation "the minimum paren-depth encountered during this scan.") - (comment-style nil :documentation "style of comment, if any.") + (quoted-p nil :documentation "t if following a quote character.") + (min-depth + nil :documentation "The minimum depth in parens encountered during this scan.") + (comment-style nil :documentation "Style of comment, if any.") (comment-or-string-start nil :documentation - "character address of start of comment or string; nil if not in one.") - (open-paren-positions + "Character address of start of comment or string; nil if not in one.") + (open-parens nil :documentation "List of positions of currently open parens, outermost first.") (two-character-syntax nil :documentation "\ When the last position scanned holds the first character of a -(potential) two character construct, the syntax of that position, +\(potential) two character construct, the syntax of that position, otherwise nil. That construct can be a two character comment delimiter or an Escaped or Char-quoted character.")) -- cgit v1.2.3 From 9d626dffc6ba62c0d7a1a5c712f576ed8684fd66 Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Sun, 23 Feb 2020 16:19:42 -0800 Subject: Add 'nofollow' flag to set-file-modes etc. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids some race conditions (Bug#39683). E.g., if some other program changes a file to a symlink between the time Emacs creates the file and the time it changes the file’s permissions, using the new flag prevents Emacs from inadvertently changing the permissions of a victim in some completely unrelated directory. * admin/merge-gnulib (GNULIB_MODULES): Add fchmodat. * doc/lispref/files.texi (Testing Accessibility, Changing Files): * doc/lispref/os.texi (File Notifications): * etc/NEWS: Adjust documentation accordingly. * lib/chmodat.c, lib/fchmodat.c, lib/lchmod.c, m4/fchmodat.m4: * m4/lchmod.m4: New files, copied from Gnulib. * lib/gnulib.mk.in: Regenerate. * lisp/dired-aux.el (dired-do-chmod): * lisp/doc-view.el (doc-view-make-safe-dir): * lisp/emacs-lisp/autoload.el (autoload--save-buffer): * lisp/emacs-lisp/bytecomp.el (byte-compile-file): * lisp/eshell/em-pred.el (eshell-pred-file-mode): * lisp/files.el (backup-buffer-copy, copy-directory): * lisp/gnus/mail-source.el (mail-source-movemail): * lisp/gnus/mm-decode.el (mm-display-external): * lisp/gnus/nnmail.el (nnmail-write-region): * lisp/net/tramp-adb.el (tramp-adb-handle-file-local-copy) (tramp-adb-handle-write-region): * lisp/net/tramp-sh.el (tramp-do-copy-or-rename-file-directly): * lisp/net/tramp-sudoedit.el (tramp-sudoedit-handle-write-region): * lisp/net/tramp.el (tramp-handle-write-region) (tramp-make-tramp-temp-file): * lisp/server.el (server-ensure-safe-dir): * lisp/url/url-util.el (url-make-private-file): When getting or setting file modes, avoid following symbolic links when the file is not supposed to be a symbolic link. * lisp/doc-view.el (doc-view-make-safe-dir): Omit no-longer-needed separate symlink test. * lisp/gnus/gnus-util.el (gnus-set-file-modes): * lisp/net/tramp.el (tramp-handle-file-modes): * lisp/net/tramp-gvfs.el (tramp-gvfs-handle-set-file-modes): * src/fileio.c (symlink_nofollow_flag): New function. (Ffile_modes, Fset_file_modes): Support an optional FLAG arg. All C callers changed. * lisp/net/ange-ftp.el (ange-ftp-set-file-modes): * lisp/net/tramp-adb.el (tramp-adb-handle-set-file-modes): * lisp/net/tramp-sh.el (tramp-sh-handle-set-file-modes): * lisp/net/tramp-smb.el (tramp-smb-handle-set-file-modes): * lisp/net/tramp-sudoedit.el (tramp-sudoedit-handle-set-file-modes): Accept an optional FLAG arg that is currently ignored, and add a FIXME comment for it. * m4/gnulib-comp.m4: Regenerate. --- admin/merge-gnulib | 2 +- doc/lispref/files.texi | 27 +++++++-- doc/lispref/os.texi | 2 +- etc/NEWS | 3 + lib/fchmodat.c | 144 ++++++++++++++++++++++++++++++++++++++++++++ lib/gnulib.mk.in | 26 ++++++++ lib/lchmod.c | 110 +++++++++++++++++++++++++++++++++ lisp/dired-aux.el | 3 +- lisp/doc-view.el | 4 +- lisp/emacs-lisp/autoload.el | 2 +- lisp/emacs-lisp/bytecomp.el | 2 +- lisp/eshell/em-pred.el | 2 +- lisp/files.el | 12 ++-- lisp/gnus/gnus-util.el | 4 +- lisp/gnus/mail-source.el | 2 +- lisp/gnus/mm-decode.el | 2 +- lisp/gnus/nnmail.el | 2 +- lisp/net/ange-ftp.el | 3 +- lisp/net/tramp-adb.el | 9 ++- lisp/net/tramp-gvfs.el | 4 +- lisp/net/tramp-sh.el | 10 +-- lisp/net/tramp-smb.el | 3 +- lisp/net/tramp-sudoedit.el | 6 +- lisp/net/tramp.el | 13 ++-- lisp/server.el | 2 +- lisp/url/url-util.el | 4 +- m4/fchmodat.m4 | 82 +++++++++++++++++++++++++ m4/gnulib-comp.m4 | 35 +++++++++++ m4/lchmod.m4 | 31 ++++++++++ src/fileio.c | 46 ++++++++------ 30 files changed, 533 insertions(+), 64 deletions(-) create mode 100644 lib/fchmodat.c create mode 100644 lib/lchmod.c create mode 100644 m4/fchmodat.m4 create mode 100644 m4/lchmod.m4 (limited to 'lisp/emacs-lisp') diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 48c81e61e2a..557119441e4 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib @@ -33,7 +33,7 @@ GNULIB_MODULES=' crypto/md5-buffer crypto/sha1-buffer crypto/sha256-buffer crypto/sha512-buffer d-type diffseq dosname double-slash-root dtoastr dtotimespec dup2 environ execinfo explicit_bzero faccessat - fcntl fcntl-h fdopendir + fchmodat fcntl fcntl-h fdopendir filemode filevercmp flexmember fpieee fstatat fsusage fsync getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog ieee754-h ignore-value intprops largefile lstat diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index a93da39f174..a69a4e5dd38 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi @@ -928,7 +928,7 @@ also checks that the file's group would be unchanged. This function does not follow symbolic links. @end defun -@defun file-modes filename +@defun file-modes filename &optional flag @cindex mode bits @cindex file permissions @cindex permissions, file @@ -946,12 +946,19 @@ The highest possible value is 4095 (7777 octal), meaning that everyone has read, write, and execute permission, the @acronym{SUID} bit is set for both others and group, and the sticky bit is set. +By default this function follows symbolic links. However, if the +optional argument @var{flag} is the symbol @code{nofollow}, this +function does not follow @var{filename} if it is a symbolic link; +this can help prevent inadvertently obtaining the mode bits of a file +somewhere else, and is more consistent with @code{file-attributes} +(@pxref{File Attributes}). + @xref{Changing Files}, for the @code{set-file-modes} function, which can be used to set these permissions. @example @group -(file-modes "~/junk/diffs") +(file-modes "~/junk/diffs" 'nofollow) @result{} 492 ; @r{Decimal integer.} @end group @group @@ -960,7 +967,7 @@ can be used to set these permissions. @end group @group -(set-file-modes "~/junk/diffs" #o666) +(set-file-modes "~/junk/diffs" #o666 'nofollow) @result{} nil @end group @@ -1801,9 +1808,17 @@ See also @code{delete-directory} in @ref{Create/Delete Dirs}. @cindex file permissions, setting @cindex permissions, file @cindex file modes, setting -@deffn Command set-file-modes filename mode +@deffn Command set-file-modes filename mode &optional flag This function sets the @dfn{file mode} (or @dfn{permissions}) of -@var{filename} to @var{mode}. This function follows symbolic links. +@var{filename} to @var{mode}. + +By default this function follows symbolic links. However, if the +optional argument @var{flag} is the symbol @code{nofollow}, this +function does not follow @var{filename} if it is a symbolic link; +this can help prevent inadvertently changing the mode bits of a file +somewhere else. On platforms that do not support changing mode bits +on a symbolic link, this function signals an error when @var{filename} +is a symbolic link and @var{flag} is @code{nofollow}. If called non-interactively, @var{mode} must be an integer. Only the lowest 12 bits of the integer are used; on most systems, only the @@ -1811,7 +1826,7 @@ lowest 9 bits are meaningful. You can use the Lisp construct for octal numbers to enter @var{mode}. For example, @example -(set-file-modes #o644) +(set-file-modes "myfile" #o644 'nofollow) @end example @noindent diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index a034ccdcd5c..cf4ef52abfb 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -3127,7 +3127,7 @@ being reported. For example: @end group @group -(set-file-modes "/tmp/foo" (default-file-modes)) +(set-file-modes "/tmp/foo" (default-file-modes) 'nofollow) @result{} Event (35025468 attribute-changed "/tmp/foo") @end group @end example diff --git a/etc/NEWS b/etc/NEWS index 02798798367..5ca054363d2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -198,6 +198,9 @@ called when the function object is garbage-collected. Use 'set_function_finalizer' to set the finalizer and 'get_function_finalizer' to retrieve it. +** 'file-modes' and 'set-file-modes' now have an optional argument +specifying whether to follow symbolic links. + ** 'parse-time-string' can now parse ISO 8601 format strings, such as "2020-01-15T16:12:21-08:00". diff --git a/lib/fchmodat.c b/lib/fchmodat.c new file mode 100644 index 00000000000..8950168608f --- /dev/null +++ b/lib/fchmodat.c @@ -0,0 +1,144 @@ +/* Change the protections of file relative to an open directory. + Copyright (C) 2006, 2009-2020 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 Jim Meyering and Paul Eggert */ + +/* If the user's config.h happens to include , let it include only + the system's here, so that orig_fchmodat doesn't recurse to + rpl_fchmodat. */ +#define __need_system_sys_stat_h +#include + +/* Specification. */ +#include +#undef __need_system_sys_stat_h + +#if HAVE_FCHMODAT +static int +orig_fchmodat (int dir, char const *file, mode_t mode, int flags) +{ + return fchmodat (dir, file, mode, flags); +} +#endif + +#include +#include +#include +#include +#include + +#ifdef __osf__ +/* Write "sys/stat.h" here, not , otherwise OSF/1 5.1 DTK cc + eliminates this include because of the preliminary #include + above. */ +# include "sys/stat.h" +#else +# include +#endif + +#include + +/* Invoke chmod or lchmod on FILE, using mode MODE, in the directory + open on descriptor FD. If possible, do it without changing the + working directory. Otherwise, resort to using save_cwd/fchdir, + then (chmod|lchmod)/restore_cwd. If either the save_cwd or the + restore_cwd fails, then give a diagnostic and exit nonzero. + Note that an attempt to use a FLAG value of AT_SYMLINK_NOFOLLOW + on a system without lchmod support causes this function to fail. */ + +#if HAVE_FCHMODAT +int +fchmodat (int dir, char const *file, mode_t mode, int flags) +{ +# if NEED_FCHMODAT_NONSYMLINK_FIX + if (flags == AT_SYMLINK_NOFOLLOW) + { + struct stat st; + +# if defined O_PATH && defined AT_EMPTY_PATH + /* Open a file descriptor with O_NOFOLLOW, to make sure we don't + follow symbolic links, if /proc is mounted. O_PATH is used to + avoid a failure if the file is not readable. + Cf. */ + int fd = openat (dir, file, O_PATH | O_NOFOLLOW | O_CLOEXEC); + if (fd < 0) + return fd; + + /* Up to Linux 5.3 at least, when FILE refers to a symbolic link, the + chmod call below will change the permissions of the symbolic link + - which is undesired - and on many file systems (ext4, btrfs, jfs, + xfs, ..., but not reiserfs) fail with error EOPNOTSUPP - which is + misleading. Therefore test for a symbolic link explicitly. + Use fstatat because fstat does not work on O_PATH descriptors + before Linux 3.6. */ + if (fstatat (fd, "", &st, AT_EMPTY_PATH) != 0) + { + int stat_errno = errno; + close (fd); + errno = stat_errno; + return -1; + } + if (S_ISLNK (st.st_mode)) + { + close (fd); + errno = EOPNOTSUPP; + return -1; + } + +# if defined __linux__ || defined __ANDROID__ + static char const fmt[] = "/proc/self/fd/%d"; + char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)]; + sprintf (buf, fmt, fd); + int chmod_result = chmod (buf, mode); + int chmod_errno = errno; + close (fd); + if (chmod_result == 0) + return chmod_result; + if (chmod_errno != ENOENT) + { + errno = chmod_errno; + return chmod_result; + } +# endif + /* /proc is not mounted or would not work as in GNU/Linux. */ + +# else + int fstatat_result = fstatat (dir, file, &st, AT_SYMLINK_NOFOLLOW); + if (fstatat_result != 0) + return fstatat_result; + if (S_ISLNK (st.st_mode)) + { + errno = EOPNOTSUPP; + return -1; + } +# endif + + /* Fall back on orig_fchmodat with no flags, despite a possible race. */ + flags = 0; + } +# endif + + return orig_fchmodat (dir, file, mode, flags); +} +#else +# define AT_FUNC_NAME fchmodat +# define AT_FUNC_F1 lchmod +# define AT_FUNC_F2 chmod +# define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW +# define AT_FUNC_POST_FILE_PARAM_DECLS , mode_t mode, int flag +# define AT_FUNC_POST_FILE_ARGS , mode +# include "at-func.c" +#endif diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index 3c01e61b266..d4dc6a3df33 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in @@ -95,6 +95,7 @@ # execinfo \ # explicit_bzero \ # faccessat \ +# fchmodat \ # fcntl \ # fcntl-h \ # fdopendir \ @@ -1082,6 +1083,7 @@ gl_GNULIB_ENABLED_dirfd = @gl_GNULIB_ENABLED_dirfd@ gl_GNULIB_ENABLED_euidaccess = @gl_GNULIB_ENABLED_euidaccess@ gl_GNULIB_ENABLED_getdtablesize = @gl_GNULIB_ENABLED_getdtablesize@ gl_GNULIB_ENABLED_getgroups = @gl_GNULIB_ENABLED_getgroups@ +gl_GNULIB_ENABLED_lchmod = @gl_GNULIB_ENABLED_lchmod@ gl_GNULIB_ENABLED_malloca = @gl_GNULIB_ENABLED_malloca@ gl_GNULIB_ENABLED_open = @gl_GNULIB_ENABLED_open@ gl_GNULIB_ENABLED_strtoll = @gl_GNULIB_ENABLED_strtoll@ @@ -1586,6 +1588,17 @@ EXTRA_libgnu_a_SOURCES += at-func.c faccessat.c endif ## end gnulib module faccessat +## begin gnulib module fchmodat +ifeq (,$(OMIT_GNULIB_MODULE_fchmodat)) + + +EXTRA_DIST += at-func.c fchmodat.c + +EXTRA_libgnu_a_SOURCES += at-func.c fchmodat.c + +endif +## end gnulib module fchmodat + ## begin gnulib module fcntl ifeq (,$(OMIT_GNULIB_MODULE_fcntl)) @@ -1936,6 +1949,19 @@ EXTRA_DIST += inttypes.in.h endif ## end gnulib module inttypes-incomplete +## begin gnulib module lchmod +ifeq (,$(OMIT_GNULIB_MODULE_lchmod)) + +ifneq (,$(gl_GNULIB_ENABLED_lchmod)) + +endif +EXTRA_DIST += lchmod.c + +EXTRA_libgnu_a_SOURCES += lchmod.c + +endif +## end gnulib module lchmod + ## begin gnulib module libc-config ifeq (,$(OMIT_GNULIB_MODULE_libc-config)) diff --git a/lib/lchmod.c b/lib/lchmod.c new file mode 100644 index 00000000000..e1132116234 --- /dev/null +++ b/lib/lchmod.c @@ -0,0 +1,110 @@ +/* Implement lchmod on platforms where it does not work correctly. + + Copyright 2020 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 Paul Eggert */ + +#include + +/* Specification. */ +#include + +#include +#include +#include +#include + +#ifdef __osf__ +/* Write "sys/stat.h" here, not , otherwise OSF/1 5.1 DTK cc + eliminates this include because of the preliminary #include + above. */ +# include "sys/stat.h" +#else +# include +#endif + +#include + +/* Work like chmod, except when FILE is a symbolic link. + In that case, on systems where permissions on symbolic links are unsupported + (such as Linux), set errno to EOPNOTSUPP and return -1. */ + +int +lchmod (char const *file, mode_t mode) +{ +#if defined O_PATH && defined AT_EMPTY_PATH + /* Open a file descriptor with O_NOFOLLOW, to make sure we don't + follow symbolic links, if /proc is mounted. O_PATH is used to + avoid a failure if the file is not readable. + Cf. */ + int fd = open (file, O_PATH | O_NOFOLLOW | O_CLOEXEC); + if (fd < 0) + return fd; + + /* Up to Linux 5.3 at least, when FILE refers to a symbolic link, the + chmod call below will change the permissions of the symbolic link + - which is undesired - and on many file systems (ext4, btrfs, jfs, + xfs, ..., but not reiserfs) fail with error EOPNOTSUPP - which is + misleading. Therefore test for a symbolic link explicitly. + Use fstatat because fstat does not work on O_PATH descriptors + before Linux 3.6. */ + struct stat st; + if (fstatat (fd, "", &st, AT_EMPTY_PATH) != 0) + { + int stat_errno = errno; + close (fd); + errno = stat_errno; + return -1; + } + if (S_ISLNK (st.st_mode)) + { + close (fd); + errno = EOPNOTSUPP; + return -1; + } + +# if defined __linux__ || defined __ANDROID__ + static char const fmt[] = "/proc/self/fd/%d"; + char buf[sizeof fmt - sizeof "%d" + INT_BUFSIZE_BOUND (int)]; + sprintf (buf, fmt, fd); + int chmod_result = chmod (buf, mode); + int chmod_errno = errno; + close (fd); + if (chmod_result == 0) + return chmod_result; + if (chmod_errno != ENOENT) + { + errno = chmod_errno; + return chmod_result; + } +# endif + /* /proc is not mounted or would not work as in GNU/Linux. */ + +#elif HAVE_LSTAT + struct stat st; + int lstat_result = lstat (file, &st); + if (lstat_result != 0) + return lstat_result; + if (S_ISLNK (st.st_mode)) + { + errno = EOPNOTSUPP; + return -1; + } +#endif + + /* Fall back on chmod, despite a possible race. */ + return chmod (file, mode); +} diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index 0069c1744dc..8f00317c2b0 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el @@ -409,7 +409,8 @@ has no effect on MS-Windows." (set-file-modes file (if num-modes num-modes - (file-modes-symbolic-to-number modes (file-modes file))))) + (file-modes-symbolic-to-number modes (file-modes file 'nofollow))) + 'nofollow)) (dired-do-redisplay arg))) ;;;###autoload diff --git a/lisp/doc-view.el b/lisp/doc-view.el index 3788d797258..8b3d5527f08 100644 --- a/lisp/doc-view.el +++ b/lisp/doc-view.el @@ -683,8 +683,6 @@ at the top edge of the page moves to the previous page." ;; time-window of loose permissions otherwise. (with-file-modes #o0700 (make-directory dir)) (file-already-exists - (when (file-symlink-p dir) - (error "Danger: %s points to a symbolic link" dir)) ;; In case it was created earlier with looser rights. ;; We could check the mode info returned by file-attributes, but it's ;; a pain to parse and it may not tell you what we want under @@ -694,7 +692,7 @@ at the top edge of the page moves to the previous page." ;; sure we have write-access to the directory and that we own it, thus ;; closing a bunch of security holes. (condition-case error - (set-file-modes dir #o0700) + (set-file-modes dir #o0700 'nofollow) (file-error (error (format "Unable to use temporary directory %s: %s" diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el index 785e350e0e5..e9f76583272 100644 --- a/lisp/emacs-lisp/autoload.el +++ b/lisp/emacs-lisp/autoload.el @@ -895,7 +895,7 @@ FILE's modification time." (cons (lambda () (ignore-errors (delete-file tempfile))) kill-emacs-hook))) (unless (= temp-modes desired-modes) - (set-file-modes tempfile desired-modes)) + (set-file-modes tempfile desired-modes 'nofollow)) (write-region (point-min) (point-max) tempfile nil 1) (backup-buffer) (rename-file tempfile buffer-file-name t)) diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index fce5e4aed6d..24a36393b2e 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -2008,7 +2008,7 @@ The value is non-nil if there were no errors, nil if errors." (delete-file tempfile))) kill-emacs-hook))) (unless (= temp-modes desired-modes) - (set-file-modes tempfile desired-modes)) + (set-file-modes tempfile desired-modes 'nofollow)) (write-region (point-min) (point-max) tempfile nil 1) ;; This has the intentional side effect that any ;; hard-links to target-file continue to diff --git a/lisp/eshell/em-pred.el b/lisp/eshell/em-pred.el index 04bf3ff8998..7219af45f54 100644 --- a/lisp/eshell/em-pred.el +++ b/lisp/eshell/em-pred.el @@ -478,7 +478,7 @@ that `ls -l' will show in the first column of its display." (defsubst eshell-pred-file-mode (mode) "Return a test which tests that MODE pertains to the file." `(lambda (file) - (let ((modes (file-modes file))) + (let ((modes (file-modes file 'nofollow))) (if modes (logand ,mode modes))))) diff --git a/lisp/files.el b/lisp/files.el index 683f4a8ce7c..2e7694d7677 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -4672,6 +4672,7 @@ BACKUPNAME is the backup file name, which is the old file renamed." ;; Create temp files with strict access rights. It's easy to ;; loosen them later, whereas it's impossible to close the ;; time-window of loose permissions otherwise. + (let (nofollow-flag) (with-file-modes ?\700 (when (condition-case nil ;; Try to overwrite old backup first. @@ -4682,6 +4683,7 @@ BACKUPNAME is the backup file name, which is the old file renamed." (when (file-exists-p to-name) (delete-file to-name)) (copy-file from-name to-name nil t t) + (setq nofollow-flag 'nofollow) nil) (file-already-exists t)) ;; The file was somehow created by someone else between @@ -4694,7 +4696,7 @@ BACKUPNAME is the backup file name, which is the old file renamed." (with-demoted-errors (set-file-extended-attributes to-name extended-attributes))) (and modes - (set-file-modes to-name (logand modes #o1777))))) + (set-file-modes to-name (logand modes #o1777) nofollow-flag))))) (defvar file-name-version-regexp "\\(?:~\\|\\.~[-[:alnum:]:#@^._]+\\(?:~[[:digit:]]+\\)?~\\)" @@ -5900,7 +5902,8 @@ into NEWNAME instead." ;; If default-directory is a remote directory, make sure we find its ;; copy-directory handler. (let ((handler (or (find-file-name-handler directory 'copy-directory) - (find-file-name-handler newname 'copy-directory)))) + (find-file-name-handler newname 'copy-directory))) + (follow parents)) (if handler (funcall handler 'copy-directory directory newname keep-time parents copy-contents) @@ -5920,7 +5923,8 @@ into NEWNAME instead." (or parents (not (file-directory-p newname))) (setq newname (concat newname (file-name-nondirectory directory)))) - (make-directory (directory-file-name newname) parents))) + (make-directory (directory-file-name newname) parents)) + (t (setq follow t))) ;; Copy recursively. (dolist (file @@ -5941,7 +5945,7 @@ into NEWNAME instead." (let ((modes (file-modes directory)) (times (and keep-time (file-attribute-modification-time (file-attributes directory))))) - (if modes (set-file-modes newname modes)) + (if modes (set-file-modes newname modes (unless follow 'nofollow))) (if times (set-file-times newname times)))))) diff --git a/lisp/gnus/gnus-util.el b/lisp/gnus/gnus-util.el index eb0fd2522d3..83a85161aa0 100644 --- a/lisp/gnus/gnus-util.el +++ b/lisp/gnus/gnus-util.el @@ -1601,10 +1601,10 @@ empty directories from OLD-PATH." (file-truename (concat old-dir ".."))))))))) -(defun gnus-set-file-modes (filename mode) +(defun gnus-set-file-modes (filename mode &optional flag) "Wrapper for set-file-modes." (ignore-errors - (set-file-modes filename mode))) + (set-file-modes filename mode flag))) (defun gnus-rescale-image (image size) "Rescale IMAGE to SIZE if possible. diff --git a/lisp/gnus/mail-source.el b/lisp/gnus/mail-source.el index f5b68789b85..acf35a376a9 100644 --- a/lisp/gnus/mail-source.el +++ b/lisp/gnus/mail-source.el @@ -695,7 +695,7 @@ Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile) mail-source-movemail-program nil errors nil from to))))) (when (file-exists-p to) - (set-file-modes to mail-source-default-file-modes)) + (set-file-modes to mail-source-default-file-modes 'nofollow)) (if (and (or (not (buffer-modified-p errors)) (zerop (buffer-size errors))) (and (numberp result) diff --git a/lisp/gnus/mm-decode.el b/lisp/gnus/mm-decode.el index 2dab278b373..96695aabfde 100644 --- a/lisp/gnus/mm-decode.el +++ b/lisp/gnus/mm-decode.el @@ -948,7 +948,7 @@ external if displayed external." ;; The file is deleted after the viewer exists. If the users edits ;; the file, changes will be lost. Set file to read-only to make it ;; clear. - (set-file-modes file #o400) + (set-file-modes file #o400 'nofollow) (message "Viewing with %s" method) (cond (needsterm diff --git a/lisp/gnus/nnmail.el b/lisp/gnus/nnmail.el index 6e01b5c4d0b..93e4b0e7a8f 100644 --- a/lisp/gnus/nnmail.el +++ b/lisp/gnus/nnmail.el @@ -1958,7 +1958,7 @@ If TIME is nil, then return the cutoff time for oldness instead." (let ((coding-system-for-write nnmail-file-coding-system) (file-name-coding-system nnmail-pathname-coding-system)) (write-region start end filename append visit lockname) - (set-file-modes filename nnmail-default-file-modes))) + (set-file-modes filename nnmail-default-file-modes 'nofollow))) ;;; ;;; Status functions diff --git a/lisp/net/ange-ftp.el b/lisp/net/ange-ftp.el index f28394260dd..e2d4d7dd057 100644 --- a/lisp/net/ange-ftp.el +++ b/lisp/net/ange-ftp.el @@ -4740,7 +4740,8 @@ NEWNAME should be the name to give the new compressed or uncompressed file.") (setq ange-ftp-ls-cache-file nil) ;Stop confusing Dired. 0) -(defun ange-ftp-set-file-modes (filename mode) +(defun ange-ftp-set-file-modes (filename mode &optional flag) + flag ;; FIXME: Support 'nofollow'. (ange-ftp-call-chmod (list (format "%o" mode) filename))) (defun ange-ftp-make-symbolic-link (&rest _arguments) diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index aa7fe147c20..96ef95dbe30 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el @@ -591,7 +591,8 @@ Emacs dired can't find files." (ignore-errors (delete-file tmpfile)) (tramp-error v 'file-error "Cannot make local copy of file `%s'" filename)) - (set-file-modes tmpfile (logior (or (file-modes filename) 0) #o0400))) + (set-file-modes tmpfile (logior (or (file-modes filename) 0) #o0400) + 'nofollow)) tmpfile))) (defun tramp-adb-handle-file-writable-p (filename) @@ -636,7 +637,8 @@ But handle the case, if the \"test\" command is not available." (tmpfile (tramp-compat-make-temp-file filename))) (when (and append (file-exists-p filename)) (copy-file filename tmpfile 'ok) - (set-file-modes tmpfile (logior (or (file-modes tmpfile) 0) #o0600))) + (set-file-modes tmpfile (logior (or (file-modes tmpfile) 0) #o0600) + 'nofollow)) (tramp-run-real-handler #'write-region (list start end tmpfile append 'no-message lockname)) (with-tramp-progress-reporter @@ -665,8 +667,9 @@ But handle the case, if the \"test\" command is not available." (tramp-message v 0 "Wrote %s" filename)) (run-hooks 'tramp-handle-write-region-hook)))) -(defun tramp-adb-handle-set-file-modes (filename mode) +(defun tramp-adb-handle-set-file-modes (filename mode &optional flag) "Like `set-file-modes' for Tramp files." + flag ;; FIXME: Support 'nofollow'. (with-parsed-tramp-file-name filename nil (tramp-flush-file-properties v localname) (tramp-adb-send-command-and-check v (format "chmod %o %s" mode localname)))) diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index 762c4fe4b3b..79835804bc0 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el @@ -1562,12 +1562,12 @@ If FILE-SYSTEM is non-nil, return file system attributes." (tramp-run-real-handler #'rename-file (list filename newname ok-if-already-exists)))) -(defun tramp-gvfs-handle-set-file-modes (filename mode) +(defun tramp-gvfs-handle-set-file-modes (filename mode &optional flag) "Like `set-file-modes' for Tramp files." (with-parsed-tramp-file-name filename nil (tramp-flush-file-properties v localname) (tramp-gvfs-send-command - v "gvfs-set-attribute" "-t" "uint32" + v "gvfs-set-attribute" (if flag "-nt" "-t") "uint32" (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v)) "unix::mode" (number-to-string mode)))) diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 5a3abc31ea6..f31d3615884 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -1478,10 +1478,11 @@ of." ;; only if that agrees with the buffer's record. (t (tramp-compat-time-equal-p mt tramp-time-doesnt-exist))))))))) -(defun tramp-sh-handle-set-file-modes (filename mode) +(defun tramp-sh-handle-set-file-modes (filename mode &optional flag) "Like `set-file-modes' for Tramp files." (with-parsed-tramp-file-name filename nil (tramp-flush-file-properties v localname) + flag ;; FIXME: Support 'nofollow'. ;; FIXME: extract the proper text from chmod's stderr. (tramp-barf-unless-okay v @@ -2279,7 +2280,7 @@ the uid and gid from FILENAME." ;; We must change the ownership as local user. ;; Since this does not work reliable, we also ;; give read permissions. - (set-file-modes tmpfile #o0777) + (set-file-modes tmpfile #o0777 'nofollow) (tramp-set-file-uid-gid tmpfile (tramp-get-remote-uid v 'integer) @@ -3221,7 +3222,8 @@ STDERR can also be a file name." (delete-file tmpfile2))))) ;; Set proper permissions. - (set-file-modes tmpfile (tramp-default-file-modes filename)) + (set-file-modes tmpfile (tramp-default-file-modes filename) + 'nofollow) ;; Set local user ownership. (tramp-set-file-uid-gid tmpfile)) @@ -3320,7 +3322,7 @@ STDERR can also be a file name." ;; handles permissions. ;; Ensure that it is still readable. (when modes - (set-file-modes tmpfile (logior (or modes 0) #o0400))) + (set-file-modes tmpfile (logior (or modes 0) #o0400) 'nofollow)) ;; This is a bit lengthy due to the different methods ;; possible for file transfer. First, we check whether the diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index f02be394a7b..95505ea101f 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el @@ -1464,8 +1464,9 @@ component is used as the target of the symlink." (tramp-flush-connection-property v "process-name") (tramp-flush-connection-property v "process-buffer"))))))) -(defun tramp-smb-handle-set-file-modes (filename mode) +(defun tramp-smb-handle-set-file-modes (filename mode &optional flag) "Like `set-file-modes' for Tramp files." + flag ;; FIXME: Support 'nofollow'. (with-parsed-tramp-file-name filename nil (when (tramp-smb-get-cifs-capabilities v) (tramp-flush-file-properties v localname) diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el index f258ad6b931..4654d633fab 100644 --- a/lisp/net/tramp-sudoedit.el +++ b/lisp/net/tramp-sudoedit.el @@ -463,8 +463,9 @@ the result will be a local, non-Tramp, file name." (tramp-sudoedit-send-command v "test" "-r" (tramp-compat-file-name-unquote localname))))) -(defun tramp-sudoedit-handle-set-file-modes (filename mode) +(defun tramp-sudoedit-handle-set-file-modes (filename mode &optional flag) "Like `set-file-modes' for Tramp files." + flag ;; FIXME: Support 'nofollow'. (with-parsed-tramp-file-name filename nil (tramp-flush-file-properties v localname) (unless (tramp-sudoedit-send-command @@ -735,7 +736,8 @@ ID-FORMAT valid values are `string' and `integer'." (file-attributes filename 'integer)) gid)) (tramp-set-file-uid-gid filename uid gid)) - (set-file-modes filename modes))))) + (set-file-modes filename modes + (when (eq mustbenew 'excl) 'nofollow)))))) ;; Internal functions. diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el index 409e1f7499a..64acaa95d47 100644 --- a/lisp/net/tramp.el +++ b/lisp/net/tramp.el @@ -3179,10 +3179,13 @@ User is always nil." (copy-file filename tmpfile 'ok-if-already-exists 'keep-time) tmpfile))) -(defun tramp-handle-file-modes (filename) +(defun tramp-handle-file-modes (filename &optional flag) "Like `file-modes' for Tramp files." - (when-let ((attrs (file-attributes (or (file-truename filename) filename)))) - (tramp-mode-string-to-int (tramp-compat-file-attribute-modes attrs)))) + (when-let ((attrs (file-attributes filename))) + (let ((mode-string (tramp-compat-file-attribute-modes attrs))) + (if (and (not flag) (eq ?l (aref mode-string 0))) + (tramp-handle-file-modes (file-chase-links filename) 'nofollow) + (tramp-mode-string-to-int mode-string))))) ;; Localname manipulation functions that grok Tramp localnames... (defun tramp-handle-file-name-as-directory (file) @@ -3884,7 +3887,7 @@ of." ;; renamed to the backup file. This case `save-buffer' ;; handles permissions. ;; Ensure that it is still readable. - (set-file-modes tmpfile (logior (or modes 0) #o0400)) + (set-file-modes tmpfile (logior (or modes 0) #o0400) 'nofollow) ;; We say `no-message' here because we don't want the visited file ;; modtime data to be clobbered from the temp file. We call ;; `set-visited-file-modtime' ourselves later on. @@ -4664,7 +4667,7 @@ Return the local name of the temporary file." (setq result nil) ;; This creates the file by side effect. (set-file-times result) - (set-file-modes result #o0700))) + (set-file-modes result #o0700 'nofollow))) ;; Return the local part. (tramp-file-local-name result))) diff --git a/lisp/server.el b/lisp/server.el index e6d8b1783c9..18612181477 100644 --- a/lisp/server.el +++ b/lisp/server.el @@ -563,7 +563,7 @@ See variable `server-auth-dir' for details." (format "it is not owned by you (owner = %s (%d))" (user-full-name uid) uid)) (w32 nil) ; on NTFS? - ((let ((modes (file-modes dir))) + ((let ((modes (file-modes dir 'nofollow))) (unless (zerop (logand (or modes 0) #o077)) (format "it is accessible by others (%03o)" modes)))) (t nil)))) diff --git a/lisp/url/url-util.el b/lisp/url/url-util.el index 645011a5783..6dd7a9c2aac 100644 --- a/lisp/url/url-util.el +++ b/lisp/url/url-util.el @@ -615,9 +615,7 @@ Creates FILE and its parent directories if they do not exist." (with-temp-buffer (write-region (point-min) (point-max) file nil 'silent nil 'excl))) (file-already-exists - (if (file-symlink-p file) - (error "Danger: `%s' is a symbolic link" file)) - (set-file-modes file #o0600)))) + (set-file-modes file #o0600 'nofollow)))) (autoload 'puny-encode-domain "puny") (autoload 'url-domsuf-cookie-allowed-p "url-domsuf") diff --git a/m4/fchmodat.m4 b/m4/fchmodat.m4 new file mode 100644 index 00000000000..e3f2f048162 --- /dev/null +++ b/m4/fchmodat.m4 @@ -0,0 +1,82 @@ +# fchmodat.m4 serial 4 +dnl Copyright (C) 2004-2020 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. + +# Written by Jim Meyering. + +AC_DEFUN([gl_FUNC_FCHMODAT], +[ + AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS]) + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_CHECK_FUNCS_ONCE([fchmodat lchmod]) + if test $ac_cv_func_fchmodat != yes; then + HAVE_FCHMODAT=0 + else + AC_CACHE_CHECK( + [whether fchmodat+AT_SYMLINK_NOFOLLOW works on non-symlinks], + [gl_cv_func_fchmodat_works], + [dnl This test fails on GNU/Linux with glibc 2.31 (but not on + dnl GNU/kFreeBSD nor GNU/Hurd) and Cygwin 2.9. + AC_RUN_IFELSE( + [AC_LANG_PROGRAM( + [ + AC_INCLUDES_DEFAULT[ + #include + #ifndef S_IRUSR + #define S_IRUSR 0400 + #endif + #ifndef S_IWUSR + #define S_IWUSR 0200 + #endif + #ifndef S_IRWXU + #define S_IRWXU 0700 + #endif + #ifndef S_IRWXG + #define S_IRWXG 0070 + #endif + #ifndef S_IRWXO + #define S_IRWXO 0007 + #endif + ]], + [[ + int permissive = S_IRWXU | S_IRWXG | S_IRWXO; + int desired = S_IRUSR | S_IWUSR; + static char const f[] = "conftest.fchmodat"; + struct stat st; + if (creat (f, permissive) < 0) + return 1; + if (fchmodat (AT_FDCWD, f, desired, AT_SYMLINK_NOFOLLOW) != 0) + return 1; + if (stat (f, &st) != 0) + return 1; + return ! ((st.st_mode & permissive) == desired); + ]])], + [gl_cv_func_fchmodat_works=yes], + [gl_cv_func_fchmodat_works=no], + [case "$host_os" in + dnl Guess no on Linux with glibc and Cygwin, yes otherwise. + linux-gnu* | cygwin*) gl_cv_func_fchmodat_works="guessing no" ;; + *) gl_cv_func_fchmodat_works="$gl_cross_guess_normal" ;; + esac + ]) + rm -f conftest.fchmodat]) + case $gl_cv_func_fchmodat_works in + *yes) ;; + *) + AC_DEFINE([NEED_FCHMODAT_NONSYMLINK_FIX], [1], + [Define to 1 if fchmodat+AT_SYMLINK_NOFOLLOW does not work right on non-symlinks.]) + REPLACE_FCHMODAT=1 + ;; + esac + fi +]) + +# Prerequisites of lib/fchmodat.c. +AC_DEFUN([gl_PREREQ_FCHMODAT], +[ + AC_CHECK_FUNCS_ONCE([lchmod]) + : +]) diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index fea32b544f9..1465ce811b8 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -82,6 +82,7 @@ AC_DEFUN([gl_EARLY], # Code from module extensions: # Code from module extern-inline: # Code from module faccessat: + # Code from module fchmodat: # Code from module fcntl: # Code from module fcntl-h: # Code from module fdopendir: @@ -111,6 +112,7 @@ AC_DEFUN([gl_EARLY], # Code from module inttypes-incomplete: # Code from module largefile: AC_REQUIRE([AC_SYS_LARGEFILE]) + # Code from module lchmod: # Code from module libc-config: # Code from module limits-h: # Code from module localtime-buffer: @@ -255,6 +257,12 @@ AC_DEFUN([gl_INIT], fi gl_MODULE_INDICATOR([faccessat]) gl_UNISTD_MODULE_INDICATOR([faccessat]) + gl_FUNC_FCHMODAT + if test $HAVE_FCHMODAT = 0 || test $REPLACE_FCHMODAT = 1; then + AC_LIBOBJ([fchmodat]) + gl_PREREQ_FCHMODAT + fi + gl_SYS_STAT_MODULE_INDICATOR([fchmodat]) gl_FUNC_FCNTL if test $HAVE_FCNTL = 0 || test $REPLACE_FCNTL = 1; then AC_LIBOBJ([fcntl]) @@ -468,6 +476,7 @@ AC_DEFUN([gl_INIT], gl_gnulib_enabled_getgroups=false gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36=false gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1=false + gl_gnulib_enabled_lchmod=false gl_gnulib_enabled_21ee726a3540c09237a8e70c0baf7467=false gl_gnulib_enabled_2049e887c7e5308faad27b3f894bb8c9=false gl_gnulib_enabled_malloca=false @@ -569,6 +578,18 @@ AC_DEFUN([gl_INIT], fi fi } + func_gl_gnulib_m4code_lchmod () + { + if ! $gl_gnulib_enabled_lchmod; then + gl_FUNC_LCHMOD + if test $HAVE_LCHMOD = 0; then + AC_LIBOBJ([lchmod]) + gl_PREREQ_LCHMOD + fi + gl_SYS_STAT_MODULE_INDICATOR([lchmod]) + gl_gnulib_enabled_lchmod=true + fi + } func_gl_gnulib_m4code_21ee726a3540c09237a8e70c0baf7467 () { if ! $gl_gnulib_enabled_21ee726a3540c09237a8e70c0baf7467; then @@ -660,6 +681,15 @@ AC_DEFUN([gl_INIT], if test $HAVE_FACCESSAT = 0 || test $REPLACE_FACCESSAT = 1; then func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 fi + if test $HAVE_FCHMODAT = 0; then + func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b + fi + if test $HAVE_FCHMODAT = 0; then + func_gl_gnulib_m4code_lchmod + fi + if test $HAVE_FCHMODAT = 0; then + func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 + fi if test $HAVE_FCNTL = 0 || test $REPLACE_FCNTL = 1; then func_gl_gnulib_m4code_getdtablesize fi @@ -708,6 +738,7 @@ AC_DEFUN([gl_INIT], AM_CONDITIONAL([gl_GNULIB_ENABLED_getgroups], [$gl_gnulib_enabled_getgroups]) AM_CONDITIONAL([gl_GNULIB_ENABLED_be453cec5eecf5731a274f2de7f2db36], [$gl_gnulib_enabled_be453cec5eecf5731a274f2de7f2db36]) AM_CONDITIONAL([gl_GNULIB_ENABLED_a9786850e999ae65a836a6041e8e5ed1], [$gl_gnulib_enabled_a9786850e999ae65a836a6041e8e5ed1]) + AM_CONDITIONAL([gl_GNULIB_ENABLED_lchmod], [$gl_gnulib_enabled_lchmod]) AM_CONDITIONAL([gl_GNULIB_ENABLED_21ee726a3540c09237a8e70c0baf7467], [$gl_gnulib_enabled_21ee726a3540c09237a8e70c0baf7467]) AM_CONDITIONAL([gl_GNULIB_ENABLED_2049e887c7e5308faad27b3f894bb8c9], [$gl_gnulib_enabled_2049e887c7e5308faad27b3f894bb8c9]) AM_CONDITIONAL([gl_GNULIB_ENABLED_malloca], [$gl_gnulib_enabled_malloca]) @@ -908,6 +939,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/execinfo.in.h lib/explicit_bzero.c lib/faccessat.c + lib/fchmodat.c lib/fcntl.c lib/fcntl.in.h lib/fdopendir.c @@ -946,6 +978,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/ignore-value.h lib/intprops.h lib/inttypes.in.h + lib/lchmod.c lib/libc-config.h lib/limits.in.h lib/localtime-buffer.c @@ -1058,6 +1091,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/extensions.m4 m4/extern-inline.m4 m4/faccessat.m4 + m4/fchmodat.m4 m4/fcntl-o.m4 m4/fcntl.m4 m4/fcntl_h.m4 @@ -1083,6 +1117,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/include_next.m4 m4/inttypes.m4 m4/largefile.m4 + m4/lchmod.m4 m4/limits-h.m4 m4/localtime-buffer.m4 m4/lstat.m4 diff --git a/m4/lchmod.m4 b/m4/lchmod.m4 new file mode 100644 index 00000000000..b9e8a97cb31 --- /dev/null +++ b/m4/lchmod.m4 @@ -0,0 +1,31 @@ +#serial 7 + +dnl Copyright (C) 2005-2006, 2008-2020 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl From Paul Eggert. +dnl Provide a replacement for lchmod on hosts that lack a working version. + +AC_DEFUN([gl_FUNC_LCHMOD], +[ + AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS]) + + dnl Persuade glibc to declare lchmod(). + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + + AC_CHECK_FUNCS_ONCE([lchmod lstat]) + if test "$ac_cv_func_lchmod" = no; then + HAVE_LCHMOD=0 + fi +]) + +# Prerequisites of lib/lchmod.c. +AC_DEFUN([gl_PREREQ_LCHMOD], +[ + AC_REQUIRE([AC_C_INLINE]) + : +]) diff --git a/src/fileio.c b/src/fileio.c index 6b56c473abf..2532f5233c4 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -3332,50 +3332,60 @@ support. */) return Qnil; } -DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 1, 0, +static int +symlink_nofollow_flag (Lisp_Object flag) +{ + /* For now, treat all non-nil FLAGs like 'nofollow'. */ + return !NILP (flag) ? AT_SYMLINK_NOFOLLOW : 0; +} + +DEFUN ("file-modes", Ffile_modes, Sfile_modes, 1, 2, 0, doc: /* Return mode bits of file named FILENAME, as an integer. -Return nil if FILENAME does not exist. */) - (Lisp_Object filename) +Return nil if FILENAME does not exist. If optional FLAG is `nofollow', +do not follow FILENAME if it is a symbolic link. */) + (Lisp_Object filename, Lisp_Object flag) { struct stat st; + int nofollow = symlink_nofollow_flag (flag); Lisp_Object absname = expand_and_dir_to_file (filename); /* If the file name has special constructs in it, call the corresponding file name handler. */ Lisp_Object handler = Ffind_file_name_handler (absname, Qfile_modes); if (!NILP (handler)) - return call2 (handler, Qfile_modes, absname); + return call3 (handler, Qfile_modes, absname, flag); - if (emacs_fstatat (AT_FDCWD, SSDATA (ENCODE_FILE (absname)), &st, 0) != 0) + char *fname = SSDATA (ENCODE_FILE (absname)); + if (emacs_fstatat (AT_FDCWD, fname, &st, nofollow) != 0) return file_attribute_errno (absname, errno); return make_fixnum (st.st_mode & 07777); } -DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 2, +DEFUN ("set-file-modes", Fset_file_modes, Sset_file_modes, 2, 3, "(let ((file (read-file-name \"File: \"))) \ (list file (read-file-modes nil file)))", doc: /* Set mode bits of file named FILENAME to MODE (an integer). -Only the 12 low bits of MODE are used. +Only the 12 low bits of MODE are used. If optional FLAG is `nofollow', +do not follow FILENAME if it is a symbolic link. Interactively, mode bits are read by `read-file-modes', which accepts symbolic notation, like the `chmod' command from GNU Coreutils. */) - (Lisp_Object filename, Lisp_Object mode) + (Lisp_Object filename, Lisp_Object mode, Lisp_Object flag) { - Lisp_Object absname, encoded_absname; - Lisp_Object handler; - - absname = Fexpand_file_name (filename, BVAR (current_buffer, directory)); CHECK_FIXNUM (mode); + int nofollow = symlink_nofollow_flag (flag); + Lisp_Object absname = Fexpand_file_name (filename, + BVAR (current_buffer, directory)); /* If the file name has special constructs in it, call the corresponding file name handler. */ - handler = Ffind_file_name_handler (absname, Qset_file_modes); + Lisp_Object handler = Ffind_file_name_handler (absname, Qset_file_modes); if (!NILP (handler)) - return call3 (handler, Qset_file_modes, absname, mode); - - encoded_absname = ENCODE_FILE (absname); + return call4 (handler, Qset_file_modes, absname, mode, flag); - if (chmod (SSDATA (encoded_absname), XFIXNUM (mode) & 07777) < 0) + char *fname = SSDATA (ENCODE_FILE (absname)); + mode_t imode = XFIXNUM (mode) & 07777; + if (fchmodat (AT_FDCWD, fname, imode, nofollow) != 0) report_file_error ("Doing chmod", absname); return Qnil; @@ -5740,7 +5750,7 @@ auto_save_1 (void) == 0) /* But make sure we can overwrite it later! */ auto_save_mode_bits = (st.st_mode | 0600) & 0777; - else if (modes = Ffile_modes (BVAR (current_buffer, filename)), + else if (modes = Ffile_modes (BVAR (current_buffer, filename), Qnil), FIXNUMP (modes)) /* Remote files don't cooperate with fstatat. */ auto_save_mode_bits = (XFIXNUM (modes) | 0600) & 0777; -- cgit v1.2.3 From 03c07c88d90b5747456b9d286bace2dd4a713aac Mon Sep 17 00:00:00 2001 From: Mattias Engdegård Date: Fri, 21 Feb 2020 12:16:20 +0100 Subject: Generate 'substring' byte op (bug#39709) The 'substring' byte op was not emitted, apparently by mistake. Fix. Suggested by Mark Oteiza . * lisp/emacs-lisp/bytecomp.el (byte-defop-compiler): Add '1-3' clause. (byte-compile-one-to-three-args): New. * lisp/emacs-lisp/byte-opt.el (byte-compile-side-effect-free-ops): Add 'byte-substring'. * test/lisp/emacs-lisp/bytecomp-tests.el (byte-opt-testsuite-arith-data): Test 'substring'. --- lisp/emacs-lisp/byte-opt.el | 2 +- lisp/emacs-lisp/bytecomp.el | 10 +++++++++- test/lisp/emacs-lisp/bytecomp-tests.el | 7 ++++++- 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'lisp/emacs-lisp') diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index fe0930c684b..4f72251aed5 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -1515,7 +1515,7 @@ byte-eqlsign byte-gtr byte-lss byte-leq byte-geq byte-diff byte-negate byte-plus byte-max byte-min byte-mult byte-char-after byte-char-syntax byte-buffer-substring byte-string= byte-string< byte-nthcdr byte-elt - byte-member byte-assq byte-quo byte-rem) + byte-member byte-assq byte-quo byte-rem byte-substring) byte-compile-side-effect-and-error-free-ops)) ;; This crock is because of the way DEFVAR_BOOL variables work. diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el index 24a36393b2e..63348456a15 100644 --- a/lisp/emacs-lisp/bytecomp.el +++ b/lisp/emacs-lisp/bytecomp.el @@ -3487,7 +3487,7 @@ the opcode to be used. If function is a list, the first element is the function and the second element is the bytecode-symbol. The second element may be nil, meaning there is no opcode. COMPILE-HANDLER is the function to use to compile this byte-op, or -may be the abbreviations 0, 1, 2, 3, 0-1, or 1-2. +may be the abbreviations 0, 1, 2, 2-and, 3, 0-1, 1-2, 1-3, or 2-3. If it is nil, then the handler is \"byte-compile-SYMBOL.\"" (let (opcode) (if (symbolp function) @@ -3506,6 +3506,7 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\"" (0-1 . byte-compile-zero-or-one-arg) (1-2 . byte-compile-one-or-two-args) (2-3 . byte-compile-two-or-three-args) + (1-3 . byte-compile-one-to-three-args) ))) compile-handler (intern (concat "byte-compile-" @@ -3690,6 +3691,13 @@ These implicitly `and' together a bunch of two-arg bytecodes." ((= len 4) (byte-compile-three-args form)) (t (byte-compile-subr-wrong-args form "2-3"))))) +(defun byte-compile-one-to-three-args (form) + (let ((len (length form))) + (cond ((= len 2) (byte-compile-three-args (append form '(nil nil)))) + ((= len 3) (byte-compile-three-args (append form '(nil)))) + ((= len 4) (byte-compile-three-args form)) + (t (byte-compile-subr-wrong-args form "1-3"))))) + (defun byte-compile-noop (_form) (byte-compile-constant nil)) diff --git a/test/lisp/emacs-lisp/bytecomp-tests.el b/test/lisp/emacs-lisp/bytecomp-tests.el index de11ae22d50..d4ceb47c36e 100644 --- a/test/lisp/emacs-lisp/bytecomp-tests.el +++ b/test/lisp/emacs-lisp/bytecomp-tests.el @@ -347,7 +347,12 @@ ((eq x 't) 99) (t 999)))) '((a c) (b c) (7 c) (-3 c) (nil nil) (t c) (q c) (r c) (s c) - (t c) (x "a") (x "c") (x c) (x d) (x e)))) + (t c) (x "a") (x "c") (x c) (x d) (x e))) + + ;; `substring' bytecode generation (bug#39709). + (substring "abcdef") + (substring "abcdef" 2) + (substring "abcdef" 3 2)) "List of expression for test. Each element will be executed by interpreter and with bytecompiled code, and their results compared.") -- cgit v1.2.3 From c0fcbd2c119b8418855f0931aceefbef717c5e53 Mon Sep 17 00:00:00 2001 From: Mark Oteiza Date: Tue, 25 Feb 2020 17:53:04 -0500 Subject: Expose ElDoc functions in a hook (Bug#28257) * lisp/emacs-lisp/eldoc.el: Update commentary. (eldoc--eval-expression-setup): Use new hook. (eldoc--supported-p): Accomodate new hook. (eldoc-documentation-functions): New hook. (eldoc-documentation-default, eldoc-documentation-compose): New functions. (eldoc-documentation-function): Use 'eldoc-documentation-default' as new default value. Update documentation and custom attributes. (eldoc-print-current-symbol-info): Accomodate possible null value for 'eldoc-documentation-function'. * etc/NEWS: Mention them. * doc/emacs/programs.texi (Emacs Lisp Documentation Lookup): Mention new hook and changes to 'eldoc-documentation-function'. * lisp/hexl.el (hexl-mode, hexl-revert-buffer-function): * lisp/ielm.el (inferior-emacs-lisp-mode): * lisp/progmodes/cfengine.el (cfengine3-mode): * lisp/progmodes/elisp-mode.el (emacs-lisp-mode): * lisp/progmodes/octave.el (octave-mode): * lisp/progmodes/python.el (python-mode): Use new hook. --- doc/emacs/programs.texi | 24 ++++++++++++-- etc/NEWS | 12 +++++++ lisp/emacs-lisp/eldoc.el | 77 ++++++++++++++++++++++++++++++++++---------- lisp/hexl.el | 6 ++-- lisp/ielm.el | 4 +-- lisp/progmodes/cfengine.el | 15 +++++---- lisp/progmodes/elisp-mode.el | 4 +-- lisp/progmodes/octave.el | 3 +- lisp/progmodes/python.el | 6 ++-- 9 files changed, 115 insertions(+), 36 deletions(-) (limited to 'lisp/emacs-lisp') diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi index 683374c6153..865a3a67d56 100644 --- a/doc/emacs/programs.texi +++ b/doc/emacs/programs.texi @@ -1269,9 +1269,27 @@ information whenever there is a Lisp function or variable at point; for a function, it shows the argument list, and for a variable it shows the first line of the variable's documentation string. To toggle Eldoc mode, type @kbd{M-x eldoc-mode}. There's also a Global -Eldoc mode, which is turned on by default, and affects buffers, such -as @samp{*scratch*}, whose major mode is Emacs Lisp or Lisp -Interaction (@w{@kbd{M-x global-eldoc-mode}} to turn it off globally). +Eldoc mode, which is turned on by default, and affects buffers whose +major mode sets the variables described below. Use @w{@kbd{M-x +global-eldoc-mode}} to turn it off globally. + +@vindex eldoc-documentation-function +@vindex eldoc-documentation-functions + These variables can be used to configure ElDoc mode: + +@table @code +@item eldoc-documentation-function +This variable holds the function which is used to retrieve +documentation for the item at point from the functions in the hook +@code{eldoc-documentation-functions}. By default, +@code{eldoc-documentation-function} returns the first documentation +string produced by the @code{eldoc-documentation-functions} hook. + +@item eldoc-documentation-functions +This abnormal hook holds documentation functions. It acts as a +collection of backends for ElDoc. This is what modes should use to +register their documentation functions with ElDoc. +@end table @node Hideshow @section Hideshow minor mode diff --git a/etc/NEWS b/etc/NEWS index 54aab1a5b60..ee3a3c19e7c 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -125,6 +125,18 @@ To revert to the previous behaviour, unconditionally aborts the current edebug instrumentation with the supplied error message. ++++ +** ElDoc + +*** New hook 'eldoc-documentation-functions' to be used for registering +doc string functions. This makes the results of all doc string +functions accessible to the user through the existing single function hook +'eldoc-documentation-function'. + +*** 'eldoc-documentation-function' is now a custom variable. +Modes should use the new hook instead of this variable to register +their backends. + ** Tramp +++ diff --git a/lisp/emacs-lisp/eldoc.el b/lisp/emacs-lisp/eldoc.el index 7a7b8ec1647..456a650828c 100644 --- a/lisp/emacs-lisp/eldoc.el +++ b/lisp/emacs-lisp/eldoc.el @@ -40,9 +40,9 @@ ;; (add-hook 'ielm-mode-hook 'eldoc-mode) ;; (add-hook 'eval-expression-minibuffer-setup-hook 'eldoc-mode) -;; Major modes for other languages may use ElDoc by defining an -;; appropriate function as the buffer-local value of -;; `eldoc-documentation-function'. +;; Major modes for other languages may use ElDoc by adding an +;; appropriate function to the buffer-local value of +;; `eldoc-documentation-functions'. ;;; Code: @@ -222,8 +222,8 @@ expression point is on." (defun eldoc--eval-expression-setup () ;; Setup `eldoc', similar to `emacs-lisp-mode'. FIXME: Call ;; `emacs-lisp-mode' itself? - (add-function :before-until (local 'eldoc-documentation-function) - #'elisp-eldoc-documentation-function) + (add-hook 'eldoc-documentation-functions + #'elisp-eldoc-documentation-function nil t) (eldoc-mode +1)) ;;;###autoload @@ -235,7 +235,11 @@ See `eldoc-documentation-function' for more detail." (defun eldoc--supported-p () "Non-nil if an ElDoc function is set for this buffer." - (not (memq eldoc-documentation-function '(nil ignore)))) + (let ((hook 'eldoc-documentation-functions)) + (and (not (memq eldoc-documentation-function '(nil ignore))) + (or (and (local-variable-p hook) + (buffer-local-value hook (current-buffer))) + (default-value hook))))) (defun eldoc-schedule-timer () @@ -347,8 +351,46 @@ Also store it in `eldoc-last-message' and return that value." (not (or executing-kbd-macro (bound-and-true-p edebug-active)))) -;;;###autoload -(defvar eldoc-documentation-function #'ignore +(defvar eldoc-documentation-functions nil + "Hook for functions to call to return doc string. +Each function should accept no arguments and return a one-line +string for displaying doc about a function etc. appropriate to +the context around point. It should return nil if there's no doc +appropriate for the context. Typically doc is returned if point +is on a function-like name or in its arg list. + +Major modes should modify this hook locally, for example: + (add-hook \\='eldoc-documentation-functions #\\='foo-mode-eldoc nil t) +so that the global value (i.e. the default value of the hook) is +taken into account if the major mode specific function does not +return any documentation.") + +(defun eldoc-documentation-default () + "Show first doc string for item at point. +Default value for `eldoc-documentation-function'." + (let ((res (run-hook-with-args-until-success 'eldoc-documentation-functions))) + (when res + (if eldoc-echo-area-use-multiline-p res + (truncate-string-to-width + res (1- (window-width (minibuffer-window)))))))) + +(defun eldoc-documentation-compose () + "Show multiple doc string results at once. +Meant as a value for `eldoc-documentation-function'." + (let (res) + (run-hook-wrapped + 'eldoc-documentation-functions + (lambda (f) + (let ((str (funcall f))) + (when str (push str res)) + nil))) + (when res + (setq res (mapconcat #'identity (nreverse res) ", ")) + (if eldoc-echo-area-use-multiline-p res + (truncate-string-to-width + res (1- (window-width (minibuffer-window)))))))) + +(defcustom eldoc-documentation-function #'eldoc-documentation-default "Function to call to return doc string. The function of no args should return a one-line string for displaying doc about a function etc. appropriate to the context around point. @@ -359,14 +401,14 @@ arg list. The result is used as is, so the function must explicitly handle the variables `eldoc-argument-case' and `eldoc-echo-area-use-multiline-p', and the face `eldoc-highlight-function-argument', if they are to have any -effect. - -Major modes should modify this variable using `add-function', for example: - (add-function :before-until (local \\='eldoc-documentation-function) - #\\='foo-mode-eldoc-function) -so that the global documentation function (i.e. the default value of the -variable) is taken into account if the major mode specific function does not -return any documentation.") +effect." + :link '(info-link "(emacs) Lisp Doc") + :type '(radio (function-item eldoc-documentation-default) + (function-item eldoc-documentation-compose) + (function :tag "Other function") + (const :tag "None" nil)) + :version "28.1" + :group 'eldoc) (defun eldoc-print-current-symbol-info () "Print the text produced by `eldoc-documentation-function'." @@ -381,7 +423,8 @@ return any documentation.") ;; Only keep looking for the info as long as the user hasn't ;; requested our attention. This also locally disables inhibit-quit. (while-no-input - (eldoc-message (funcall eldoc-documentation-function))))))) + (let ((fun eldoc-documentation-function)) + (when fun (eldoc-message (funcall fun))))))))) ;; If the entire line cannot fit in the echo area, the symbol name may be ;; truncated or eliminated entirely from the output to make room for the diff --git a/lisp/hexl.el b/lisp/hexl.el index 58518e74169..cf7118f2089 100644 --- a/lisp/hexl.el +++ b/lisp/hexl.el @@ -367,8 +367,8 @@ You can use \\[hexl-find-file] to visit a file in Hexl mode. (add-hook 'change-major-mode-hook #'hexl-maybe-dehexlify-buffer nil t) ;; Set a callback function for eldoc. - (add-function :before-until (local 'eldoc-documentation-function) - #'hexl-print-current-point-info) + (add-hook 'eldoc-documentation-functions + #'hexl-print-current-point-info nil t) (eldoc-add-command-completions "hexl-") (eldoc-remove-command "hexl-save-buffer" "hexl-current-address") @@ -455,6 +455,8 @@ and edit the file in `hexl-mode'." ;; 2. reset change-major-mode-hook in case that `hexl-mode' ;; previously added hexl-maybe-dehexlify-buffer to it. (remove-hook 'change-major-mode-hook #'hexl-maybe-dehexlify-buffer t) + (remove-hook 'eldoc-documentation-functions + #'hexl-print-current-point-info t) (setq major-mode 'fundamental-mode) (hexl-mode))) diff --git a/lisp/ielm.el b/lisp/ielm.el index 41675c011d8..fc06ebfa2db 100644 --- a/lisp/ielm.el +++ b/lisp/ielm.el @@ -541,8 +541,8 @@ Customized bindings may be defined in `ielm-map', which currently contains: (set (make-local-variable 'completion-at-point-functions) '(comint-replace-by-expanded-history ielm-complete-filename elisp-completion-at-point)) - (add-function :before-until (local 'eldoc-documentation-function) - #'elisp-eldoc-documentation-function) + (add-hook 'eldoc-documentation-functions + #'elisp-eldoc-documentation-function nil t) (set (make-local-variable 'ielm-prompt-internal) ielm-prompt) (set (make-local-variable 'comint-prompt-read-only) ielm-prompt-read-only) (setq comint-get-old-input 'ielm-get-old-input) diff --git a/lisp/progmodes/cfengine.el b/lisp/progmodes/cfengine.el index 18df372f2c6..f25b3cb9e2b 100644 --- a/lisp/progmodes/cfengine.el +++ b/lisp/progmodes/cfengine.el @@ -1390,12 +1390,15 @@ to the action header." (when buffer-file-name (shell-quote-argument buffer-file-name))))) - ;; For emacs < 25.1 where `eldoc-documentation-function' defaults to - ;; nil. - (or eldoc-documentation-function - (setq-local eldoc-documentation-function #'ignore)) - (add-function :before-until (local 'eldoc-documentation-function) - #'cfengine3-documentation-function) + (if (boundp 'eldoc-documentation-functions) + (add-hook 'eldoc-documentation-functions + #'cfengine3-documentation-function nil t) + ;; For emacs < 25.1 where `eldoc-documentation-function' defaults + ;; to nil. + (or eldoc-documentation-function + (setq-local eldoc-documentation-function #'ignore)) + (add-function :before-until (local 'eldoc-documentation-function) + #'cfengine3-documentation-function)) (add-hook 'completion-at-point-functions #'cfengine3-completion-function nil t) diff --git a/lisp/progmodes/elisp-mode.el b/lisp/progmodes/elisp-mode.el index 2617a6e4cce..813b628bc35 100644 --- a/lisp/progmodes/elisp-mode.el +++ b/lisp/progmodes/elisp-mode.el @@ -250,8 +250,8 @@ Blank lines separate paragraphs. Semicolons start comments. (add-hook 'electric-pair-mode-hook #'emacs-lisp-set-electric-text-pairs)) (setq-local electric-quote-string t) (setq imenu-case-fold-search nil) - (add-function :before-until (local 'eldoc-documentation-function) - #'elisp-eldoc-documentation-function) + (add-hook 'eldoc-documentation-functions + #'elisp-eldoc-documentation-function nil t) (add-hook 'xref-backend-functions #'elisp--xref-backend nil t) (setq-local project-vc-external-roots-function #'elisp-load-path-roots) (add-hook 'completion-at-point-functions diff --git a/lisp/progmodes/octave.el b/lisp/progmodes/octave.el index 9e039562549..352c1810d1f 100644 --- a/lisp/progmodes/octave.el +++ b/lisp/progmodes/octave.el @@ -619,8 +619,7 @@ Key bindings: (add-hook 'before-save-hook 'octave-sync-function-file-names nil t) (setq-local beginning-of-defun-function 'octave-beginning-of-defun) (and octave-font-lock-texinfo-comment (octave-font-lock-texinfo-comment)) - (add-function :before-until (local 'eldoc-documentation-function) - 'octave-eldoc-function) + (add-hook 'eldoc-documentation-functions 'octave-eldoc-function nil t) (easy-menu-add octave-mode-menu)) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index a2d85d0bef8..67383b34154 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -5544,8 +5544,10 @@ REPORT-FN is Flymake's callback function." ;; Emacs<25 (set (make-local-variable 'eldoc-documentation-function) #'python-eldoc-function) - (add-function :before-until (local 'eldoc-documentation-function) - #'python-eldoc-function)) + (if (boundp 'eldoc-documentation-functions) + (add-hook 'eldoc-documentation-functions #'python-eldoc-function nil t) + (add-function :before-until (local 'eldoc-documentation-function) + #'python-eldoc-function))) (add-to-list 'hs-special-modes-alist -- cgit v1.2.3 From 4a94881345819dd68062bc729b5b7ddeab00041b Mon Sep 17 00:00:00 2001 From: Ryan Olson Date: Sun, 23 Feb 2020 08:15:10 -0700 Subject: Show friendly message after package install * lisp/emacs-lisp/package.el (package-install): Once we know the package has successfully been installed using the `package-install` command, instead of relying on the compile "Done" message, give a message that tells the user that the package has been installed. (Bug#21857) Copyright-paperwork-exempt: yes --- lisp/emacs-lisp/package.el | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lisp/emacs-lisp') diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el index c91ee445e13..43eb038a865 100644 --- a/lisp/emacs-lisp/package.el +++ b/lisp/emacs-lisp/package.el @@ -2081,7 +2081,8 @@ to install it but still mark it as selected." (package-compute-transaction () (list (list pkg)))))) (progn (package-download-transaction transaction) - (package--quickstart-maybe-refresh)) + (package--quickstart-maybe-refresh) + (message "Package `%s' installed." name)) (message "`%s' is already installed" name)))) (defun package-strip-rcs-id (str) -- cgit v1.2.3