mu: Default to XDG Base Directory Specification

Instead of using ~/.mu, use the XDG Base Directory Specification, typically:
  ~/.cache/xapian
  ~/.cache/mu.log
  ~/.cache/parts
  ~/.config/bookmarks

Update dependencies, documentation.
This commit is contained in:
Dirk-Jan C. Binnema 2019-07-12 17:36:08 +03:00
parent 003d0a39b5
commit 632f383c38
11 changed files with 156 additions and 343 deletions

View File

@ -64,7 +64,6 @@ libmu_la_SOURCES= \
mu-bookmarks.h \
mu-contacts.cc \
mu-contacts.hh \
mu-contacts.h \
mu-container.c \
mu-container.h \
mu-date.c \
@ -98,7 +97,7 @@ libmu_la_SOURCES= \
mu-msg.h \
mu-query.cc \
mu-query.h \
mu-runtime.c \
mu-runtime.cc \
mu-runtime.h \
mu-script.c \
mu-script.h \
@ -125,7 +124,6 @@ libmu_la_LIBADD= \
libmu_la_LDFLAGS= \
$(ASAN_LDFLAGS)
EXTRA_DIST= \
mu-msg-crypto.c \
doxyfile.in

View File

@ -1,206 +0,0 @@
/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
**
** Copyright (C) 2010-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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, 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, write to the Free Software Foundation,
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**
*/
#include "mu-runtime.h"
#include <glib-object.h>
#include <locale.h> /* for setlocale() */
#include <stdio.h> /* for fileno() */
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include "mu-msg.h"
#include "mu-log.h"
#include "mu-util.h"
#define MU_XAPIAN_DIRNAME "xapian"
#define MU_BOOKMARKS_FILENAME "bookmarks"
#define MU_CACHE_DIRNAME "cache"
#define MU_CONTACTS_FILENAME "contacts"
#define MU_LOG_DIRNAME "log"
struct _MuRuntimeData {
gchar *_str[MU_RUNTIME_PATH_NUM];
gchar *_name; /* e.g., 'mu', 'mug' */
};
typedef struct _MuRuntimeData MuRuntimeData;
/* static, global data for this singleton */
static gboolean _initialized = FALSE;
static MuRuntimeData *_data = NULL;
static void runtime_free (void);
static gboolean init_paths (const char* muhome, MuRuntimeData *data);
static const char* runtime_path (MuRuntimePath path);
static gboolean
init_log (const char *muhome, const char *name, MuLogOptions opts)
{
gboolean rv;
char *logpath;
logpath = g_strdup_printf ("%s%c%s%c%s.log",
muhome, G_DIR_SEPARATOR,
MU_LOG_DIRNAME, G_DIR_SEPARATOR,
name);
rv = mu_log_init (logpath, opts);
g_free (logpath);
return rv;
}
gboolean
mu_runtime_init (const char* muhome_arg, const char *name)
{
gchar *muhome;
g_return_val_if_fail (!_initialized, FALSE);
g_return_val_if_fail (name, FALSE);
setlocale (LC_ALL, "");
if (muhome_arg)
muhome = g_strdup (muhome_arg);
else
muhome = mu_util_guess_mu_homedir ();
if (!mu_util_create_dir_maybe (muhome, 0700, TRUE)) {
g_printerr ("mu: invalid mu homedir specified;"
" use --muhome=<dir>\n");
runtime_free ();
return FALSE;
}
_data = g_new0 (MuRuntimeData, 1);
_data->_str[MU_RUNTIME_PATH_MUHOME] = muhome;
init_paths (muhome, _data);
_data->_name = g_strdup (name);
if (!init_log (muhome, name, MU_LOG_OPTIONS_BACKUP)) {
runtime_free ();
g_free (muhome);
return FALSE;
}
return _initialized = TRUE;
}
static void
runtime_free (void)
{
int i;
mu_log_uninit();
if (!_data)
return;
for (i = 0; i != MU_RUNTIME_PATH_NUM; ++i)
g_free (_data->_str[i]);
g_free (_data->_name);
g_free (_data);
}
void
mu_runtime_uninit (void)
{
if (!_initialized)
return;
runtime_free ();
_initialized = FALSE;
}
static gboolean
create_dirs_maybe (MuRuntimeData *data)
{
if (!mu_util_create_dir_maybe
(data->_str[MU_RUNTIME_PATH_CACHE], 0700, TRUE)) {
g_warning ("failed to create cache dir");
return FALSE;
}
if (!mu_util_create_dir_maybe
(data->_str[MU_RUNTIME_PATH_LOG], 0700, TRUE)) {
g_warning ("failed to create log dir");
return FALSE;
}
return TRUE;
}
static gboolean
init_paths (const char* muhome, MuRuntimeData *data)
{
data->_str [MU_RUNTIME_PATH_XAPIANDB] =
g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR,
MU_XAPIAN_DIRNAME);
data->_str [MU_RUNTIME_PATH_BOOKMARKS] =
g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR,
MU_BOOKMARKS_FILENAME);
data->_str [MU_RUNTIME_PATH_CACHE] =
g_strdup_printf ("%s%c%s", muhome, G_DIR_SEPARATOR,
MU_CACHE_DIRNAME);
data->_str [MU_RUNTIME_PATH_CONTACTS] =
g_strdup_printf ("%s%c%s", data->_str[MU_RUNTIME_PATH_CACHE],
G_DIR_SEPARATOR, MU_CONTACTS_FILENAME);
data->_str [MU_RUNTIME_PATH_LOG] =
g_strdup_printf ("%s%c%s", muhome,
G_DIR_SEPARATOR, MU_LOG_DIRNAME);
if (!create_dirs_maybe (data))
return FALSE;
return TRUE;
}
/* so we can called when _initialized is FALSE still */
static const char*
runtime_path (MuRuntimePath path)
{
return _data->_str[path];
}
const char*
mu_runtime_path (MuRuntimePath path)
{
g_return_val_if_fail (_initialized, NULL);
g_return_val_if_fail (path < MU_RUNTIME_PATH_NUM, NULL);
return runtime_path (path);
}

