unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Michal Nazarewicz <mina86@mina86.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: petewil@google.com, emacs-devel@gnu.org
Subject: Re: [PATCH] for review - Allow expansion of "~" (as opposed to "~user")
Date: Tue, 03 Mar 2015 18:48:24 +0100	[thread overview]
Message-ID: <xa1tmw3urmzb.fsf@mina86.com> (raw)
In-Reply-To: <83fv9mqeh9.fsf@gnu.org>

On Tue, Mar 03 2015, Eli Zaretskii <eliz@gnu.org> wrote:
>> From: Michal Nazarewicz <mina86@mina86.com>
>> Cc: petewil@google.com, emacs-devel@gnu.org
>> Date: Mon, 02 Mar 2015 21:37:56 +0100
>> 
>> >> Looking at that code, it appears it’s inconsistent on Windows when it
>> >> comes to handling ~/.emacs.d/init.el.  If I understand it correctly,
>> >> here’s how Emacs behave on Windows:
>> >> 
>> >> * emacs        -> load ~/.emacs, ~/_emacs or ~/.emacs.d/init.el
>> >> * emacs -u foo -> load ~/.emacs or ~/_emacs
>> >
>> > I'm not bothered about the inconsistency, since the "-u foo"
>> > "handling" is a kludge for a situation that shouldn't happen.  I
>> > wouldn't object to emitting an error in that case.
>> 
>> Doesn’t this basically mean doing this:
>> 
>> -       (if (file-directory-p (expand-file-name
>> -                              ;; We don't support ~USER on MS-Windows
>> -                              ;; and MS-DOS except for the current
>> -                              ;; user, and always load .emacs from
>> -                              ;; the current user's home directory
>> -                              ;; (see below).  So always check "~",
>> -                              ;; even if invoked with "-u USER", or
>> -                              ;; if $USER or $LOGNAME are set to
>> -                              ;; something different.
>> -                              (if (memq system-type '(windows-nt ms-dos))
>> -                                  "~"
>> -                                (concat "~" init-file-user))))
>> -           nil
>> -         (display-warning 'initialization
>> +       (unless (file-directory-p (expand-file-name
>> +                                   (concat "~" init-file-user)))
>> +          (display-warning 'initialization
>> 
>> and then using ~user/… for every system including windows-nt?
>
> Not exactly.  First, we had what you suggest before 1df1e49eb, so
> going back to that code means re-introducing the problem it solved.

1df1e49eb actually makes sense to me (since windows-nt ignores
`init-file-user' when looking for .emacs) but 5463218ce doesn’t since
ms-dos uses `init-file-user' when looking for _emacs.

> Second, it's not really nice to have init-file-user have the value of,
> say, "~bob/.emacs", when Emacs was invoked with "-u bob", whereas in
> fact we loaded "C:/fred's-home/.emacs".

That does not happen.  If an init file has been loaded, `user-itni-file'
variable *will* point to it.

However, if ‘bob’ is not a valid user, and fred runs Emacs with ‘-u
bob’, `init-file-user' will be "bob" while `user-init-file' will be:

* on Windows: "C:/fred's-home/.emacs"      (if that file exists) or
              "~/.emacs"                   (if it does not);
* on DOS:     "D:/current/dir/~bob/_emacs" (if that file exists) or
              "~bob/_emacs"                (if it does not); and
* on POSIX:   "/current/dir/~bob/.emacs"   (if that file exists) or 
              "~bob/.emacs"                (if it does not).

If ‘bob’ is a valid user with a home directory though, `user-init-file'
will be:

* on Windows: "C:/fred's-home/.emacs"      (if that file exists) or
              "~/.emacs"                   (if it does not);
* on DOS:     "C:/bob's-home/_emacs"       (if that file exists) or
              "~bob/_emacs"                (if it does not); and
* on POSIX:   "/home/bob/.emacs"           (if that file exists) or 
              "~bob/.emacs"                (if it does not).

Furthermore:

* DOS port will check "C:/fred's-home" directory even though it will try
  to load "C:/fred's-home/~bob/_emacs" or "C:/bob's-home/_emacs" file; and
* Windows port will load "C:/fred's-home/.emacs" even if
  "C:/bob's-home/.emacs" or "C:/fred's-home/~bob/.emacs" exists.

> It's a lie, and one that's tricky to untangle: you need to know that
> to get a _real_ file name, you need to run it through
> expand-file-name.  If you don't, things will subtly fail, e.g., if you
> compare this with some other file name.

This is already a problem on *all* systems and it happens if user init
file is not found.  If you run ‘emacs -u foo’ on GNU/Linux, you’ll end
up with `user-init-file" equal to "~foo/.emacs".

> So I'd rather we either errored out with such invocation, or fixed the
> value to not lie, as we do now.
>
>> If I understand correctly, ~user/… expansion depends on getpwnam and a)
>> on MS-DOS DJGPP provides it (by having a single pw entry for user ==
>> current user), b) on W32 src/w32.c provides it (with the same
>> implementation) and on POSIX we have it properly implemented.

> Another important piece of the puzzle is that if USER is not
> recognized as a valid user by the getpwnam emulation on
> MS-Windows/MS-DOS, you get this:
>
>   (expand-file-name "~USER") => /current/directory/~USER
>
> And it is possible to have a literal "~USER" directory, in which case
> the warning will be incorrectly skipped.

This also happens on UNIX-like systems.  Like I’ve said above, if you
run ‘emacs -u foo’ on GNU/Linux, you end up with "~foo/.emacs"
`user-init-file'.  You also end up with ‘User foo has no home directory’
warning.

Based on all that, I think the most consistent option is to:

1) always check if ~USER/ directory exists for the ‘User foo has no home
   directory’ warning;

2) user ~USER/.emacs and ~USER/_emacs on MS-Windows; and

3) use `expand-file-name' when setting `user-init-file' if init file has
   not been loaded.

The below untested patch does that plus stops Emacs from attempting to
load init file if `init-file-user' is ‘invalid’, i.e. contains ~, /, :
or \n.

From d96e4d6e4f9ed3919351f81bdb50412f9a1ea178 Mon Sep 17 00:00:00 2001
From: Michal Nazarewicz <mina86@mina86.com>
Date: Tue, 3 Mar 2015 18:44:27 +0100
Subject: [PATCH] startup.el: Harmonise initialisation file loading across
 systems
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* lisp/startup.el (command-line): Change how initialisation file is
  loaded on various systems in the effort to harmonise it and get rid
  of various inconsistencies.

  1. If init file has not been loaded, the `user-file-user' will now
  be an absolute path rather than "~/.emacs" (or "~/_emacs" on DOS).

  2. With ‘-u’ argument, MS-Windows version of Emacs will now try to
  load ~USER/.emacs, ~USER/_emacs and finally ~USER/.emacs.d/init
  files (in that order) rather than ~/.emacs, ~/_emacs and
  ~USER/.emacs.d/init.

  NB: If `-u’ argument specifies an unknown user, expansion of
  "~USER/…" yields "/current/working/directory/~USER/…"  This is
  nothing new and it’s how *NIX and DOS ports behaved already.

  3. With ‘-u’ argument, MS-DOS and MS-Windows versions will check if
  "~USER/" directory exists before issuing a ‘user has no home’
  warning.  This is consistent with the fact that both those ports
  will attempt to read files inside of "~USER/".  (For MS-DOS port,
  the latter has already been the case, but it checked "~/"
  nonetheless).
---
 lisp/startup.el | 159 ++++++++++++++++++++++----------------------------------
 1 file changed, 63 insertions(+), 96 deletions(-)

diff --git a/lisp/startup.el b/lisp/startup.el
index 999e53e..0bfb5f0 100644
--- a/lisp/startup.el
+++ b/lisp/startup.el
@@ -1043,108 +1043,75 @@ (defun command-line ()
     (setq inhibit-startup-screen nil)
 
     ;; Warn for invalid user name.
-    (when init-file-user
-      (if (string-match "[~/:\n]" init-file-user)
-	  (display-warning 'initialization
-			   (format "Invalid user name %s"
-				   init-file-user)
-			   :error)
-	(if (file-directory-p (expand-file-name
-			       ;; We don't support ~USER on MS-Windows
-			       ;; and MS-DOS except for the current
-			       ;; user, and always load .emacs from
-			       ;; the current user's home directory
-			       ;; (see below).  So always check "~",
-			       ;; even if invoked with "-u USER", or
-			       ;; if $USER or $LOGNAME are set to
-			       ;; something different.
-			       (if (memq system-type '(windows-nt ms-dos))
-				   "~"
-				 (concat "~" init-file-user))))
-	    nil
-	  (display-warning 'initialization
-			   (format "User %s has no home directory"
-				   (if (equal init-file-user "")
-				       (user-real-login-name)
-				     init-file-user))
-			   :error))))
-
     ;; Load that user's init file, or the default one, or none.
     (let (debug-on-error-from-init-file
 	  debug-on-error-should-be-set
 	  (debug-on-error-initial
 	   (if (eq init-file-debug t) 'startup init-file-debug))
-	  (orig-enable-multibyte (default-value 'enable-multibyte-characters)))
+	  (orig-enable-multibyte (default-value 'enable-multibyte-characters))
+          home-dir)
+
+      (when init-file-user
+        (if (string-match "[~/:\n]" init-file-user)
+            (display-warning 'initialization
+                             (format "Invalid user name %s" init-file-user)
+                             :error)
+          (setq home-dir (file-name-as-directory
+                          (expand-file-name (concat "~" init-file-user))))
+          (unless (file-directory-p home-dir)
+            (display-warning 'initialization
+                             (format "User %s has no home directory"
+                                     (if (equal init-file-user "")
+                                         (user-real-login-name)
+                                       init-file-user))
+                             :error))))
+
       (let ((debug-on-error debug-on-error-initial)
-	    ;; This function actually reads the init files.
-	    (inner
-	     (function
-	      (lambda ()
-		(if init-file-user
-		    (let ((user-init-file-1
-			   (cond
-			     ((eq system-type 'ms-dos)
-			      (concat "~" init-file-user "/_emacs"))
-			     ((not (eq system-type 'windows-nt))
-			      (concat "~" init-file-user "/.emacs"))
-			     ;; Else deal with the Windows situation
-			     ((directory-files "~" nil "^\\.emacs\\(\\.elc?\\)?$")
-			      ;; Prefer .emacs on Windows.
-			      "~/.emacs")
-			     ((directory-files "~" nil "^_emacs\\(\\.elc?\\)?$")
-			      ;; Also support _emacs for compatibility, but warn about it.
-			      (push '(initialization
-				      "`_emacs' init file is deprecated, please use `.emacs'")
-				    delayed-warnings-list)
-			      "~/_emacs")
-			     (t ;; But default to .emacs if _emacs does not exist.
-			      "~/.emacs"))))
-		      ;; This tells `load' to store the file name found
-		      ;; into user-init-file.
-		      (setq user-init-file t)
-		      (load user-init-file-1 t t)
-
-		      (when (eq user-init-file t)
-			;; If we did not find ~/.emacs, try
-			;; ~/.emacs.d/init.el.
-			(let ((otherfile
-			       (expand-file-name
-				"init"
-				(file-name-as-directory
-				 (concat "~" init-file-user "/.emacs.d")))))
-			  (load otherfile t t)
-
-			  ;; If we did not find the user's init file,
-			  ;; set user-init-file conclusively.
-			  ;; Don't let it be set from default.el.
-			  (when (eq user-init-file t)
-			    (setq user-init-file user-init-file-1))))
-
-		      ;; If we loaded a compiled file, set
-		      ;; `user-init-file' to the source version if that
-		      ;; exists.
-		      (when (and user-init-file
-				 (equal (file-name-extension user-init-file)
-					"elc"))
-			(let* ((source (file-name-sans-extension user-init-file))
-			       (alt (concat source ".el")))
-			  (setq source (cond ((file-exists-p alt) alt)
-					     ((file-exists-p source) source)
-					     (t nil)))
-			  (when source
-			    (when (file-newer-than-file-p source user-init-file)
-			      (message "Warning: %s is newer than %s"
-				       source user-init-file)
-			      (sit-for 1))
-			    (setq user-init-file source))))
-
-		      (unless inhibit-default-init
-                        (let ((inhibit-startup-screen nil))
-                          ;; Users are supposed to be told their rights.
-                          ;; (Plus how to get help and how to undo.)
-                          ;; Don't you dare turn this off for anyone
-                          ;; except yourself.
-                          (load "default" t t)))))))))
+            ;; This function actually reads the init files.
+            (inner
+             (function
+              (lambda ()
+                (when home-dir
+                  ;; This tells `load' to store the file name found
+                  ;; into user-init-file.
+                  (setq user-init-file t)
+                  ;; On MS-DOS, try ~USER/_emacs.  On MS-Windows try
+                  ;; ~USER/.emacs, ~USER/_emacs and ~/emacs.d/init.  On all
+                  ;; other systems, try ~USER/.emacs and ~/emacs.d/init.
+                  (if (or (and (not (eq system-type 'ms-dos))
+                               (load (concat home-dir ".emacs") t t))
+                          (and (memq system-type '(ms-dos windows-nt))
+                               (load (concat home-dir "_emacs") t t))
+                          (and (not (eq system-type 'ms-dos))
+                               (load (concat home-dir ".emacs.d/init") t t)))
+                      ;; If we loaded a compiled file, set `user-init-file' to
+                      ;; the source version if that exists.
+                      (when (equal (file-name-extension user-init-file) "elc")
+                        (let* ((src (file-name-sans-extension user-init-file))
+                               (alt (concat src ".el")))
+                          (setq src (cond ((file-exists-p alt) alt)
+                                          ((file-exists-p src) src)
+                                          (t nil)))
+                          (when source
+                            (when (file-newer-than-file-p src user-init-file)
+                              (message "Warning: %s is newer than %s"
+                                       src user-init-file)
+                              (sit-for 1))
+                            (setq user-init-file src))))
+                    ;; If we failed to load the init file, set `user-init-file'
+                    ;; to the default location for it.  On MS-DOS that’s
+                    ;; ~USER/_emacs and on all other systems it’s ~USER/.emacs
+                    (setq user-init-file (if (eq system-type 'ms-dos)
+                                             (concat home-dir "_emacs")
+                                           (concat home-dir ".emacs")))
+
+                    (unless inhibit-default-init
+                      (let ((inhibit-startup-screen nil))
+                        ;; Users are supposed to be told their rights.  (Plus
+                        ;; how to get help and how to undo.)  Don't you dare
+                        ;; turn this off for anyone except yourself.
+                        (load "default" t t)))))))))
+
 	(if init-file-debug
 	    ;; Do this without a condition-case if the user wants to debug.
 	    (funcall inner)
-- 
Best regards,                                         _     _
.o. | Liege of Serenely Enlightened Majesty of      o' \,=./ `o
..o | Computer Science,  Michał “mina86” Nazarewicz    (o o)
ooo +--<mpn@google.com>--<xmpp:mina86@jabber.org>--ooO--(_)--Ooo--



  reply	other threads:[~2015-03-03 17:48 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-02-28  0:06 [PATCH] for review - Allow expansion of "~" (as opposed to "~user") Pete Williamson
2015-02-28  8:08 ` Eli Zaretskii
2015-03-02  9:44   ` Michal Nazarewicz
2015-03-02 13:50     ` Eli Zaretskii
2015-03-02 20:37       ` Michal Nazarewicz
2015-03-03 15:37         ` Eli Zaretskii
2015-03-03 17:48           ` Michal Nazarewicz [this message]
2015-03-03 18:09             ` Eli Zaretskii
2015-03-03 18:20               ` Eli Zaretskii
2015-03-03 18:22                 ` Pete Williamson
2015-03-03 18:36                   ` Eli Zaretskii
2015-03-03 18:38                     ` Pete Williamson
2015-03-03 21:01                       ` Michal Nazarewicz
2015-03-03 21:03               ` Michal Nazarewicz
2015-03-04 18:06                 ` Eli Zaretskii

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=xa1tmw3urmzb.fsf@mina86.com \
    --to=mina86@mina86.com \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    --cc=petewil@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).