changeset 816:ed446ead03bd

Add support for conditionals in list texts.
author Ben Schmidt
date Tue, 17 Jan 2012 14:14:16 +1100
parents fdd43713fb52
children 9caf15c6ae31
files ChangeLog src/Makefile.am src/prepstdreply.c
diffstat 3 files changed, 239 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Tue Jan 17 13:05:40 2012 +1100
+++ b/ChangeLog	Tue Jan 17 14:14:16 2012 +1100
@@ -1,3 +1,4 @@
+ o Add support for conditionals in list texts
  o Add %wrap% and %wrap W% formatting directives
  o Add %digestthreads%, %gatekeepers%, %listsubs%, %digestsubs%, %nomailsubs%,
    %moderators% and %bouncenumbers%
--- a/src/Makefile.am	Tue Jan 17 13:05:40 2012 +1100
+++ b/src/Makefile.am	Tue Jan 17 14:14:16 2012 +1100
@@ -49,7 +49,7 @@
 		       subscriberfuncs.c strgen.c random-int.c writen.c \
 		       prepstdreply.c mygetline.c chomp.c getlistaddr.c \
 		       memory.c find_email_adr.c gethdrline.c readn.c \
-		       getlistdelim.c unistr.c ctrlvalue.c
+		       getlistdelim.c unistr.c ctrlvalue.c statctrl.c
 
 mlmmj_maintd_SOURCES = mlmmj-maintd.c print-version.c log_error.c mygetline.c \
 		       strgen.c random-int.c chomp.c writen.c memory.c \
--- a/src/prepstdreply.c	Tue Jan 17 13:05:40 2012 +1100
+++ b/src/prepstdreply.c	Tue Jan 17 14:14:16 2012 +1100
@@ -33,6 +33,7 @@
 #include <errno.h>
 
 #include "prepstdreply.h"
+#include "statctrl.h"
 #include "ctrlvalue.h"
 #include "strgen.h"
 #include "chomp.h"
@@ -80,13 +81,35 @@
 };
 
 
+struct conditional;
+typedef struct conditional conditional;
+struct conditional {
+	int satisfied;
+	int elsepart;
+	conditional *outer;
+};
+
+
+enum conditional_target {
+	ACTION,
+	REASON,
+	TYPE,
+	CONTROL
+};
+
+
 struct text {
+	char *action;
+	char *reason;
+	char *type;
 	source *src;
 	substitution *substs;
 	char *mailname;
 	formatted *fmts;
 	size_t wrapindent;
 	size_t wrapwidth;
+	conditional *cond;
+	conditional *skip;
 };
 
 
@@ -424,11 +447,16 @@
 	txt->src->transparent = 0;
 	txt->src->limit = -1;
 	txt->src->fmt = NULL;
+	txt->action = NULL;
+	txt->reason = NULL;
+	txt->type = NULL;
 	txt->substs = NULL;
 	txt->mailname = NULL;
 	txt->fmts = NULL;
 	txt->wrapindent = 0;
 	txt->wrapwidth = 0;
+	txt->cond = NULL;
+	txt->skip = NULL;
 
 	tmp = concatstr(3, listdir, "/text/", filename);
 	txt->src->fd = open(tmp, O_RDONLY);
@@ -485,6 +513,10 @@
 		return NULL;
 	} while (0);
 
+	txt->action = action != NULL ? mystrdup(action) : NULL;
+	txt->reason = reason != NULL ? mystrdup(reason) : NULL;
+	txt->type = type != NULL ? mystrdup(type) : NULL;
+
 	return txt;
 }
 
@@ -626,6 +658,91 @@
 }
 
 
