changeset 806:c9237f9a3465

Improve algorithm for list text substitutions. Now the string is processed character by character, so multiple substitutions on a single line will work when substitutions do not actually take place, and it is more efficient.
author Ben Schmidt
date Fri, 30 Dec 2011 00:20:21 +1100
parents 255d277b37db
children 5dc52f70e76b
files ChangeLog include/prepstdreply.h src/prepstdreply.c
diffstat 3 files changed, 69 insertions(+), 108 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Jan 04 04:10:31 2012 +1100
+++ b/ChangeLog	Fri Dec 30 00:20:21 2011 +1100
@@ -1,4 +1,5 @@
  o Allow a space in $originalmail N$ substitution
+ o Improve algorithm for list text substitutions
  o Add $text T$ substitution
  o Add $$ substitution
  o Allow list texts to include real UTF-8 characters, as documented
--- a/include/prepstdreply.h	Wed Jan 04 04:10:31 2012 +1100
+++ b/include/prepstdreply.h	Fri Dec 30 00:20:21 2011 +1100
@@ -1,4 +1,5 @@
 /* Copyright (C) 2004 Mads Martin Joergensen <mmj at mmj.dk>
+ * Copyright (C) 2011 Ben Schmidt <mail_ben_schmidt at yahoo.com.au>
  *
  * $Id$
  *
@@ -26,8 +27,6 @@
 
 char *substitute(const char *line, const char *listaddr, const char *listdelim,
 		size_t datacount, char **data, const char *listdir);
-char *substitute_one(const char *line, const char *listaddr,
-		const char *listdelim, size_t datacount, char **data, const char *listdir);
 int open_listtext(const char *listdir, const char *filename);
 char *prepstdreply(const char *listdir, const char *purpose, const char *action,
 		const char *reason, const char *type, const char *compat,
--- a/src/prepstdreply.c	Wed Jan 04 04:10:31 2012 +1100
+++ b/src/prepstdreply.c	Fri Dec 30 00:20:21 2011 +1100
@@ -1,5 +1,6 @@
 /* Copyright (C) 2004 Mads Martin Joergensen <mmj at mmj.dk>
  * Copyright (C) 2007 Morten K. Poulsen <morten at afdelingp.dk>
+ * Copyright (C) 2011 Ben Schmidt <mail_ben_schmidt at yahoo.com.au>
  *
  * $Id$
  *
@@ -44,184 +45,144 @@
 #include "getlistdelim.h"
 #include "unistr.h"
 
-char *substitute(const char *line, const char *listaddr, const char *listdelim,
-		 size_t datacount, char **data, const char *listdir)
-{
-	char *s1, *s2;
 
-	s1 = substitute_one(line, listaddr, listdelim, datacount, data,
-			listdir);
-	while(s1) {
-		s2 = substitute_one(s1, listaddr, listdelim, datacount, data,
-				listdir);
-		if(s2) {
-			myfree(s1);
-			s1 = s2;
-		} else
-			return s1;
+static char *alphanum_token(char *token) {
+	char *pos;
+	if (*token == '\0') return NULL;
+	for(pos = token; *pos != '\0'; pos++) {
+		if(*pos >= '0' && *pos <= '9') continue;
+		if(*pos >= 'A' && *pos <= 'Z') continue;
+		if(*pos >= 'a' && *pos <= 'z') continue;
+		break;
+	}
+	if (*pos != '\0') return NULL;
+	return token;
 	}
 		
-	return mystrdup(line);
-}
 
-char *substitute_one(const char *line, const char *listaddr,
+static void substitute_one(char **line_p, char **pos_p, const char *listaddr,
 			const char *listdelim, size_t datacount, char **data,
 			const char *listdir)
 {
-	char *fqdn, *listname, *d1, *d2, *token, *value = NULL;
-	char *retstr, *origline;
-	size_t len, i;
-
-	if(line == NULL)
-		return NULL;
-
-	origline = mystrdup(line);
-
-	d1 = strchr(origline, '$');
+	char *line = *line_p;
+	char *pos = *pos_p;
+	char *token = pos + 1;
+	char *endpos;
+	char *fqdn, *listname;
+	char *value = NULL;
+	size_t i;
 
-	if(d1 == NULL) {
-		myfree(origline);
-		return NULL;
-	} else
-		d2 = strchr(d1 + 1, '$');
-	
-	if(d1 && d2) {
-		len = d2 - d1;
-		token = mymalloc(len + 1);
-		snprintf(token, len, "%s", d1 + 1);
-	} else {
-		myfree(origline);
-		return NULL;
+	endpos = strchr(token, '$');
+	if (endpos == NULL) {
+		(*pos_p)++;
+		return;
 	}
 
-	*d1 = '\0';
+	*pos = '\0';
+	*endpos = '\0';
 
 	fqdn = genlistfqdn(listaddr);
 	listname = genlistname(listaddr);
 
 	if(strcmp(token, "") == 0) {
 		value = mystrdup("$");
-		goto concatandreturn;
 	} else if(strcmp(token, "listaddr") == 0) {
 		/* DEPRECATED: use $list$@$domain$ instead */
 		value = mystrdup(listaddr);
