> that depends on whose perspective you're assuming here.
> i'm likely not going to get it done anytime soon. pushing patches on me 
> prompts me into action, so it's good for the project (not so much for my 
> other projects, but the trade-off isn't that bad, as i'm actually much 
> more efficient at reviewing than hacking myself).
> so if you think it would be a good use of *your* time, then go ahead.

After some somewhat more synchronous communication, it might be. This
project has been a nice refresher course on C.

> i implied that you need to do that anyway to handle things properly.  
> also note that you'd save the duplicated check for the command.
> 
> but looking at it again, duplication probably isn't even necessary: you 
> can factor out check_excess_tokens() and make it also return a value.

I hope this is what you meant. 

> the first patch is now Perfect (TM), so no need to re-send it.

Nice! Progress.

On the subject of more things I could do, is there a way to reproduce
the 2 regressions? I am not familiar with IMAP, but do know how to fix
a regression. I'm not in any hurry, and do not want to promise
anything. But I'd like to take a look.

Cheers, Michiel
>From 8f4c551449e90f80cdbf31fd6a0b207e9802c3e7 Mon Sep 17 00:00:00 2001
From: Michiel van den Heuvel <michielvdnheu...@gmail.com>
Date: Thu, 17 Aug 2023 20:25:51 +0200
Subject: [PATCH] Add IncludeCmd directive to config parser

---
 src/config.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++----
 src/config.h |  3 ++
 src/mbsync.1 |  8 +++++
 3 files changed, 93 insertions(+), 6 deletions(-)

diff --git a/src/config.c b/src/config.c
index ac90c25..a7476a9 100644
--- a/src/config.c
+++ b/src/config.c
@@ -61,12 +61,21 @@ expand_strdup( const char *s, const conffile_t *cfile )
 	}
 }
 
+static void
+conf_print_loc( const conffile_t *cfile )
+{
+	if (cfile->eval_fp)
+		fprintf( stderr, "%s:%d:included:%d: ", cfile->file, cfile->line, cfile->eval_line );
+	else
+		fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
+}
+
 void
 conf_error( conffile_t *cfile, const char *fmt, ... )
 {
 	va_list va;
 
-	fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
+	conf_print_loc( cfile );
 	va_start( va, fmt );
 	vfprintf( stderr, fmt, va );
 	va_end( va );
@@ -79,7 +88,7 @@ conf_sys_error( conffile_t *cfile, const char *fmt, ... )
 	va_list va;
 
 	int errno_bak = errno;
-	fprintf( stderr, "%s:%d: ", cfile->file, cfile->line );
+	conf_print_loc( cfile );
 	errno = errno_bak;
 	va_start( va, fmt );
 	vsys_error( fmt, va );
@@ -318,17 +327,72 @@ getopt_helper( conffile_t *cfile, int *cops, channel_conf_t *conf )
 	return 1;
 }
 
