From b865f291ea06536aec28744b7bf896ed36aceace Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Mon, 2 Sep 2019 11:49:08 -0700 Subject: [PATCH] Assume XDG only in XDGish environments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not assume XDG conventions for configuration file names when starting in a home directory that has no Emacs configuration files. Instead, use XDG conventions only in XDGish environments. * doc/emacs/custom.texi (Find Init): * doc/lispref/os.texi (Init File): * etc/NEWS: Describe adjusted behavior. * lisp/files.el (locate-user-emacs-file): Do not use (make-directory user-emacs-directory t), as that’s too aggressive: we don’t want to make ancestors of parents. Instead, just make the parent directory if needed for XDG. * lisp/startup.el (startup--xdg-config-default) (startup--xdg-config-home-emacs, startup--xdg-or-homedot): Remove, replacing with ... (startup--xdg-config-home-default, startup--xdg-config-home) (startup--init-dir-file-cache, startup--init-dir-file): ... these new vars and function. All uses changed. (normal-top-level): Implement the changed behavior. (startup--load-user-init-file): New args DIR and FILE, replacing old args FILENAME-FUNCTION and ALTERNATE-FILENAME-FUNCTION. This simplifies both callers and callees, now that the callers already check for home-directory init files. All callers changed. --- doc/emacs/custom.texi | 16 ++-- doc/lispref/os.texi | 11 +-- etc/NEWS | 2 +- lisp/files.el | 10 ++- lisp/startup.el | 181 ++++++++++++++++++++---------------------- 5 files changed, 109 insertions(+), 111 deletions(-) diff --git a/doc/emacs/custom.texi b/doc/emacs/custom.texi index 0c2509e1cd..eef0ff9a4f 100644 --- a/doc/emacs/custom.texi +++ b/doc/emacs/custom.texi @@ -2638,15 +2638,13 @@ Find Init Emacs normally finds your init file in a location under your home directory. @xref{Init File}. By default this location is -@file{~/.config/emacs/init.el} where @file{~/} stands for your home directory. -This default can be overridden as described below. +@file{$XDG_CONFIG_HOME/emacs/init.el} if @env{XDG_CONFIG_HOME} is set +in your environment, otherwise @file{~/.config/emacs/init.el} if the +directory @file{~/.config/emacs} exists, where @file{~/} stands for +your home directory. - If @env{XDG_CONFIG_HOME} is set in your environment, its -value replaces @file{~/.config} in the name of the default -init file. - - If the default init file's parent directory does not exist but the -directory @file{~/.emacs.d} does exist, Emacs looks for your init file + If @env{XDG_CONFIG_HOME} is unset and @file{~/.config/emacs} +is absent, Emacs looks for your init file using the filenames @file{~/.emacs.el}, @file{~/.emacs}, or @file{~/.emacs.d/init.el}; you can choose to use any one of these names. (Note that only the locations directly in your home directory @@ -2656,7 +2654,7 @@ Find Init that troubleshooting a problem that might be due to a bad init file, or archiving a collection of init files, can be done by renaming that directory. To help older Emacs versions find configuration files in -their current default locations, you can execute the following +the @file{~/.config} location, you can execute the following Emacs Lisp code: @example diff --git a/doc/lispref/os.texi b/doc/lispref/os.texi index c94e96bde8..69a8209882 100644 --- a/doc/lispref/os.texi +++ b/doc/lispref/os.texi @@ -474,12 +474,13 @@ Init File @defvar user-emacs-directory This variable holds the name of the Emacs default directory. -It defaults to @file{$@{XDG_CONFIG_HOME-'~/.config'@}/emacs/} -if that directory exists and @file{~/.emacs.d/} does not exist, +It defaults to @file{$XDG_CONFIG_HOME/emacs/} if the environment +variable @env{XDG_CONFIG_HOME} is set, otherwise to +@file{~/.config/emacs/} if that directory exists, otherwise to @file{~/.emacs.d/} on all platforms but MS-DOS@. -Here, @file{$@{XDG_CONFIG_HOME-'~/.config'@}} -stands for the value of the environment variable @env{XDG_CONFIG_HOME} -if that variable is set, and for @file{~/.config} otherwise. +In an interactive session, the directory is created if necessary; +in this case the @file{~/.emacs.d/} convention is assumed if a home-directory +init file exists or if the platform does not follow the XDG convention. @xref{Find Init,,How Emacs Finds Your Init File, emacs, The GNU Emacs Manual}. @end defvar diff --git a/etc/NEWS b/etc/NEWS index d5130e9f3c..5eb3d15c30 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -145,7 +145,7 @@ and will override their traditional locations (the home directory, ~/.emacs.d, etc.). Emacs will still look for init files in their traditional locations if -XDG_CONFIG_HOME does not exist, so invoking Emacs with +XDG_CONFIG_HOME is unset and ~/.config/emacs is absent, so setting XDG_CONFIG_HOME='/nowhere' might be useful if your new-location init files are scrambled, or if you want to force Emacs to ignore files under XDG_CONFIG_HOME for some other reason. diff --git a/lisp/files.el b/lisp/files.el index ce4dd99bd5..7fc4f38ea9 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -1043,7 +1043,15 @@ locate-user-emacs-file (setq errtype "access")) (with-file-modes ?\700 (condition-case nil - (make-directory user-emacs-directory t) + (condition-case nil + (make-directory user-emacs-directory) + (file-missing + (when (string-suffix-p "/.config/emacs/" + user-emacs-directory) + ;; Make both .config and .config/emacs, XDG style. + (make-directory + (substring user-emacs-directory 0 -7)) + (make-directory user-emacs-directory)))) (error (setq errtype "create"))))) (when (and errtype user-emacs-directory-warning diff --git a/lisp/startup.el b/lisp/startup.el index a16db242da..5a530c17a0 100644 --- a/lisp/startup.el +++ b/lisp/startup.el @@ -490,26 +490,57 @@ normal-top-level-add-to-load-path (when tail (setcdr tail (append (mapcar 'expand-file-name dirs) (cdr tail)))))) -;; The default location for XDG-convention Emacs init files. -(defconst startup--xdg-config-default "~/.config/emacs/") -;; The location for XDG-convention Emacs init files. -(defvar startup--xdg-config-home-emacs) - -;; Return the name of the init file directory for Emacs, assuming -;; XDG-DIR is the XDG location and USER-NAME is the user name. -;; If USER-NAME is nil or "", use the current user. -;; Prefer the XDG location unless it does does not exist and the -;; .emacs.d location does exist. -(defun startup--xdg-or-homedot (xdg-dir user-name) - (if (file-exists-p xdg-dir) - xdg-dir - (let ((emacs-d-dir (concat "~" user-name - (if (eq system-type 'ms-dos) - "/_emacs.d/" - "/.emacs.d/")))) - (if (file-exists-p emacs-d-dir) - emacs-d-dir - xdg-dir)))) +;; The default location for XDG-convention configuration. +(defconst startup--xdg-config-home-default "~/.config") +;; The location for XDG-convention configuration. +(defvar startup--xdg-config-home) +;; A cons (INIT-DIR . INIT-FILE) computed during startup. +(defvar startup--init-dir-file-cache) + +;; Return an (INIT-DIR . INIT-FILE) pair. +;; The latter is either absolute, or relative to the former. +;; XDG-CONFIG-HOME is the XDG config home and HOME-DIR the home directory. +;; Prefer the XDG location if XDG-CONFIG-HOME/emacs exists, or +;; if XDG-CONFIG-HOME exists and non-XDG init files are absent. +(defun startup--init-dir-file (xdg-config-home home-dir) + (let ((xdg-emacs (concat xdg-config-home "/emacs/"))) + (or (unless (file-accessible-directory-p xdg-emacs) + (let* ((dot-emacs (concat home-dir + (if (eq system-type 'ms-dos) + "/_emacs.d/" "/.emacs.d/"))) + (match (if (memq system-type '(windows-nt ms-dos)) + "\\`[._]emacs\\(\\.elc?\\)?\\'" + "\\`\\.emacs\\(\\.elc?\\)?\\'")) + (inits (ignore-errors + (directory-files home-dir nil match t)))) + (when (or inits (file-accessible-directory-p dot-emacs) + ;; No Emacs configuration files are present, so + ;; choose their convention by looking at context. + ;; Assume the XDG convention if it is already in use by + ;; this user for other programs (a cheaper test), + ;; or if the platform follows the XDG convention + ;; (a more expensive test). + (not (or (file-accessible-directory-p xdg-config-home) + (executable-find "xdg-open")))) + (cons dot-emacs + (if (not inits) + "init" + (concat + home-dir + (or (when (and (eq system-type 'windows-nt) + (not (catch t + (dolist (init inits) + (when (eq ?. (aref init 0)) + (throw t t)))))) + (push `(initialization + ,(format-message + (concat + "`_emacs' init file is deprecated," + " please use `.emacs'"))) + delayed-warnings-list) + "/_emacs") + "/.emacs"))))))) + (cons xdg-emacs "init")))) (defun normal-top-level () "Emacs calls this function when it first starts up. @@ -520,13 +551,14 @@ normal-top-level (message internal--top-level-message) (setq command-line-processed t) - (setq startup--xdg-config-home-emacs + (setq startup--init-dir-file-cache (let ((xdg-config-home (getenv-internal "XDG_CONFIG_HOME"))) + (setq startup--xdg-config-home + (or xdg-config-home startup--xdg-config-home-default)) (if xdg-config-home - (concat xdg-config-home "/emacs/") - startup--xdg-config-default))) - (setq user-emacs-directory - (startup--xdg-or-homedot startup--xdg-config-home-emacs nil)) + (cons (concat xdg-config-home "/emacs/") "init") + (startup--init-dir-file startup--xdg-config-home-default "~")))) + (setq user-emacs-directory (car startup--init-dir-file-cache)) ;; Look in each dir in load-path for a subdirs.el file. If we ;; find one, load it, which will add the appropriate subdirs of @@ -908,15 +940,10 @@ startup--setup-quote-display (when standard-display-table (aset standard-display-table char nil))))))) -(defun startup--load-user-init-file - (filename-function &optional alternate-filename-function load-defaults) - "Load a user init-file. -FILENAME-FUNCTION is called with no arguments and should return -the name of the init-file to load. If this file cannot be -loaded, and ALTERNATE-FILENAME-FUNCTION is non-nil, then it is -called with no arguments and should return the name of an -alternate init-file to load. If LOAD-DEFAULTS is non-nil, then -load default.el after the init-file. +(defun startup--load-user-init-file (dir file &optional load-defaults) + "Load a user init-file in directory DIR and named by FILE. +If LOAD-DEFAULTS is non-nil, then load default.el after the +init-file. This function sets `user-init-file' to the name of the loaded init-file, or to a default value if loading is not possible." @@ -929,26 +956,16 @@ startup--load-user-init-file (let ((debug-on-error debug-on-error-initial)) (condition-case-unless-debug error (when init-file-user - (let ((init-file-name (funcall filename-function))) - + (let ((init-file-name (expand-file-name file dir))) ;; If `user-init-file' is t, then `load' will store ;; the name of the file that it loads into ;; `user-init-file'. (setq user-init-file t) - (when init-file-name - (load (if (equal (file-name-extension init-file-name) - "el") - (file-name-sans-extension init-file-name) - init-file-name) - 'noerror 'nomessage)) - - (when (and (eq user-init-file t) alternate-filename-function) - (let ((alt-file (funcall alternate-filename-function))) - (and (equal (file-name-extension alt-file) "el") - (setq alt-file (file-name-sans-extension alt-file))) - (unless init-file-name - (setq init-file-name alt-file)) - (load alt-file 'noerror 'nomessage))) + (load (if (equal (file-name-extension init-file-name) + "el") + (file-name-sans-extension init-file-name) + init-file-name) + 'noerror 'nomessage) ;; If we did not find the user's init file, set ;; user-init-file conclusively. Don't let it be @@ -1006,7 +1023,6 @@ startup--load-user-init-file (defun command-line () "A subroutine of `normal-top-level'. Amongst another things, it parses the command-line arguments." - (let (xdg-dir startup-init-directory) (setq before-init-time (current-time) after-init-time nil command-line-default-directory default-directory) @@ -1196,28 +1212,25 @@ command-line :error)))) ;; Calculate the name of the Emacs init directory. - ;; This is typically ~INIT-FILE-USER/.config/emacs unless the user - ;; is following the ~INIT-FILE-USER/.emacs.d convention. - (setq xdg-dir startup--xdg-config-home-emacs) - (setq startup-init-directory - (if (or (zerop (length init-file-user)) - (and (eq xdg-dir user-emacs-directory) - (not (eq xdg-dir startup--xdg-config-default)))) - user-emacs-directory - ;; The name is not obvious, so access more directories to calculate it. - (setq xdg-dir (concat "~" init-file-user "/.config/emacs/")) - (startup--xdg-or-homedot xdg-dir init-file-user))) + ;; This is typically ~INIT-FILE-USER/.config/emacs/ if + ;; ~INIT-FILE-USER/.config exists, ~INIT-FILE-USER/.emacs.d/ otherwise. + (when (and (not (zerop (length init-file-user))) + (eq startup--xdg-config-home + startup--xdg-config-home-default)) + ;; It might differ for init-file-user; access more dirs to calculate it. + (setq startup--init-dir-file-cache + (let ((home-dir (concat "~" init-file-user))) + (startup--init-dir-file (concat home-dir "/.config") + home-dir)))) ;; Load the early init file, if found. (startup--load-user-init-file - (lambda () - (expand-file-name - ;; We use an explicit .el extension here to force - ;; startup--load-user-init-file to set user-init-file to "early-init.el", - ;; with the .el extension, if the file doesn't exist, not just - ;; "early-init" without an extension, as it does for ".emacs". - "early-init.el" - startup-init-directory))) + (car startup--init-dir-file-cache) + ;; We use an explicit .el extension here to force + ;; startup--load-user-init-file to set user-init-file to "early-init.el", + ;; with the .el extension, if the file doesn't exist, not just + ;; "early-init" without an extension, as it does for ".emacs". + "early-init.el") (setq early-init-file user-init-file) ;; If any package directory exists, initialize the package system. @@ -1353,30 +1366,8 @@ command-line ;; Load that user's init file, or the default one, or none. (startup--load-user-init-file - (lambda () - (cond - ((eq startup-init-directory xdg-dir) nil) - ((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 - ,(format-message - "`_emacs' init file is deprecated, please use `.emacs'")) - delayed-warnings-list) - "~/_emacs") - (t ;; But default to .emacs if _emacs does not exist. - "~/.emacs"))) - (lambda () - (expand-file-name - "init" - startup-init-directory)) + (car startup--init-dir-file-cache) + (cdr startup--init-dir-file-cache) (not inhibit-default-init)) (when (and deactivate-mark transient-mark-mode) @@ -1492,7 +1483,7 @@ command-line (if (and (boundp 'x-session-previous-id) (stringp x-session-previous-id)) (with-no-warnings - (emacs-session-restore x-session-previous-id))))) + (emacs-session-restore x-session-previous-id)))) (defun x-apply-session-resources () "Apply X resources which specify initial values for Emacs variables. -- 2.21.0