-		goto concatandreturn;
 	} else if(strcmp(token, "list+") == 0) {
 		value = concatstr(2, listname, listdelim);
-		goto concatandreturn;
 	} else if(strcmp(token, "list") == 0) {
 		value = mystrdup(listname);
-		goto concatandreturn;
 	} else if(strcmp(token, "domain") == 0) {
 		value = mystrdup(fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "listowner") == 0) {
 		/* DEPRECATED: use $list+$owner@$domain$ instead */
 		value = concatstr(4, listname, listdelim, "owner@", fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "helpaddr") == 0) {
 		/* DEPRECATED: use $list+$help@$domain$ instead */
 		value = concatstr(4, listname, listdelim, "help@", fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "faqaddr") == 0) {
 		/* DEPRECATED: use $list+$faq@$domain$ instead */
 		value = concatstr(4, listname, listdelim, "faq@", fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "listgetN") == 0) {
 		/* DEPRECATED: use $list+$get-N@$domain$ instead */
 		value = concatstr(4, listname, listdelim, "get-N@", fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "listunsubaddr") == 0) {
 		/* DEPRECATED: use $list+$unsubscribe@$domain$ instead */
 		value = concatstr(4, listname, listdelim, "unsubscribe@", fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "digestunsubaddr") == 0) {
 		/* DEPRECATED: use $list+$unsubscribe-digest@$domain$ instead */
 		value = concatstr(4, listname, listdelim,
 				  "unsubscribe-digest@", fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "nomailunsubaddr") == 0) {
 		/* DEPRECATED: use $list+$unsubscribe-nomail@$domain$ instead */
 		value = concatstr(4, listname, listdelim,
 				  "unsubscribe-nomail@", fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "listsubaddr") == 0) {
 		/* DEPRECATED: use $list+$subscribe@$domain$ instead */
 		value = concatstr(4, listname, listdelim, "subscribe@", fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "digestsubaddr") == 0) {
 		/* DEPRECATED: use $list+$subscribe-digest@$domain$ instead */
 		value = concatstr(4, listname, listdelim, "subscribe-digest@",
 				  fqdn);
-		goto concatandreturn;
 	} else if(strcmp(token, "nomailsubaddr") == 0) {
 		/* DEPRECATED: use $list+$subscribe-nomail@$domain$ instead */
 		value = concatstr(4, listname, listdelim, "subscribe-nomail@",
 				  fqdn);
-		goto concatandreturn;
 	} else if(strncmp(token, "control ", 8) == 0) {
-		value = token + 8;
-		if(*value == '\0') {
-			value = mystrdup("");
-			goto concatandreturn;
-		}
-		for(; *value != '\0'; value++) {
-			if(*value >= '0' && *value <= '9') continue;
-			if(*value >= 'A' && *value <= 'Z') continue;
-			if(*value >= 'a' && *value <= 'z') continue;
-			break;
-		}
-		if(*value != '\0') {
-			value = mystrdup(token + 8);
-			goto concatandreturn;
-		}
-		value = token + 8;
-		value = ctrlcontent(listdir, value);
-		if (value == NULL)
-			value = mystrdup("");
-		goto concatandreturn;
+		token = alphanum_token(token + 8);
+		if (token != NULL) value = ctrlcontent(listdir, token);
 	} else if(strncmp(token, "text ", 5) == 0) {
-		value = token + 5;
-		if(*value == '\0') {
-			value = mystrdup("");
-			goto concatandreturn;
-		}
-		for(; *value != '\0'; value++) {
-			if(*value >= '0' && *value <= '9') continue;
-			if(*value >= 'A' && *value <= 'Z') continue;
-			if(*value >= 'a' && *value <= 'z') continue;
-			break;
-		}
-		if(*value != '\0') {
-			value = mystrdup(token + 5);
-			goto concatandreturn;
-		}
-		value = token + 5;
-		value = textcontent(listdir, value);
-		if (value == NULL)
-			value = mystrdup("");
-		goto concatandreturn;
+		token = alphanum_token(token + 5);
+		if (token != NULL) value = textcontent(listdir, token);
 	} else if(data) {
 		for(i = 0; i < datacount; i++) {
 			if(strcmp(token, data[i*2]) == 0) {
 				value = mystrdup(data[(i*2)+1]);
-				goto concatandreturn;
+				break;
 			}
 		}
 	}
 
-	myfree(origline);
-	return NULL;
-
-concatandreturn:
-	retstr = concatstr(3, origline, value, d2 + 1);
-	myfree(origline);
+	if (value != NULL) {
+		line = concatstr(3, line, value, endpos + 1);
+		*pos_p = line + (*pos_p - *line_p);
+		if (strcmp(value, "$") == 0) (*pos_p)++;
+		myfree(*line_p);
+		*line_p = line;
 	myfree(value);
-	myfree(token);
+	} else {
+		*pos = '$';
+		*endpos = '$';
+		(*pos_p)++;
+	}
 	myfree(fqdn);
 	myfree(listname);
+}
 
-	return retstr;
+
+char *substitute(const char *line, const char *listaddr, const char *listdelim,
+		 size_t datacount, char **data, const char *listdir)
+{
+	char *new;
+	char *pos;
+
+	new = mystrdup(line);
+	pos = new;
+
+	while (*pos != '\0') {
+		if (*pos == '$') {
+			substitute_one(&new, &pos,
+					listaddr, listdelim,
+					datacount, data, listdir);
+			/* The function sets up for the next character
+			 * to process, so continue straight away. */
+			continue;
+		}
+		pos++;
+	}
+
+	return new;
 }