+static void
+eval_cmd_popen( conffile_t *cfile, const char *cmd )
+{
+	if (!(cfile->eval_fp = popen( cmd, "r" ))) {
+		sys_error( "popen" );
+		cfile->err = 1;
+		return;
+	}
+	cfile->eval_line = 0;
+	cfile->eval_command = nfstrdup( cmd );
+}
+
+static void
+eval_cmd_pclose( conffile_t *cfile )
+{
+	int ret;
+
+	if ((ret = pclose( cfile->eval_fp ))) {
+		if (ret < 0) {
+			sys_error( "pclose" );
+			cfile->err = 1;
+		} else if (WIFSIGNALED( ret )) {
+			conf_error( cfile, "command \"%s\" crashed with signal %d\n",
+			            cfile->eval_command, WTERMSIG( ret ) );
+		} else {
+			conf_error( cfile, "command \"%s\" exited with status %d\n",
+			            cfile->eval_command, WEXITSTATUS( ret ) );
+		}
+	}
+	free( cfile->eval_command );
+	cfile->eval_fp = NULL;
+	cfile->eval_command = NULL;
+}
+
+static int
+read_cline( conffile_t *cfile )
+{
+	if (cfile->eval_fp) {
+		cfile->eval_line++;
+		if ((cfile->rest = fgets( cfile->buf, cfile->bufl, cfile->eval_fp )))
+			return 1;
+		eval_cmd_pclose( cfile );
+	}
+	cfile->line++;
+	return (cfile->rest = fgets( cfile->buf, cfile->bufl, cfile->fp )) != NULL;
+}
+
+static char *
+check_excess_tokens( conffile_t *cfile )
+{
+	char *arg = NULL;
+
+	if (cfile->rest)
+		arg = get_arg( cfile, ARG_OPTIONAL, NULL );
+	return arg;
+}
+
 int
 getcline( conffile_t *cfile )
 {
 	char *arg;
 	int comment;
 
-	if (cfile->rest && (arg = get_arg( cfile, ARG_OPTIONAL, NULL )))
+	if ((arg = check_excess_tokens( cfile )))
 		conf_error( cfile, "excess token '%s'\n", arg );
-	while (fgets( cfile->buf, cfile->bufl, cfile->fp )) {
-		cfile->line++;
-		cfile->rest = cfile->buf;
+	while (read_cline( cfile )) {
 		if (!(cfile->cmd = get_arg( cfile, ARG_OPTIONAL, &comment ))) {
 			if (comment)
 				continue;
@@ -336,6 +400,16 @@ getcline( conffile_t *cfile )
 		}
 		if (!(cfile->val = get_arg( cfile, ARG_REQUIRED, NULL )))
 			continue;
+		if (!strcasecmp( cfile->cmd, "IncludeCmd" )) {
+			if (cfile->eval_fp)
+				conf_error( cfile, "nested IncludeCmd\n" );
+			else if ((arg = check_excess_tokens( cfile )))
+				conf_error( cfile, "excess token '%s', not executing "
+				            "potentially malformed command\n", arg );
+			else
+				eval_cmd_popen( cfile, cfile->val );
+			continue;
+		}
 		return 1;
 	}
 	return 0;
@@ -488,6 +562,7 @@ load_config( const char *where )
 		return 1;
 	}
 	buf[sizeof(buf) - 1] = 0;
+	cfile.eval_fp = NULL;
 	cfile.buf = buf;
 	cfile.bufl = sizeof(buf) - 1;
 	cfile.line = 0;
@@ -495,6 +570,7 @@ load_config( const char *where )
 	cfile.ms_warn = 0;
 	cfile.renew_warn = 0;
 	cfile.delete_warn = 0;
+	cfile.cmd = NULL;
 	cfile.rest = NULL;
 
 	gcops = 0;
diff --git a/src/config.h b/src/config.h
index 2177fb4..c1642ef 100644
--- a/src/config.h
+++ b/src/config.h
@@ -12,10 +12,13 @@
 
 typedef struct {
 	const char *file;
+	char *eval_command;
 	FILE *fp;
+	FILE *eval_fp;
 	char *buf;
 	int bufl;
 	int line;
+	int eval_line;
 	int err;
 	int ms_warn, renew_warn, delete_warn;
 	int path_len;
diff --git a/src/mbsync.1 b/src/mbsync.1
index 939c8c5..1a9e70e 100644
--- a/src/mbsync.1
+++ b/src/mbsync.1
@@ -786,6 +786,14 @@ absolute limit, as even a single message can consume more memory than
 this.
 (Default: \fI10M\fR)
 .
+.SS Dynamic Configuration
+.TP
+\fBIncludeCmd\fR \fIcommand\fR
+Specify a shell command to obtain configuration file content from.
+This keyword can appear anywhere. The command's output will be
+interpreted as if it appeared inline; it can range from nothing at all
+to multiple sections.
+.
 .SH CONSOLE OUTPUT
 If \fBmbsync\fR's output is connected to a console, it will print progress
 counters by default. The output will look like this:
-- 
2.42.0

_______________________________________________
isync-devel mailing list
isync-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/isync-devel

Reply via email to