diff --git a/lisp/files.el b/lisp/files.el
index ce4dd99bd535906f6df0d77bb418a14c2814f5a5..d32c6acb82cbfcbf554c73571467c8aafe1f22bd 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -1307,9 +1307,14 @@ containing it, until no links are left at any level.
(let ((handler (find-file-name-handler filename 'file-truename)))
;; For file name that has a special handler, call handler.
;; This is so that ange-ftp can save time by doing a no-op.
- (if handler
- (setq filename (funcall handler 'file-truename filename)
- done t)
+ (or
+ (if handler
+ (setq filename (funcall handler 'file-truename filename)
+ done t)
+ (condition-case nil
+ (setq filename (fileio--truename filename)
+ done t)
+ (file-missing nil)))
(let ((dir (or (file-name-directory filename) default-directory))
target dirfile)
;; Get the truename of the directory.
diff --git a/src/fileio.c b/src/fileio.c
index 968a55e595615599d4f85890568523ac866fcac9..adf2f3d9705f5065e47cc6cf6765ce4ff1df2dac 100644
--- a/src/fileio.c
+++ b/src/fileio.c
@@ -20,6 +20,7 @@ along with GNU Emacs. If not, see . */
#include
#include
#include
+#include
#include "sysstdio.h"
#include
#include
@@ -2693,6 +2694,46 @@ file_name_absolute_p (char const *filename)
&& (!filename[1] || IS_DIRECTORY_SEP (filename[1])
|| user_homedir (&filename[1]))));
}
+
+DEFUN ("fileio--truename", Ffileio__truename, Sfileio__truename, 1, 1, 0,
+ doc: /* Return the true name of FILENAME, without file name handlers.
+
+The returned string is an absolute file name that does not involve
+\".\", \"..\", or symbolic links. Signal an error if FILENAME does
+not exist or if its true name cannot be determined. */)
+ (Lisp_Object filename)
+{
+ CHECK_STRING (filename);
+ Lisp_Object absname = Fexpand_file_name (filename, Qnil);
+ Lisp_Object encoded_absname = ENCODE_FILE (absname);
+ ptrdiff_t encoded_len = SBYTES (encoded_absname);
+ char *encoded = SSDATA (encoded_absname);
+ bool append_slash = (1 < encoded_len
+ && IS_DIRECTORY_SEP (encoded[encoded_len - 1])
+ && !IS_DIRECTORY_SEP (encoded[encoded_len - 2]));
+ char *truename = realpath (encoded, NULL);
+ if (!truename)
+ report_file_error ("Deriving truename", filename);
+ ptrdiff_t truename_len = strlen (truename);
+ if (truename_len == encoded_len - append_slash
+ && memcmp (truename, encoded, truename_len) == 0)
+ {
+ /* ABSNAME is already the true name. */
+ xfree (truename);
+ return absname;
+ }
+ else
+ {
+ if (append_slash)
+ {
+ truename = xrealloc (truename, truename_len + 2);
+ strcpy (truename + truename_len, "/");
+ }
+ Lisp_Object unibyte_truename = build_unibyte_string (truename);
+ xfree (truename);
+ return DECODE_FILE (unibyte_truename);
+ }
+}
DEFUN ("file-exists-p", Ffile_exists_p, Sfile_exists_p, 1, 1, 0,
doc: /* Return t if file FILENAME exists (whether or not you can read it).
@@ -6428,6 +6469,7 @@ This includes interactive calls to `delete-file' and
defsubr (&Sadd_name_to_file);
defsubr (&Smake_symbolic_link);
defsubr (&Sfile_name_absolute_p);
+ defsubr (&Sfileio__truename);
defsubr (&Sfile_exists_p);
defsubr (&Sfile_executable_p);
defsubr (&Sfile_readable_p);