+static int handle_conditional(text *txt, char **line_p, char **pos_p,
+		char *token, int neg, enum conditional_target tgt, int multi,
+		const char *listdir)
+{
+	char *line = *line_p;
+	char *pos;
+	int satisfied = 0;
+	int matches;
+	conditional *cond;
+
+	if (txt->skip == NULL) {
+		for (;;) {
+			if (*token == '\0') break;
+			for (pos = token;
+					*pos != '\0' && (!multi || *pos != ' ');
+					pos++) {
+				if(*pos >= '0' && *pos <= '9') continue;
+				if(*pos >= 'A' && *pos <= 'Z') continue;
+				if(*pos >= 'a' && *pos <= 'z') continue;
+				if(*pos == '_') continue;
+				if(*pos == '-') continue;
+				if(*pos == '.') continue;
+				break;
+			}
+			if (*pos == ' ') {
+				*pos = '\0';
+			} else {
+				multi = 0;
+			}
+			if (*pos != '\0') return 1;
+
+			matches = 0;
+			if (tgt == ACTION) {
+				if (txt->action == NULL) return 1;
+				if (strcasecmp(token, txt->action) == 0)
+						matches = 1;
+			} else if (tgt == REASON) {
+				if (txt->reason == NULL) return 1;
+				if (strcasecmp(token, txt->reason) == 0)
+						matches = 1;
+			} else if (tgt == TYPE) {
+				if (txt->type == NULL) return 1;
+				if (strcasecmp(token, txt->type) == 0)
+						matches = 1;
+			} else if (tgt == CONTROL) {
+				if (statctrl(listdir, token))
+						matches = 1;
+			}
+			if ((matches && !neg) || (!matches && neg)) {
+				satisfied = 1;
+				break;
+			}
+
+			if (!multi) break;
+			*pos = ' ';
+			pos++;
+		}
+	} else {
+		satisfied = 1;
+		pos = token + 1;
+		while (*pos != '\0') pos++;
+		multi = 0;
+	}
+
+	cond = mymalloc(sizeof(conditional));
+	cond->satisfied = satisfied;
+	cond->elsepart = 0;
+	cond->outer = txt->cond;
+	txt->cond = cond;
+	if (!satisfied) txt->skip = cond;
+
+	if (multi) {
+		*pos = ' ';
+		pos++;
+		while (*pos != '\0') pos++;
+	}
+	line = concatstr(2, line, pos + 1);
+	*pos_p = line + (*pos_p - *line_p);
+	myfree(*line_p);
+	*line_p = line;
+
+	return 0;
+}
+
+
 static void handle_directive(text *txt, char **line_p, char **pos_p,
 		const char *listdir) {
 	char *line = *line_p;
@@ -635,6 +752,7 @@
 	char *filename;
 	int limit;
 	formatted *fmt;
+	conditional *cond;
 
 	endpos = strchr(token, '%');
 	if (endpos == NULL) {
@@ -645,6 +763,72 @@
 	*pos = '\0';
 	*endpos = '\0';
 
+	if(strncmp(token, "ifaction ", 9) == 0) {
+		token += 9;
+		if (handle_conditional(txt, line_p, pos_p, token,
+				0, ACTION, 1, listdir) == 0) return;
+	} else if(strncmp(token, "ifreason ", 9) == 0) {
+		token += 9;
+		if (handle_conditional(txt, line_p, pos_p, token,
+				0, REASON, 1, listdir) == 0) return;
+	} else if(strncmp(token, "iftype ", 7) == 0) {
+		token += 7;
+		if (handle_conditional(txt, line_p, pos_p, token,
+				0, TYPE, 1, listdir) == 0) return;
+	} else if(strncmp(token, "ifcontrol ", 10) == 0) {
+		token += 10;
+		if (handle_conditional(txt, line_p, pos_p, token,
+				0, CONTROL, 1, listdir) == 0) return;
+	} else if(strncmp(token, "ifnaction ", 10) == 0) {
+		token += 10;
+		if (handle_conditional(txt, line_p, pos_p, token,
+				1, ACTION, 0, listdir) == 0) return;
+	} else if(strncmp(token, "ifnreason ", 10) == 0) {
+		token += 10;
+		if (handle_conditional(txt, line_p, pos_p, token,
+				1, REASON, 0, listdir) == 0) return;
+	} else if(strncmp(token, "ifntype ", 8) == 0) {
+		token += 8;
+		if (handle_conditional(txt, line_p, pos_p, token,
+				1, TYPE, 0, listdir) == 0) return;
+	} else if(strncmp(token, "ifncontrol ", 11) == 0) {
+		token += 11;
+		if (handle_conditional(txt, line_p, pos_p, token,
+				1, CONTROL, 1, listdir) == 0) return;
+	} else if(strcmp(token, "else") == 0) {
+		if (txt->cond != NULL) {
+			if (txt->skip == txt->cond) txt->skip = NULL;
+			else if (txt->skip == NULL) txt->skip = txt->cond;
+			txt->cond->elsepart = 1;
+			line = concatstr(2, line, endpos + 1);
+			*pos_p = line + (*pos_p - *line_p);
+			myfree(*line_p);
+			*line_p = line;
+			return;
+		}
+	} else if(strcmp(token, "endif") == 0) {
+		if (txt->cond != NULL) {
+			if (txt->skip == txt->cond) txt->skip = NULL;
+			cond = txt->cond;
+			txt->cond = txt->cond->outer;
+			myfree(cond);
+			line = concatstr(2, line, endpos + 1);
+			*pos_p = line + (*pos_p - *line_p);
+			myfree(*line_p);
+			*line_p = line;
+			return;
+		}
+	}
+
+	if (txt->skip != NULL) {
+		/* We don't process anything but conditionals if we're
+		 * already skipping text in one. */
+		*pos = '%';
+		*endpos = '%';
+		(*pos_p)++;
+		return;
+	}
+
 	if(strcmp(token, "") == 0) {
 		line = concatstr(3, line, "%", endpos + 1);
 		*pos_p = line + (*pos_p - *line_p) + 1;
@@ -764,6 +948,7 @@
 	char *pos;
 	char *tmp, *spc;
 	char *prev = NULL;
+	int incision;
 	size_t len, i;
 
 	for (;;) {
@@ -845,9 +1030,11 @@
 			pos = line;
 		}
 
+		incision = txt->skip != NULL ? pos - line : -1;
 		spc = NULL;
 		while (*pos != '\0') {
-			if (txt->wrapwidth != 0 && len > txt->wrapwidth) break;
+			if (txt->wrapwidth != 0 && len > txt->wrapwidth &&
+					txt->skip == NULL) break;
 			if (*pos == '\r') {
 				*pos = '\0';
 				pos++;
@@ -866,7 +1053,7 @@
 			} else if (txt->src->transparent) {
 				/* Do nothing if the file is to be included
 			 	 * transparently */
-			} else if (*pos == '$') {
+			} else if (*pos == '$' && txt->skip == NULL) {
 				substitute_one(&line, &pos, listaddr,
 						listdelim, listdir, txt);
 				len = pos - line;
@@ -878,14 +1065,54 @@
 				handle_directive(txt, &line, &pos, listdir);
 				len = pos - line;
 				spc = NULL;
-				/* The function sets up for the next character
-				 * to process, so continue straight away. */
+				if (txt->skip != NULL) {
+					if (incision == -1) {
+						/* We have to cut a bit out
+						 * later */
+						incision = pos - line;
+					}
+				} else {
+					if (incision != -1) {
+					    /* Time to cut */
+					    if (pos-line != incision) {
+						line[incision] = '\0';
+						tmp = concatstr(2, line, pos);
+						pos = tmp + incision;
+						myfree(line);
+						line = tmp;
+					    }
+					    incision = -1;
+					}
+				}
+				/* handle_directive() sets up for the next
+				 * character to process, so continue straight
+				 * away. */
 				continue;
 			}
 			len++;
 			pos++;
 		}
 
+		if (incision == 0) {
+			/* The whole line was skipped; nothing to return yet;
+			 * keep reading */
+			incision = -1;
+			myfree(line);
+			continue;
+		}
+
+		if (incision != -1) {
+			/* Time to cut */
+			if (pos - line != incision) {
+				line[incision] = '\0';
+				tmp = mystrdup(line);
+				pos = tmp + incision;
+				myfree(line);
+				line = tmp;
+			}
+			incision = -1;
+		}
+
 		if (txt->wrapwidth != 0) {
 			if (len <= txt->wrapwidth) {
 				prev = line;
@@ -944,6 +1171,7 @@
 void close_text(text *txt) {
 	substitution *subst;
 	formatted *fmt;
+	conditional *cond;
 	while (txt->src != NULL) {
 		close_source(txt);
 	}
@@ -961,6 +1189,11 @@
 		txt->fmts = txt->fmts->next;
 		myfree(fmt);
 	}
+	while (txt->cond != NULL) {
+		cond = txt->cond;
+		txt->cond = txt->cond->outer;
+		myfree(cond);
+	}
 	myfree(txt);
 }