115
lib/mu-runtime.cc Normal file
View File

@ -0,0 +1,115 @@
/*
** Copyright (C) 2019 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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, 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, write to the Free Software Foundation,
** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
**
*/
#include "mu-runtime.h"
#include "mu-util.h"
#include <locale.h> /* for setlocale() */
#include <unordered_map>
static std::unordered_map<MuRuntimePath, std::string> RuntimePaths;
#include "mu-log.h"
constexpr auto PartsDir = "parts";
constexpr auto LogDir = "log";
constexpr auto XapianDir = "xapian";
constexpr auto Mu = "mu";
constexpr auto Bookmarks = "bookmarks";
static const std::string Sepa{G_DIR_SEPARATOR_S};
static void
init_paths_xdg ()
{
RuntimePaths.emplace(MU_RUNTIME_PATH_XAPIANDB, g_get_user_cache_dir() +
Sepa + Mu + Sepa + XapianDir);
RuntimePaths.emplace(MU_RUNTIME_PATH_MIMECACHE, g_get_user_cache_dir() +
Sepa + Mu + Sepa + PartsDir);
RuntimePaths.emplace(MU_RUNTIME_PATH_LOGDIR, g_get_user_cache_dir() +
Sepa + Mu);
RuntimePaths.emplace(MU_RUNTIME_PATH_BOOKMARKS, g_get_user_config_dir() +
Sepa + Mu);
}
static void
init_paths_muhome (const char *muhome)
{
RuntimePaths.emplace(MU_RUNTIME_PATH_XAPIANDB, muhome + Sepa + XapianDir);
RuntimePaths.emplace(MU_RUNTIME_PATH_MIMECACHE, muhome + Sepa + PartsDir);
RuntimePaths.emplace(MU_RUNTIME_PATH_LOGDIR, muhome + Sepa + LogDir);
RuntimePaths.emplace(MU_RUNTIME_PATH_BOOKMARKS, muhome + Sepa + Bookmarks);
}
gboolean
mu_runtime_init (const char* muhome, const char *name)
{
g_return_val_if_fail (RuntimePaths.empty(), FALSE);
g_return_val_if_fail (name, FALSE);
setlocale (LC_ALL, "");
if (muhome)
init_paths_muhome (muhome);
else
init_paths_xdg();
for (const auto& d: RuntimePaths ) {
char* dir;
if (d.first == MU_RUNTIME_PATH_BOOKMARKS) // special case
dir = g_path_get_dirname (d.second.c_str());
else
dir = g_strdup (d.second.c_str());
auto ok = mu_util_create_dir_maybe (dir, 0700, TRUE);
if (!ok) {
g_critical ("failed to create %s", dir);
g_free (dir);
mu_runtime_uninit();
return FALSE;
}
g_free (dir);
}
const auto log_path = RuntimePaths[MU_RUNTIME_PATH_LOGDIR] +
Sepa + name + ".log";
if (!mu_log_init (log_path.c_str(), MU_LOG_OPTIONS_BACKUP)) {
mu_runtime_uninit();
return FALSE;
}
return TRUE;
}
void
mu_runtime_uninit (void)
{
RuntimePaths.clear();
mu_log_uninit();
}
const char*
mu_runtime_path (MuRuntimePath path)
{
const auto it = RuntimePaths.find (path);
if (it == RuntimePaths.end())
return NULL;
else
return it->second.c_str();
}

