commit d3f118be79686adfcd085211ad3a4b3fa892bf4e
parent cf13630a00e4761e26f054b37875085958800eeb
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date: Mon, 30 May 2022 23:04:52 +0200
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.
Diffstat:
8 files changed, 39 insertions(+), 19 deletions(-)
diff --git a/NEWS b/NEWS
@@ -3,6 +3,9 @@
Changed default config & state locations to follow the XDG basedir spec.
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]
The 'isync' compatibility wrapper was removed.
diff --git a/src/config.c b/src/config.c
@@ -23,7 +23,7 @@ char FieldDelimiter = ':';
static store_conf_t *stores;
char *
-expand_strdup( const char *s )
+expand_strdup( const char *s, const conffile_t *cfile )
{
struct passwd *pw;
const char *p, *q;
@@ -51,6 +51,9 @@ expand_strdup( const char *s )
}
nfasprintf( &r, "%s%s", q, p ? p : "" );
return r;
+ } else if (*s != '/') {
+ nfasprintf( &r, "%.*s%s", cfile->path_len, cfile->file, s );
+ return r;
} else {
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 )));
conf->ops[F] |= XOP_HAVE_TYPE;
} 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 )) {
conf->use_internal_date = parse_bool( cfile );
} else if (!strcasecmp( "MaxMessages", cfile->cmd )) {
@@ -354,24 +357,34 @@ load_config( const char *where )
char buf[1024];
if (!where) {
+ int path_len, path_len2;
const char *config_home = getenv( "XDG_CONFIG_HOME" );
if (config_home)
- nfsnprintf( path, sizeof(path), "%s/isyncrc", config_home );
+ nfsnprintf( path, sizeof(path), "%s/%nisyncrc", config_home, &path_len );
else
- nfsnprintf( path, sizeof(path), "%s/.config/isyncrc", Home );
- nfsnprintf( path2, sizeof(path2), "%s/.mbsyncrc", Home );
+ nfsnprintf( path, sizeof(path), "%s/.config/%nisyncrc", Home, &path_len );
+ nfsnprintf( path2, sizeof(path2), "%s/%n.mbsyncrc", Home, &path_len2 );
struct stat st;
int ex = !lstat( path, &st );
int ex2 = !lstat( path2, &st );
if (ex2 && !ex) {
cfile.file = path2;
+ cfile.path_len = path_len2;
} else {
if (ex && ex2)
warn( "Both %s and %s exist; using the former.\n", path, path2 );
cfile.file = path;
+ cfile.path_len = path_len;
}
} 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 );
diff --git a/src/config.h b/src/config.h
@@ -18,6 +18,7 @@ typedef struct {
int line;
int err;
int ms_warn;
+ int path_len;
char *cmd, *val, *rest;
} conffile_t;
@@ -26,7 +27,7 @@ extern char FieldDelimiter;
#define ARG_OPTIONAL 0
#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 );
diff --git a/src/drv_imap.c b/src/drv_imap.c
@@ -3590,7 +3590,7 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
} while ((arg = get_arg( cfg, ARG_OPTIONAL, NULL )));
#ifdef HAVE_LIBSSL
} 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 )) {
sys_error( "%s:%d: CertificateFile '%s'",
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 )) {
server->sconf.system_certs = parse_bool( cfg );
} 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 )) {
sys_error( "%s:%d: ClientCertificate '%s'",
cfg->file, cfg->line, server->sconf.client_certfile );
cfg->err = 1;
}
} 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 )) {
sys_error( "%s:%d: ClientKey '%s'",
cfg->file, cfg->line, server->sconf.client_keyfile );
diff --git a/src/drv_maildir.c b/src/drv_maildir.c
@@ -1857,9 +1857,9 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
while (getcline( cfg ) && 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 )) {
- store->path = expand_strdup( cfg->val );
+ store->path = expand_strdup( cfg->val, cfg );
#ifdef USE_DB
} else if (!strcasecmp( "AltMap", cfg->cmd )) {
store->alt_map = parse_bool( cfg );
@@ -1892,7 +1892,7 @@ maildir_parse_store( conffile_t *cfg, store_conf_t **storep )
}
}
if (!store->inbox)
- store->inbox = expand_strdup( "~/Maildir" );
+ store->inbox = expand_strdup( "~/Maildir", NULL );
if (store->sub_style == SUB_MAILDIRPP && store->path) {
error( "Maildir store '%s': Setting Path is incompatible with 'SubFolders Maildir++'\n", store->name );
cfg->err = 1;
diff --git a/src/mbsync.1 b/src/mbsync.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.
Bash-like home directory expansion using the tilde (\fB~\fR) is supported
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.
Sections begin with a section-starting keyword and are terminated by an empty
line or end of file.
@@ -205,7 +207,8 @@ yet all messages are archived.
(Default: \fBno\fR)
.
.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
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.
diff --git a/src/run-tests.pl b/src/run-tests.pl
@@ -289,12 +289,12 @@ sub writecfg($)
"FSync no
MaildirStore far
-Path ./
-Inbox ./far
+Path \"\"
+Inbox far
".$$sfx[0]."
MaildirStore near
-Path ./
-Inbox ./near
+Path \"\"
+Inbox near
".$$sfx[1]."
Channel test
Far :far:
diff --git a/src/sync.h b/src/sync.h
@@ -35,7 +35,7 @@ typedef struct channel_conf {
const char *name;
store_conf_t *stores[2];
const char *boxes[2];
- char *sync_state;
+ const char *sync_state;
string_list_t *patterns;
int ops[2];
int max_messages; // For near side only.