changeset 809:99fb6ba7a76e

Add %%, %^%, %comment%, %control C%, %text T% formatting directives. To support inclusion, use a stack of sources when reading list texts.
author Ben Schmidt
date Mon, 16 Jan 2012 21:29:17 +1100
parents 8479195595af
children 8ddfc2d59a3f
files ChangeLog README.listtexts src/prepstdreply.c
diffstat 3 files changed, 223 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Jan 04 04:15:20 2012 +1100
+++ b/ChangeLog	Mon Jan 16 21:29:17 2012 +1100
@@ -1,3 +1,4 @@
+ o Add %%, %^%, %comment%, %control C% and %text T% formatting directives
  o Allow a space in $originalmail N$ substitution
  o Improve algorithm for list text substitutions
  o Add $text T$ substitution
--- a/README.listtexts	Wed Jan 04 04:15:20 2012 +1100
+++ b/README.listtexts	Mon Jan 16 21:29:17 2012 +1100
@@ -289,16 +289,27 @@
   the list of indexes of messages which may not have been received as they
   bounced
 
+- %^%
+  start the line here; anything preceding this directive is ignored (useful for
+  using indentation for readability without ruining the formatting of the text
+  when it is processed)
+
 - %comment%
-  the rest of the line is a comment
+- %$%
+  end the line here; anything following this directive is ignored
 
 - %%
   a single %
 
-All these directives have the behaviour that second and later lines are
-preceded with as many spaces as there were characters preceding the directive
-in the processed output. All of them except %wrap% and %wrap W% cause anything
-following them on the same line to be omitted.
+Directives which include a list of items have the behaviour that each item is
+preceded and followed by the same text as preceded and followed the directive
+on its line. Only one such directive is supported per line.
+
+The %wrap% and %wrap W% directives, as well as those which include a block of
+text, have the behaviour that second and later lines are preceded with as many
+spaces as there were characters preceding the directive. Apart from the
+%wrap% and %wrap W% directives, any text following the directive on the same
+line is omitted.
 
 If a line with any of these directives, after processing, contains only
 whitespace, the line does not appear at all in the output (the newline and any
--- a/src/prepstdreply.c	Wed Jan 04 04:15:20 2012 +1100
+++ b/src/prepstdreply.c	Mon Jan 16 21:29:17 2012 +1100
@@ -46,8 +46,19 @@
 #include "unistr.h"
 
 
+struct source;
+typedef struct source source;
+struct source {
+	source *prev;
+	char *upcoming;
+	char *prefix;
+	char *suffix;
+	int fd;
+};
+
+
 struct text {
-	int fd;
+	source *src;
 };
 
 
@@ -197,21 +208,26 @@
 	text *txt;
 
 	txt = mymalloc(sizeof(text));
+	txt->src = mymalloc(sizeof(source));
+	txt->src->prev = NULL;
+	txt->src->upcoming = NULL;
+	txt->src->prefix = NULL;
+	txt->src->suffix = NULL;
 
 	tmp = concatstr(3, listdir, "/text/", filename);
-	txt->fd = open(tmp, O_RDONLY);
+	txt->src->fd = open(tmp, O_RDONLY);
 	myfree(tmp);
-	if (txt->fd >= 0) return txt;
+	if (txt->src->fd >= 0) return txt;
 
 	tmp = concatstr(2, DEFAULTTEXTDIR "/default/", filename);
-	txt->fd = open(tmp, O_RDONLY);
+	txt->src->fd = open(tmp, O_RDONLY);
 	myfree(tmp);
-	if (txt->fd >= 0) return txt;
+	if (txt->src->fd >= 0) return txt;
 
 	tmp = concatstr(2, DEFAULTTEXTDIR "/en/", filename);
-	txt->fd = open(tmp, O_RDONLY);
+	txt->src->fd = open(tmp, O_RDONLY);
 	myfree(tmp);
-	if (txt->fd >= 0) return txt;
+	if (txt->src->fd >= 0) return txt;
 
 	return NULL;
 }
@@ -257,32 +273,205 @@
 }
 
 
