re-interpret relative local paths in config file
this makes config+data file "sets" relocatable, which is useful for testing. this is technically a gratuitous backwards incompatible behavior change, but to the degree that anyone uses relative paths at all, they almost certainly rely on PWD being set up such that they won't see a difference.
This commit is contained in:
parent
cf13630a00
commit
d3f118be79
3
NEWS
3
NEWS
|
@ -3,6 +3,9 @@
|
||||||
Changed default config & state locations to follow the XDG basedir spec.
|
Changed default config & state locations to follow the XDG basedir spec.
|
||||||
The old locations remain supported.
|
The old locations remain supported.
|
||||||
|
|
||||||
|
The reference point for relative local paths in the configuration file
|
||||||
|
is now the file's containing directory.
|
||||||
|
|
||||||
[1.4.0]
|
[1.4.0]
|
||||||
|
|
||||||
The 'isync' compatibility wrapper was removed.
|
The 'isync' compatibility wrapper was removed.
|
||||||
|
|
25
src/config.c
25
src/config.c
|
@ -23,7 +23,7 @@ char FieldDelimiter = ':';
|
||||||
static store_conf_t *stores;
|
static store_conf_t *stores;
|
||||||
|
|
||||||
char *
|
char *
|
||||||
expand_strdup( const char *s )
|
expand_strdup( const char *s, const conffile_t *cfile )
|
||||||
{
|
{
|
||||||
struct passwd *pw;
|
struct passwd *pw;
|
||||||
const char *p, *q;
|
const char *p, *q;
|
||||||
|
@ -51,6 +51,9 @@ expand_strdup( const char *s )
|
||||||
}
|
}
|
||||||
nfasprintf( &r, "%s%s", q, p ? p : "" );
|
nfasprintf( &r, "%s%s", q, p ? p : "" );
|
||||||
return r;
|
return r;
|
||||||
|
} else if (*s != '/') {
|
||||||
|
nfasprintf( &r, "%.*s%s", cfile->path_len, cfile->file, s );
|
||||||
|
return r;
|
||||||
} else {
|
} else {
|
||||||
return nfstrdup( s );
|
return nfstrdup( s );
|
||||||
}
|
}
|
||||||
|
@ -220,7 +223,7 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
|
||||||
} while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
|
} while ((arg = get_arg( cfile, ARG_OPTIONAL, NULL )));
|
||||||
conf->ops[F] |= XOP_HAVE_TYPE;
|
conf->ops[F] |= XOP_HAVE_TYPE;
|
||||||
} else if (!strcasecmp( "SyncState", cfile->cmd )) {
|
} else if (!strcasecmp( "SyncState", cfile->cmd )) {
|
||||||
conf->sync_state = expand_strdup( cfile->val );
|
conf->sync_state = !strcmp( cfile->val, "*" ) ? "*" : expand_strdup( cfile->val, cfile );
|
||||||
} else if (!strcasecmp( "CopyArrivalDate", cfile->cmd )) {
|
} else if (!strcasecmp( "CopyArrivalDate", cfile->cmd )) {
|
||||||
conf->use_internal_date = parse_bool( cfile );
|
conf->use_internal_date = parse_bool( cfile );
|
||||||
} else if (!strcasecmp( "MaxMessages", cfile->cmd )) {
|
} else if (!strcasecmp( "MaxMessages", cfile->cmd )) {
|
||||||
|
@ -354,24 +357,34 @@ load_config( const char *where )
|
||||||
char buf[1024];
|
char buf[1024];
|
||||||
|
|
||||||
if (!where) {
|
if (!where) {
|
||||||
|
int path_len, path_len2;
|
||||||
const char *config_home = getenv( "XDG_CONFIG_HOME" );
|
const char *config_home = getenv( "XDG_CONFIG_HOME" );
|
||||||
if (config_home)
|
if (config_home)
|
||||||
nfsnprintf( path, sizeof(path), "%s/isyncrc", config_home );
|
nfsnprintf( path, sizeof(path), "%s/%nisyncrc", config_home, &path_len );
|
||||||
else
|
else
|
||||||
nfsnprintf( path, sizeof(path), "%s/.config/isyncrc", Home );
|
nfsnprintf( path, sizeof(path), "%s/.config/%nisyncrc", Home, &path_len );
|
||||||
nfsnprintf( path2, sizeof(path2), "%s/.mbsyncrc", Home );
|
nfsnprintf( path2, sizeof(path2), "%s/%n.mbsyncrc", Home, &path_len2 );
|
||||||
struct stat st;
|
struct stat st;
|
||||||
int ex = !lstat( path, &st );
|
int ex = !lstat( path, &st );
|
||||||
int ex2 = !lstat( path2, &st );
|
int ex2 = !lstat( path2, &st );
|
||||||
if (ex2 && !ex) {
|
if (ex2 && !ex) {
|
||||||
cfile.file = path2;
|
cfile.file = path2;
|
||||||
|
cfile.path_len = path_len2;
|
||||||
} else {
|
} else {
|
||||||
if (ex && ex2)
|
if (ex && ex2)
|
||||||
warn( "Both %s and %s exist; using the former.\n", path, path2 );
|
warn( "Both %s and %s exist; using the former.\n", path, path2 );
|
||||||
cfile.file = path;
|
cfile.file = path;
|
||||||
|
cfile.path_len = path_len;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cfile.file = where;
|
const char *sl = strrchr( where, '/' );
|
||||||
|
if (!sl) {
|
||||||
|
nfsnprintf( path, sizeof(path), "./%n%s", &cfile.path_len, where );
|
||||||
|
cfile.file = path;
|
||||||
|
} else {
|
||||||
|
cfile.path_len = sl - where + 1;
|
||||||
|
cfile.file = where;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info( "Reading configuration file %s\n", cfile.file );
|
info( "Reading configuration file %s\n", cfile.file );
|
||||||
|
|
|
@ -18,6 +18,7 @@ typedef struct {
|
||||||
int line;
|
int line;
|
||||||
int err;
|
int err;
|
||||||
int ms_warn;
|
int ms_warn;
|
||||||
|
int path_len;
|
||||||
char *cmd, *val, *rest;
|
char *cmd, *val, *rest;
|
||||||
} conffile_t;
|
} conffile_t;
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ extern char FieldDelimiter;
|
||||||
#define ARG_OPTIONAL 0
|
#define ARG_OPTIONAL 0
|
||||||
#define ARG_REQUIRED 1
|
#define ARG_REQUIRED 1
|
||||||
|
|
||||||
char *expand_strdup( const char *s );
|
char *expand_strdup( const char *s, const conffile_t *cfile );
|
||||||
|
|
||||||
char *get_arg( conffile_t *cfile, int required, int *comment );
|
char *get_arg( conffile_t *cfile, int required, int *comment );
|
||||||
|
|
||||||
|
|
|
@ -3590,7 +3590,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
|
||||||
} while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL )));
|
} while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL )));
|
||||||
#ifdef HAVE_LIBSSL
|
#ifdef HAVE_LIBSSL
|
||||||
} else if (!strcasecmp( "CertificateFile", cfg->cmd )) {
|
} else if (!strcasecmp( "CertificateFile", cfg->cmd )) {
|
||||||
server->sconf.cert_file = expand_strdup( cfg->val );
|
server->sconf.cert_file = expand_strdup( cfg->val, cfg );
|
||||||
if (access( server->sconf.cert_file, R_OK )) {
|
if (access( server->sconf.cert_file, R_OK )) {
|
||||||
sys_error( "%s:%d: CertificateFile '%s'",
|
sys_error( "%s:%d: CertificateFile '%s'",
|
||||||
cfg->file, cfg->line, server->sconf.cert_file );
|
cfg->file, cfg->line, server->sconf.cert_file );
|
||||||
|
@ -3599,14 +3599,14 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
|
||||||
} else if (!strcasecmp( "SystemCertificates", cfg->cmd )) {
|
} else if (!strcasecmp( "SystemCertificates", cfg->cmd )) {
|
||||||
server->sconf.system_certs = parse_bool( cfg );
|
server->sconf.system_certs = parse_bool( cfg );
|
||||||
} else if (!strcasecmp( "ClientCertificate", cfg->cmd )) {
|
} else if (!strcasecmp( "ClientCertificate", cfg->cmd )) {
|
||||||
server->sconf.client_certfile = expand_strdup( cfg->val );
|
server->sconf.client_certfile = expand_strdup( cfg->val, cfg );
|
||||||
if (access( server->sconf.client_certfile, R_OK )) {
|
if (access( server->sconf.client_certfile, R_OK )) {
|
||||||
sys_error( "%s:%d: ClientCertificate '%s'",
|
sys_error( "%s:%d: ClientCertificate '%s'",
|
||||||
cfg->file, cfg->line, server->sconf.client_certfile );
|
cfg->file, cfg->line, server->sconf.client_certfile );
|
||||||
cfg->err = 1;
|
cfg->err = 1;
|
||||||
}
|
}
|
||||||
} else if (!strcasecmp( "ClientKey", cfg->cmd )) {
|
} else if (!strcasecmp( "ClientKey", cfg->cmd )) {
|
||||||
server->sconf.client_keyfile = expand_strdup( cfg->val );
|
server->sconf.client_keyfile = expand_strdup( cfg->val, cfg );
|
||||||
if (access( server->sconf.client_keyfile, R_OK )) {
|
if (access( server->sconf.client_keyfile, R_OK )) {
|
||||||
sys_error( "%s:%d: ClientKey '%s'",
|
sys_error( "%s:%d: ClientKey '%s'",
|
||||||
cfg->file, cfg->line, server->sconf.client_keyfile );
|
cfg->file, cfg->line, server->sconf.client_keyfile );
|
||||||
|
|
|
@ -1857,9 +1857,9 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
|
||||||
|
|
||||||
while (getcline( cfg ) && cfg->cmd) {
|
while (getcline( cfg ) && cfg->cmd) {
|
||||||
if (!strcasecmp( "Inbox", cfg->cmd )) {
|
if (!strcasecmp( "Inbox", cfg->cmd )) {
|
||||||
store->inbox = expand_strdup( cfg->val );
|
store->inbox = expand_strdup( cfg->val, cfg );
|
||||||
} else if (!strcasecmp( "Path", cfg->cmd )) {
|
} else if (!strcasecmp( "Path", cfg->cmd )) {
|
||||||
store->path = expand_strdup( cfg->val );
|
store->path = expand_strdup( cfg->val, cfg );
|
||||||
#ifdef USE_DB
|
#ifdef USE_DB
|
||||||
} else if (!strcasecmp( "AltMap", cfg->cmd )) {
|
} else if (!strcasecmp( "AltMap", cfg->cmd )) {
|
||||||
store->alt_map = parse_bool( cfg );
|
store->alt_map = parse_bool( cfg );
|
||||||
|
@ -1892,7 +1892,7 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!store->inbox)
|
if (!store->inbox)
|
||||||
store->inbox = expand_strdup( "~/Maildir" );
|
store->inbox = expand_strdup( "~/Maildir", NULL );
|
||||||
if (store->sub_style == SUB_MAILDIRPP && store->path) {
|
if (store->sub_style == SUB_MAILDIRPP && store->path) {
|
||||||
error( "Maildir store '%s': Setting Path is incompatible with 'SubFolders Maildir++'\n", store->name );
|
error( "Maildir store '%s': Setting Path is incompatible with 'SubFolders Maildir++'\n", store->name );
|
||||||
cfg->err = 1;
|
cfg->err = 1;
|
||||||
|
|
|
@ -105,6 +105,8 @@ and literal double quotes and backslashes (\fB\\\fR) must be backslash-escaped.
|
||||||
All keywords (including those used as arguments) are case-insensitive.
|
All keywords (including those used as arguments) are case-insensitive.
|
||||||
Bash-like home directory expansion using the tilde (\fB~\fR) is supported
|
Bash-like home directory expansion using the tilde (\fB~\fR) is supported
|
||||||
in all options which represent local paths.
|
in all options which represent local paths.
|
||||||
|
The reference point for relative local paths is the configuration file's
|
||||||
|
containing directory.
|
||||||
There are a few global options, the others apply to particular sections.
|
There are a few global options, the others apply to particular sections.
|
||||||
Sections begin with a section-starting keyword and are terminated by an empty
|
Sections begin with a section-starting keyword and are terminated by an empty
|
||||||
line or end of file.
|
line or end of file.
|
||||||
|
@ -205,7 +207,8 @@ yet all messages are archived.
|
||||||
(Default: \fBno\fR)
|
(Default: \fBno\fR)
|
||||||
.
|
.
|
||||||
.SS Maildir Stores
|
.SS Maildir Stores
|
||||||
The reference point for relative \fBPath\fRs is the current working directory.
|
The reference point for relative \fBPath\fRs is the configuration file's
|
||||||
|
containing directory.
|
||||||
.P
|
.P
|
||||||
As \fBmbsync\fR needs UIDs, but no standardized UID storage scheme exists for
|
As \fBmbsync\fR needs UIDs, but no standardized UID storage scheme exists for
|
||||||
Maildir, \fBmbsync\fR supports two schemes, each with its pros and cons.
|
Maildir, \fBmbsync\fR supports two schemes, each with its pros and cons.
|
||||||
|
|
|
@ -289,12 +289,12 @@ sub writecfg($)
|
||||||
"FSync no
|
"FSync no
|
||||||
|
|
||||||
MaildirStore far
|
MaildirStore far
|
||||||
Path ./
|
Path \"\"
|
||||||
Inbox ./far
|
Inbox far
|
||||||
".$$sfx[0]."
|
".$$sfx[0]."
|
||||||
MaildirStore near
|
MaildirStore near
|
||||||
Path ./
|
Path \"\"
|
||||||
Inbox ./near
|
Inbox near
|
||||||
".$$sfx[1]."
|
".$$sfx[1]."
|
||||||
Channel test
|
Channel test
|
||||||
Far :far:
|
Far :far:
|
||||||
|
|
|
@ -35,7 +35,7 @@ typedef struct channel_conf {
|
||||||
const char *name;
|
const char *name;
|
||||||
store_conf_t *stores[2];
|
store_conf_t *stores[2];
|
||||||
const char *boxes[2];
|
const char *boxes[2];
|
||||||
char *sync_state;
|
const char *sync_state;
|
||||||
string_list_t *patterns;
|
string_list_t *patterns;
|
||||||
int ops[2];
|
int ops[2];
|
||||||
int max_messages; // For near side only.
|
int max_messages; // For near side only.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user