prefer XDG paths, but fall back to old paths

Use "~/.racket" for 'pref-dir if it exists and "~/.config/racket"
doesn't, for example.

Based on discussion at #3268, especially with Ryan.
This commit is contained in:
Matthew Flatt 2020-08-28 04:39:30 -06:00
parent f52bff8a31
commit a22550a963
5 changed files with 582 additions and 378 deletions

View File

@ -52,24 +52,33 @@ by @racket[kind], which must be one of the following:
the current executable is used as the home directory.}
@item{@indexed-racket['pref-dir] --- the standard directory for
storing the current user's preferences. On Unix, the directory is the
@filepath{racket} subdirectory of the path specified by
storing the current user's preferences. The preferences directory
might not exist.
On Unix, the preferences directory is normally the @filepath{racket}
subdirectory of the path specified by
@indexed-envvar{XDG_CONFIG_HOME}, or @filepath{.config/racket} in the
@tech{user's home directory} if @envvar{XDG_CONFIG_HOME} is not set to
an absolute path. On Windows,
it is @filepath{Racket} in the @tech{user's home directory} if
determined by @envvar{PLTUSERHOME}, otherwise in the user's
application-data folder as specified by the Windows registry; the
application-data folder is usually @filepath{Application Data} in the
user's profile directory. On Mac OS, the preferences directory
is @filepath{Library/Preferences} in the
@tech{user's home directory}. The preferences directory might not exist.}
@tech{user's home directory} if @envvar{XDG_CONFIG_HOME} is not set
to an absolute path or if @envvar{PLTUSERHOME} is set. Either way, if
that directory does not exist but a @filepath{.racket} directory
exists in the @tech{user's home directory}, then that directory is
the preference directory, instead.
On Windows, the preferences directory is @filepath{Racket} in the
@tech{user's home directory} if determined by @envvar{PLTUSERHOME},
otherwise in the user's application-data folder as specified by the
Windows registry; the application-data folder is usually
@filepath{Application Data} in the user's profile directory.
On Mac OS, the preferences directory is
@filepath{Library/Preferences} in the @tech{user's home directory}.}
@item{@indexed-racket['pref-file] --- a file that contains a
symbol-keyed association list of preference values. The file's
directory path always matches the result returned for
@racket['pref-dir]. The file name is @filepath{racket-prefs.rktd} on Unix
and Windows, and it is @filepath{org.racket-lang.prefs.rktd} on Mac OS. The file's directory might not exist. See also
and Windows, and it is @filepath{org.racket-lang.prefs.rktd} on Mac OS.
The file's directory might not exist. See also
@racket[get-preference].}
@item{@indexed-racket['temp-dir] --- the standard directory for
@ -82,22 +91,34 @@ by @racket[kind], which must be one of the following:
if it is defined, otherwise it is the current directory.}
@item{@indexed-racket['init-dir] --- the directory containing the
initialization file used by the Racket executable. On Unix, it is
the same as the result returned for @racket['pref-dir]; on Mac OS and
Windows, it is the same as the @tech{user's home directory}.}
initialization file used by the Racket executable.
On Unix, the initialization directory is the same as the result
returned for @racket['pref-dir]---unless that directory does not
exist and a @filepath{.racketrc} file exists in the @tech{user's home
directory}, in which case the home directory is the initialization
directory.
On Windows, the initialization directory is the same as the
@tech{user's home directory}.
On Mac OS, the initialization directory is @filepath{Library/Racket}
in the @tech{user's home directory}---unless no
@filepath{racketrc.rktl} exists there and a @filepath{.racketrc} file
does exist in the home directory, in which case the home directory is
the initialization directory.}
@item{@indexed-racket['init-file] --- the file loaded at start-up by
the Racket executable. The directory part of the
path is the same path as returned for @racket['init-dir]. The file
name is platform-specific:
path is the same path as returned for @racket['init-dir].
@itemize[
On Windows, the file part of the name is
@indexed-file{racketrc.rktl}.
@item{Unix and Windows: @indexed-file{racketrc.rktl}}
@item{Mac OS: @indexed-file{.racketrc}}
]}
On Unix and Mac OS, the file part of the name is
@indexed-file{racketrc.rktl}---unless the path returned for
@racket['init-dir] is the @tech{user's home directory}, in which case
the file part of the name is @indexed-file{.racketrc}.}
@item{@indexed-racket['config-dir] --- a directory for
the installation's configuration. This directory is specified by the
@ -123,21 +144,39 @@ by @racket[kind], which must be one of the following:
@indexed-envvar{PLTADDONDIR} environment variable, and it can be
overridden by the @DFlag{addon} or @Flag{A} command-line flag. If no
environment variable or flag is specified, or if the value is not a
legal path name, then this directory defaults to
@filepath{Library/Racket} in the @tech{user's home directory} on Mac
OS and @racket['pref-dir] on Windows. On Unix, it is the
@filepath{racket} subdirectory of the path specified by
@indexed-envvar{XDG_DATA_HOME}, or @filepath{.local/share/racket} in
the @tech{user's home directory} if @envvar{XDG_CONFIG_HOME} is not
set to an absolute path. The directory might not exist.}
legal path name, then this directory defaults to a platform-specific
locations. The directory might not exist.
On Unix, the default is normally the @filepath{racket} subdirectory
of the path specified by @indexed-envvar{XDG_DATA_HOME}, or
@filepath{.local/share/racket} in the @tech{user's home directory} if
@envvar{XDG_CONFIG_HOME} is not set to an absolute path or if
@envvar{PLTUSERHOME} is set. If that directory does not exists but a
@filepath{.racket} directory exists in the user's home directory,
that the @filepath{.racket} directory path is the default, instead.
On Windows, the default is the same as the @racket['pref-dir] directory.
On Mac OS, the default is @filepath{Library/Racket} within the
@tech{user's home directory}.}
@item{@indexed-racket['cache-dir] --- a directory for storing
user-specific caches. On Unix, it is the @filepath{racket}
subdirectory of the path specified by @indexed-envvar{XDG_CACHE_HOME},
or @filepath{.cache/racket} in the @tech{user's home directory} if
@envvar{XDG_CACHE_HOME} is not set to an absolute path. On Mac OS and
Windows, it is the same as the result returned for @racket['addon-dir].
The directory might not exists.}
user-specific caches. The directory might not exist.
On Unix, the cache directory is normally the @filepath{racket}
subdirectory of the path specified by
@indexed-envvar{XDG_CACHE_HOME}, or @filepath{.cache/racket} in the
@tech{user's home directory} if @envvar{XDG_CACHE_HOME} is not set to
an absolute path or if @envvar{PLTUSERHOME} is set. If that directory
does not exist but a @filepath{.racket} directory exists in the home
directory, then the @filepath{.racket} directory is the cache
directory, instead.
On Windows, the cache directory is the same as the result returned
for @racket['addon-dir].
On Mac OS, the cache directory is @filepath{Library/Caches/Racket}
within the @tech{user's home directory}.}
@item{@indexed-racket['doc-dir] --- the standard directory for
storing the current user's documents. On Unix, it's
@ -206,7 +245,11 @@ by @racket[kind], which must be one of the following:
@history[#:changed "6.0.0.3" @elem{Added @envvar{PLTUSERHOME}.}
#:changed "6.9.0.1" @elem{Added @racket['host-config-dir]
and @racket['host-collects-dir].}]}
and @racket['host-collects-dir].}
#:changed "7.8.0.9" @elem{Added @racket['cache-dir], and changed
to use XDG directories as preferred on Unix
with the previous paths as a fallback, and
with similar adjustments for Mac OS.}]}
@defproc[(path-list-string->path-list [str (or/c string? bytes?)]
[default-path-list (listof path?)])

View File

@ -1842,6 +1842,123 @@
(current-directory original-dir)
;; ----------------------------------------
(let ([home-dir (path->directory-path (make-temporary-file "test-home~a" 'directory))]
[env (environment-variables-copy (current-environment-variables))]
[racket (find-exe)])
(environment-variables-set! env
#"PLTUSERHOME"
(path->bytes home-dir))
(define (get-dirs)
(parameterize ([current-environment-variables env])
(define-values (s i o e) (subprocess #f #f #f racket "-I" "racket/base" "-e"
(string-append
"(map path->bytes "
" (list (find-system-path 'home-dir)"
" (find-system-path 'pref-dir)"
" (find-system-path 'pref-file)"
" (find-system-path 'init-dir)"
" (find-system-path 'init-file)"
" (find-system-path 'addon-dir)"
" (find-system-path 'cache-dir)))")))
(begin0
(cadr (read i))
(subprocess-wait s))))
(define (touch f) (close-output-port (open-output-file f #:exists 'truncate)))
(define dir-syms '(home-dir pref-dir pref-file init-dir init-file addon-dir cache-dir))
(define expected-default-dirs
(case (system-type)
[(unix) (list home-dir
(build-path home-dir ".config" "racket/")
(build-path home-dir ".config" "racket" "racket-prefs.rktd")
(build-path home-dir ".config" "racket/")
(build-path home-dir ".config" "racket" "racketrc.rktl")
(build-path home-dir ".local" "share" "racket/")
(build-path home-dir ".cache" "racket/"))]
[(macosx) (list home-dir
(build-path home-dir "Library" "Preferences/")
(build-path home-dir "Library" "Preferences" "org.racket-lang.prefs.rktd")
(build-path home-dir "Library" "Racket/")
(build-path home-dir "Library" "Racket" "racketrc.rktl")
(build-path home-dir "Library" "Racket/")
(build-path home-dir "Library" "Caches" "Racket/"))]
[(windows) (list home-dir
(build-path home-dir "Racket\\")
(build-path home-dir "Racket" "racket-prefs.rktd")
home-dir
(build-path home-dir "racketrc.rktl")
(build-path home-dir "Racket\\")
(build-path home-dir "Racket\\"))]
[else (error "unexpected system type")]))
(define default-dirs (get-dirs))
(for-each (lambda (name expect got)
(test got `(,name default) expect))
dir-syms
(map bytes->path default-dirs)
expected-default-dirs)
;; Create files/directories that trigger legacy paths:
(case (system-type)
[(unix)
(touch (build-path home-dir ".racketrc"))
(make-directory (build-path home-dir ".racket"))]
[(macosx)
(touch (build-path home-dir ".racketrc"))
;; Make sure just the existence of the would-be init dir doesn't override legacy:
(make-directory (build-path home-dir "Library"))
(make-directory (build-path home-dir "Library" "Racket"))])
(define legacy-dirs (get-dirs))
(for-each (lambda (name expect got)
(test got `(,name legacy) expect))
dir-syms
(map bytes->path legacy-dirs)
(case (system-type)
[(unix) (list home-dir
(build-path home-dir ".racket/")
(build-path home-dir ".racket/" "racket-prefs.rktd")
home-dir
(build-path home-dir ".racketrc")
(build-path home-dir ".racket/")
(build-path home-dir ".racket/"))]
[(macosx) (list home-dir
(build-path home-dir "Library" "Preferences/")
(build-path home-dir "Library" "Preferences" "org.racket-lang.prefs.rktd")
home-dir
(build-path home-dir ".racketrc")
(build-path home-dir "Library" "Racket/")
(build-path home-dir "Library" "Caches" "Racket/"))]
[(windows) expected-default-dirs]
[else (error "unexpected system type")]))
;; Create files/directories that cause legacy paths to be ignored:
(case (system-type)
[(unix)
(make-directory (build-path home-dir ".config"))
(make-directory (build-path home-dir ".config" "racket"))
(touch (build-path home-dir ".config" "racket" "racketrc.rktl"))
(make-directory (build-path home-dir ".local"))
(make-directory (build-path home-dir ".local" "share"))
(make-directory (build-path home-dir ".local" "share" "racket"))
(make-directory (build-path home-dir ".cache"))
(make-directory (build-path home-dir ".cache" "racket"))]
[(macosx)
(touch (build-path (build-path home-dir "Library" "Racket" "racketrc.rktl")))])
(define back-to-default-dirs (get-dirs))
(for-each (lambda (name expect got)
(test got `(,name back-to-default) expect))
dir-syms
(map bytes->path back-to-default-dirs)
expected-default-dirs)
(delete-directory/files home-dir))
;; ----------------------------------------
(unless (eq? 'windows (system-type))
(define can-open-nonblocking-fifo?
;; The general implementation of fifo-write ports requires

View File

@ -7,7 +7,8 @@
(define exe-id
(delay
(if (equal? (path->bytes (cross-system-library-subpath #f)) #"x86_64-macosx")
(if (member (path->bytes (cross-system-library-subpath #f))
(list #"x86_64-macosx" #"x86_64-darwin"))
#xFeedFacf
#xFeedFace)))

File diff suppressed because it is too large Load Diff

View File

@ -1914,6 +1914,20 @@ static char *append_paths(char *a, char *b, int free_a, int free_b)
return s;
}
#ifdef RKTIO_SYSTEM_UNIX
static int directory_or_file_exists(rktio_t *rktio, char *dir, char *maybe_file)
{
if (maybe_file) {
int r;
char *path = append_paths(dir, maybe_file, 0, 0);
r = rktio_file_exists(rktio, path);
free(path);
return r;
} else
return rktio_directory_exists(rktio, dir);
}
#endif
char *rktio_system_path(rktio_t *rktio, int which)
{
#ifdef RKTIO_SYSTEM_UNIX
@ -1943,10 +1957,13 @@ char *rktio_system_path(rktio_t *rktio, int which)
return rktio_get_current_directory(rktio);
}
#define USE_XDG_BASEDIR 1
{
/* Everything else is in ~: */
char *home_str, *alt_home, *home;
char *home_str, *alt_home, *home, *prefer_home_str = NULL, *prefer_home;
char *home_file = NULL, *prefer_home_file = NULL;
int free_prefer_home_str = 0;
alt_home = rktio_getenv(rktio, "PLTUSERHOME");
if ((which == RKTIO_PATH_PREF_DIR)
|| (which == RKTIO_PATH_PREF_FILE)
@ -1954,53 +1971,52 @@ char *rktio_system_path(rktio_t *rktio, int which)
|| (which == RKTIO_PATH_CACHE_DIR)
|| (which == RKTIO_PATH_INIT_DIR)
|| (which == RKTIO_PATH_INIT_FILE)) {
#if defined(OS_X) && !defined(XONX)
if ((which == RKTIO_PATH_ADDON_DIR)
|| (which == RKTIO_PATH_CACHE_DIR))
#if defined(OS_X) && !defined(RACKET_XONX) && !defined(XONX)
if (which == RKTIO_PATH_ADDON_DIR)
home_str = "~/Library/Racket/";
else if (which == RKTIO_PATH_CACHE_DIR)
home_str = "~/Library/Caches/Racket/";
else if ((which == RKTIO_PATH_INIT_DIR)
|| (which == RKTIO_PATH_INIT_FILE))
|| (which == RKTIO_PATH_INIT_FILE)) {
prefer_home_str = "~/Library/Racket/";
prefer_home_file = "racketrc.rktl";
home_str = "~/";
else
home_file = ".racketrc";
} else
home_str = "~/Library/Preferences/";
#elif USE_XDG_BASEDIR
char *envvar, *xdg_dir, *suffix;
#else
char *envvar, *xdg_dir;
if (which == RKTIO_PATH_ADDON_DIR) {
home_str = "~/.local/share/racket/";
prefer_home_str = "~/.local/share/racket/";
envvar = "XDG_DATA_HOME";
suffix = "racket/";
} else if (which == RKTIO_PATH_CACHE_DIR) {
home_str = "~/.cache/racket/";
prefer_home_str = "~/.cache/racket/";
envvar = "XDG_CACHE_HOME";
suffix = "racket/";
} else {
home_str = "~/.config/racket/";
prefer_home_str = "~/.config/racket/";
envvar = "XDG_CONFIG_HOME";
if ((which == RKTIO_PATH_PREF_DIR)
|| (which == RKTIO_PATH_INIT_DIR)) {
suffix = "racket/";
} else if (which == RKTIO_PATH_PREF_FILE) {
suffix = "racket/racket-prefs.rktd";
} else { /* (which == RKTIO_PATH_INIT_FILE) */
suffix = "racket/racketrc.rktl";
}
}
xdg_dir = rktio_getenv(rktio, envvar);
if (alt_home)
xdg_dir = NULL;
else
xdg_dir = rktio_getenv(rktio, envvar);
/* xdg_dir is invalid if it is not an absolute path */
if (xdg_dir && (strlen(xdg_dir) > 0) && (xdg_dir[0] == '/')) {
return append_paths(xdg_dir, suffix, 1, 0);
prefer_home_str = append_paths(xdg_dir, "racket/", 1, 0);
free_prefer_home_str = 1;
} else {
free(xdg_dir);
if (xdg_dir) free(xdg_dir);
}
#else
if ((which == RKTIO_PATH_INIT_DIR) || (which == RKTIO_INIT_FILE)) {
if ((which == RKTIO_PATH_INIT_DIR) || (which == RKTIO_PATH_INIT_FILE)) {
home_str = "~/";
} else { /* RKTIO_PATH_{ADDON_DIR,PREF_DIR,PREF_FILE} */
home_file = ".racketrc";
} else { /* RKTIO_PATH_{ADDON_DIR,PREF_DIR,PREF_FILE,CACHE_DIR} */
home_str = "~/.racket/";
}
#endif
#endif
} else {
#if defined(OS_X) && !defined(XONX)
#if defined(OS_X) && !defined(RACKET_XONX) && !defined(XONX)
if (which == RKTIO_PATH_DESK_DIR)
home_str = "~/Desktop/";
else if (which == RKTIO_PATH_DOC_DIR)
@ -2010,20 +2026,43 @@ char *rktio_system_path(rktio_t *rktio, int which)
home_str = "~/";
}
alt_home = rktio_getenv(rktio, "PLTUSERHOME");
if (alt_home)
home = append_paths(alt_home, home_str + 2, 1, 0);
else {
home = rktio_expand_user_tilde(rktio, home_str);
if (!home) {
/* Something went wrong with the user lookup. Just drop "~'. */
int h_len = strlen(home_str);
home = (char *)malloc(h_len - 2 + 1);
strcpy(home, home_str+2);
/* If `prefer_home_str` is non-NULL, it must be `malloc`ed */
if (prefer_home_str) {
if (alt_home)
prefer_home = append_paths(alt_home, prefer_home_str + 2, 0, 0);
else
prefer_home = rktio_expand_user_tilde(rktio, prefer_home_str);
if (free_prefer_home_str)
free(prefer_home_str);
if (directory_or_file_exists(rktio, prefer_home, prefer_home_file))
home_str = NULL;
} else
prefer_home = NULL;
if (home_str) {
if (alt_home)
home = append_paths(alt_home, home_str + 2, 1, 0);
else
home = rktio_expand_user_tilde(rktio, home_str);
if (prefer_home) {
if (!directory_or_file_exists(rktio, home, home_file)) {
free(home);
home = prefer_home;
} else {
free(prefer_home);
prefer_home = NULL;
}
}
}
} else
home = prefer_home;
/* At this point, we're using `home`, but `prefer_home` can still
be non-NULL and equal to `home` to mean that we should use
XDG-style file names. */
if ((which == RKTIO_PATH_PREF_DIR) || (which == RKTIO_PATH_INIT_DIR)
|| (which == RKTIO_PATH_HOME_DIR) || (which == RKTIO_PATH_ADDON_DIR)
|| (which == RKTIO_PATH_DESK_DIR) || (which == RKTIO_PATH_DOC_DIR)
@ -2031,18 +2070,16 @@ char *rktio_system_path(rktio_t *rktio, int which)
return home;
if (which == RKTIO_PATH_INIT_FILE) {
#if defined(OS_X) && !defined(XONX)
return append_paths(home, ".racketrc", 1, 0);
#elif USE_XDG_BASEDIR
return append_paths(home, "racketrc.rktl", 1, 0);
#else
return append_paths(home, ".racketrc", 1, 0);
#endif
if (prefer_home)
return append_paths(prefer_home, "racketrc.rktl", 1, 0);
else
return append_paths(home, ".racketrc", 1, 0);
}
if (which == RKTIO_PATH_PREF_FILE) {
#if defined(OS_X) && !defined(XONX)
#if defined(OS_X) && !defined(RACKET_XONX) && !defined(XONX)
return append_paths(home, "org.racket-lang.prefs.rktd", 1, 0);
#else
#else
return append_paths(home, "racket-prefs.rktd", 1, 0);
#endif
} else {