+static void begin_new_source_file(text *txt, char **line_p, char **pos_p,
+		const char *filename) {
+	char *line = *line_p;
+	char *pos = *pos_p;
+	char *tmp;
+	source *src;
+	int fd;
+	size_t len;
+
+	/* Save any later lines for use after finishing the source */
+	while (*pos != '\0' && *pos != '\r' && *pos != '\n') pos++;
+	if (*pos == '\r') pos++;
+	if (*pos == '\n') pos++;
+	if (*pos != '\0') txt->src->upcoming = mystrdup(pos);
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		/* Act as if the source were an empty line */
+		**pos_p = '\0';
+		return;
+	}
+
+	src = mymalloc(sizeof(source));
+	src->prev = txt->src;
+	src->upcoming = NULL;
+	len = strlen(line);
+	src->prefix = mymalloc((len + 1) * sizeof(char));
+	for (tmp = src->prefix; len > 0; ++tmp, --len) *tmp = ' ';
+	*tmp = '\0';
+	src->suffix = NULL;
+	src->fd = fd;
+	txt->src = src;
+	tmp = mygetline(fd);
+	line = concatstr(2, line, tmp);
+	*pos_p = line + (*pos_p - *line_p);
+	myfree(*line_p);
+	*line_p = line;
+	myfree(tmp);
+}
+
+
+static void handle_directive(text *txt, char **line_p, char **pos_p,
+		const char *listdir) {
+	char *line = *line_p;
+	char *pos = *pos_p;
+	char *token = pos + 1;
+	char *endpos;
+	char *filename;
+
+	endpos = strchr(token, '%');
+	if (endpos == NULL) {
+		(*pos_p)++;
+		return;
+	}
+
+	*pos = '\0';
+	*endpos = '\0';
+
+	if(strcmp(token, "") == 0) {
+		line = concatstr(3, line, "%", endpos + 1);
+		*pos_p = line + (*pos_p - *line_p) + 1;
+		myfree(*line_p);
+		*line_p = line;
+		return;
+	} else if(strcmp(token, "^") == 0) {
+		if (txt->src->prefix != NULL) {
+			line[strlen(txt->src->prefix)] = '\0';
+			line = concatstr(2, line, endpos + 1);
+		} else {
+			line = mystrdup(endpos + 1);
+		}
+		*pos_p = line;
+		myfree(*line_p);
+		*line_p = line;
+		return;
+	} else if(strcmp(token, "comment") == 0 || strcmp(token, "$") == 0 ) {
+		/* Skip the rest of the line; the earlier part which we
+		 * will return has already been truncated; the caller
+		 * will save the next line for later use if necessary. */
+		pos = endpos + 1;
+		while (*pos != '\0' && *pos != '\r' && *pos != '\n') pos++;
+		*pos_p = pos;
+		return;
+	} else if(strncmp(token, "control ", 8) == 0) {
+		token = alphanum_token(token + 8);
+		if (token != NULL) {
+			filename = concatstr(3, listdir, "/control/", token);
+			begin_new_source_file(txt, line_p, pos_p, filename);
+			myfree(filename);
+			return;
+		}
+	} else if(strncmp(token, "text ", 5) == 0) {
+		token = alphanum_token(token + 5);
+		if (token != NULL) {
+			filename = concatstr(3, listdir, "/text/", token);
+			begin_new_source_file(txt, line_p, pos_p, filename);
+			myfree(filename);
+			return;
+		}
+	}
+	if (token == NULL) {
+		/* We have encountered a directive, but not been able to deal
+		 * with it, so just advance through the string. */
+		*pos = '%';
+		*endpos = '%';
+		(*pos_p)++;
+		return;
+	}
+
+	/* No recognised directive; just advance through the string. */
+	*pos = '%';
+	*endpos = '%';
+	(*pos_p)++;
+	return;
+}
+
+
 char *get_processed_text_line(text *txt,
 		const char *listaddr, const char *listdelim,
 		size_t datacount, char **data, const char *listdir)
 {
-	char *line;
+	char *line = NULL;
+	char *pos;
 	char *tmp;
-	char *retstr;
+	source *src;
 
-	line = mygetline(txt->fd);
+	while (txt->src != NULL) {
+		if (txt->src->upcoming != NULL) {
+			if (txt->src->prefix != NULL) {
+				line = concatstr(2, txt->src->prefix, txt->src->upcoming);
+				myfree(txt->src->upcoming);
+			} else {
+				line = txt->src->upcoming;
+			}
+			txt->src->upcoming = NULL;
+			break;
+		}
+		txt->src->upcoming = mygetline(txt->src->fd);
+		if (txt->src->upcoming != NULL) continue;
+		close(txt->src->fd);
+		src = txt->src;
+		txt->src = txt->src->prev;
+		myfree(src);
+	}
 	if (line == NULL) return NULL;
 
-	chomp(line);
-
 	tmp = unistr_escaped_to_utf8(line);
 	myfree(line);
+	line = tmp;
 
-	retstr = substitute(tmp, listaddr, listdelim,
+	pos = line;
+	while (*pos != '\0') {
+		if (*pos == '\r') {
+			*pos = '\0';
+			pos++;
+			if (*pos == '\n') pos++;
+			if (*pos == '\0') break;
+			txt->src->upcoming = mystrdup(pos);
+			break;
+		} else if (*pos == '\n') {
+			*pos = '\0';
+			pos++;
+			if (*pos == '\0') break;
+			txt->src->upcoming = mystrdup(pos);
+			break;
+		} else if (*pos == '$') {
+			substitute_one(&line, &pos,
+					listaddr, listdelim,
 	                 datacount, data, listdir);
-	myfree(tmp);
+			/* The function sets up for the next character
+			 * to process, so continue straight away. */
+			continue;
+		} else if (*pos == '%') {
+			handle_directive(txt, &line, &pos, listdir);
+			/* The function sets up for the next character
+			 * to process, so continue straight away. */
+			continue;
+		}
+		pos++;
+	}
 
-	return retstr;
+	if (txt->src->suffix != NULL) {
+		tmp = concatstr(2, line, txt->src->suffix);
+		myfree(line);
+		return tmp;
+	} else {
+		return line;
+	}
 }
 
 
 void close_text(text *txt) {
-	close(txt->fd);
+	source *tmp;
+	while (txt->src != NULL) {
+		close(txt->src->fd);
+		tmp = txt->src;
+		txt->src = txt->src->prev;
+		myfree(tmp);
+	}
 }