# HG changeset patch # User Ben Schmidt # Date 1326709757 -39600 # Node ID 99fb6ba7a76ea5ca9ca8a79f321e9dd9f9606b17 # Parent 8479195595aff4dd43be7f367064c1374a01efcb Add %%, %^%, %comment%, %control C%, %text T% formatting directives. To support inclusion, use a stack of sources when reading list texts. diff -r 8479195595af -r 99fb6ba7a76e ChangeLog --- 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 diff -r 8479195595af -r 99fb6ba7a76e README.listtexts --- 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 diff -r 8479195595af -r 99fb6ba7a76e src/prepstdreply.c --- 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); + } }