View File

@ -1,6 +1,6 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
**
** Copyright (C) 2012-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
** Copyright (C) 2012-2019 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
**
** 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
@ -37,24 +37,6 @@ G_BEGIN_DECLS
*/
gboolean mu_runtime_init (const char *muhome, const char *name);
/**
* initialize the mu runtime system with comand line argument; this
* will parse the command line assuming the parameters of the 'mu'
* program. Initializes logging and other systems. To uninitialize,
* use mu_runtime_uninit
*
* @param ptr to the param count (typically, argc)
* @param ptr to the params (typically, argv)
* @param name of the main program, ie. 'mu', 'mug' or
* 'procmule'. this influences the name of the e.g. the logfile
*
* @return TRUE if succeeded, FALSE in case of error
*/
gboolean mu_runtime_init_from_cmdline (int *pargc, char ***pargv,
const char *name);
/**
* free all resources
*
@ -62,17 +44,14 @@ gboolean mu_runtime_init_from_cmdline (int *pargc, char ***pargv,
void mu_runtime_uninit (void);
enum _MuRuntimePath {
MU_RUNTIME_PATH_MUHOME, /* mu home path */
typedef enum {
MU_RUNTIME_PATH_XAPIANDB, /* mu xapian db path */
MU_RUNTIME_PATH_BOOKMARKS, /* mu bookmarks file path */
MU_RUNTIME_PATH_CACHE, /* mu cache path */
MU_RUNTIME_PATH_LOG, /* mu path for log files */
MU_RUNTIME_PATH_CONTACTS, /* mu path to the contacts cache */
MU_RUNTIME_PATH_MIMECACHE, /* mu cache path for attachments etc. */
MU_RUNTIME_PATH_LOGDIR, /* mu path for log files */
MU_RUNTIME_PATH_NUM
};
typedef enum _MuRuntimePath MuRuntimePath;
} MuRuntimePath;
/**
* get a file system path to some 'special' file or directory

View File

@ -194,32 +194,6 @@ mu_util_guess_maildir (void)
return NULL;
}
gchar*
mu_util_guess_mu_homedir (void)
{
const char* home;
const gchar *hdir1;
/* first, try MU_HOME */
hdir1 = g_getenv ("MU_HOME");
if (hdir1 && mu_util_check_dir (hdir1, TRUE, FALSE))
return g_strdup (hdir1);
/* then, g_get_home_dir use /etc/passwd, not $HOME; this is
* better, as HOME may be wrong when using 'sudo' etc.*/
home = g_get_home_dir ();
if (!home) {
MU_WRITE_LOG ("failed to determine homedir");
return NULL;
}
return g_strdup_printf ("%s%c%s", home ? home : ".",
G_DIR_SEPARATOR, ".mu");
}
gboolean
mu_util_create_dir_maybe (const gchar *path, mode_t mode, gboolean nowarn)
{

View File

@ -60,19 +60,6 @@ char* mu_util_dir_expand (const char* path)
char* mu_util_guess_maildir (void)
G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
/**
* guess the place of the mu homedir; first try $MU_HOME; if it is
* unset or non-existant, try ~/.mu. Note, the fallback ~/.mu
* directory does not necessarily exist. mu_util_check_dir can be used
* to check that
*
* @return the guessed mu homedir, which needs to be freed with g_free
* when no longer needed.
*/
gchar* mu_util_guess_mu_homedir (void)
G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
/**
* if path exists, check that's a read/writeable dir; otherwise try to
* create it (with perms 0700)

View File

@ -1,17 +1,18 @@
.TH MU-BOOKMARKS 5 "May 2011" "User Manuals"
.TH MU-BOOKMARKS 5 "July 2019" "User Manuals"
.SH NAME
.SH NAME
bookmarks \- file with bookmarks (shortcuts) for mu search expressions
.SH DESCRIPTION
Bookmarks are named shortcuts for search queries. They allow using a
convenient name for often-used queries. The bookmarks are also visible as
shortcuts in the mu experimental user interfaces, \fImug\fR and \fImug2\fR.
Bookmarks are named shortcuts for search queries. They allow using a convenient
name for often-used queries. The bookmarks are also visible as shortcuts in the
mu experimental user interfaces, \fImug\fR and \fImug2\fR.
\fBmu\fR supports bookmarks stored in a file called \fBbookmarks\fR in the mu
home directory (typically, this would be \fI~/.mu/bookmarks\fR).
The bookmarks file is read from \fI<muhome>/bookmarks\fR. On Unix this would
typically be w be \fI~/.config/mu/bookmarks\fR, but this can be influenced using
the \fB\-\-muhome\fR parameter for \fBmu-find\fR(1) and \fBmug\fR(1).
The bookmarks file is a typical key=value \fB.ini\fR-file, which is best shown
by means of an example:
@ -26,12 +27,6 @@ The \fB[mu]\fR group header is required.
For practical uses of bookmarks, see \fBmu-find\fR(1).
.SH LOCATION
The bookmarks file is read from \fI<muhome>/bookmarks\fR. Typically, this
would be \fI~/.mu/bookmarks\fR, but this can be influenced using the
\fB\-\-muhome\fR parameter for \fBmu-find\fR(1) and \fBmug\fR(1).
.SH AUTHOR
Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>

View File

@ -1,4 +1,4 @@
.TH MU 1 "September 2018" "User Manuals"
.TH MU 1 "July 2019" "User Manuals"
.SH NAME
@ -120,10 +120,10 @@ including \fBmu\fR without any command.
.TP
\fB\-\-muhome\fR
causes \fBmu\fR to use an alternative directory to
store and read its database and logs. By default, \fBmu\fR uses
whatever the \fBMU_HOME\fR environment variable is set to; if it is
not set, \fI~/.mu\fR is used.
use an alternative directory to store and read the database, write the logs,
etc. By default, \fBmu\fR uses XDG Base Directory Specification (e.g. on Linux
by default \fI~/.cache/mu\fR, \fI~/.config/mu\fR). Earlier versions of \fBmu\fR defaulted
to \fI~/.mu\fR, which now requires \fI\-\-muhome=~/.mu\fR.
.TP
\fB\-d\fR, \fB\-\-debug\fR
@ -157,40 +157,7 @@ lists the various command line options.
.SH ERROR CODES
The various mu subcommands typically exit with 0 (zero) upon success, and
non-zero when some error occurred. The table lists the various error codes.
.nf
exit code | error
----------+-------------------------------------------
1 | MU_ERROR
2 | MU_ERROR_IN_PARAMETERS
3 | MU_ERROR_INTERNAL
4 | MU_ERROR_NO_MATCHES
|
11 | MU_ERROR_XAPIAN
|
13 | MU_ERROR_XAPIAN_QUERY
14 | MU_ERROR_XAPIAN_DIR_NOT_ACCESSIBLE
15 | MU_ERROR_XAPIAN_NOT_UP_TO_DATE
16 | MU_ERROR_XAPIAN_MISSING_DATA
17 | MU_ERROR_XAPIAN_CORRUPTION
18 | MU_ERROR_XAPIAN_CANNOT_GET_WRITELOCK
30 | MU_ERROR_GMIME
|
50 | MU_ERROR_CONTACTS
51 | MU_ERROR_CONTACTS_CANNOT_RETRIEVE
|
70 | MU_ERROR_FILE
71 | MU_ERROR_FILE_INVALID_NAME
72 | MU_ERROR_FILE_CANNOT_LINK
73 | MU_ERROR_FILE_CANNOT_OPEN
74 | MU_ERROR_FILE_CANNOT_READ
75 | MU_ERROR_FILE_CANNOT_CREATE
76 | MU_ERROR_FILE_CANNOT_MKDIR
77 | MU_ERROR_FILE_STAT_FAILED
78 | MU_ERROR_FILE_READDIR_FAILED
79 | MU_ERROR_FILE_INVALID_SOURCE
.fi
non-zero when some error occurred.
.SH BUGS
@ -204,3 +171,4 @@ Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
.SH "SEE ALSO"
.BR mu-index (1), mu-find (1), mu-cfind (1), mu-mkdir (1), mu-view (1),
.BR mu-extract (1), mu-easy (1), mu-bookmarks (5), mu-query (7)
.BR https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html

View File

@ -69,15 +69,16 @@ get_output_format (const char *formatstr)
static void
set_group_mu_defaults (void)
{
gchar *exp;
/* If muhome is not set, we use the XDG Base Directory Specification
* locations. */
if (!MU_CONFIG.muhome)
MU_CONFIG.muhome = mu_util_guess_mu_homedir();
exp = mu_util_dir_expand(MU_CONFIG.muhome);
if (exp) {
g_free(MU_CONFIG.muhome);
MU_CONFIG.muhome = exp;
if (MU_CONFIG.muhome) {
gchar *exp;
exp = mu_util_dir_expand(MU_CONFIG.muhome);
if (exp) {
g_free(MU_CONFIG.muhome);
MU_CONFIG.muhome = exp;
}
}
/* check for the MU_NOCOLOR or NO_COLOR env vars; but in any case don't

View File

@ -34,7 +34,8 @@
:group 'mail)
(defcustom mu4e-mu-home nil
"Location of the mu homedir, or nil for the default."
"Location of an alternate mu home dir. If not set, use the
defaults, based on the XDG Base Directory Specification."
:group 'mu4e
:type '(choice (const :tag "Default location" nil)
(directory :tag "Specify location"))

View File

@ -12,7 +12,7 @@
@c %**end of header
@copying
Copyright @copyright{} 2012-2018 Dirk-Jan C. Binnema
Copyright @copyright{} 2012-2019 Dirk-Jan C. Binnema
@quotation
Permission is granted to copy, distribute and/or modify this document
@ -2637,8 +2637,9 @@ invoke those function even in that case.
the current context is also visible in the mode-line when in
headers, view or main mode.
@item You can set any kind of variable; including settings for mail servers etc.
However, settings such as @code{mu4e-maildir} and @code{mu4e-mu-home} are
not changeable after they have been set without quitting @t{mu4e} first.
However, settings such as @code{mu4e-maildir} and @code{mu4e-mu-home}
are not changeable after they have been set without quitting @t{mu4e}
first.
@item @code{leave-func} (if defined) for the context we are leaving, is invoked
before the @code{enter-func} (if defined) of the
context we are entering.
@ -3917,7 +3918,7 @@ Use @kbd{M-x mu4e-update-index}
errors like:}
@verbatim
mu: mu_store_new_writable: xapian error
'Unable to get write lock on ~/.mu/xapian: already locked
'Unable to get write lock on ~/.cache/mu/xapian: already locked
@end verbatim
@emph{What to do about this?} You get this error because the underlying
Xapian database is locked by some other process; it can be opened only once in
@ -4736,10 +4737,10 @@ log-buffer is called @t{*mu4e-log*}, and in the @ref{Main view}, @ref{Headers
view} and @ref{Message view}, there's a keybinding @key{$} that takes you
there. You can quit it by pressing @key{q}.
Logging can be a bit resource-intensive, so you may not want to leave it on
all the time. By default, the log only maintains the most recent 1200
lines. @t{mu} itself keeps a log as well, you can find this it in
@t{<MUHOME>/log/mu.log}, typically @t{~/.mu/log/mu.log}.
Logging can be a bit resource-intensive, so you may not want to leave
it on all the time. By default, the log only maintains the most recent
1200 lines. @t{mu} itself keeps a log as well, you can find it in
@t{<MUHOME>/mu.log}, on Unix typically @t{~/.cache/mu/mu.log}.
@node GNU Free Documentation License
@appendix GNU Free Documentation License