diff options
Diffstat (limited to '3rd-party/xfpt/src/para.c')
-rw-r--r-- | 3rd-party/xfpt/src/para.c | 385 |
1 files changed, 385 insertions, 0 deletions
diff --git a/3rd-party/xfpt/src/para.c b/3rd-party/xfpt/src/para.c new file mode 100644 index 000000000..4f00a981d --- /dev/null +++ b/3rd-party/xfpt/src/para.c @@ -0,0 +1,385 @@ +/************************************************* +* xfpt - Simple ASCII->Docbook processor * +*************************************************/ + +/* Copyright (c) University of Cambridge, 2008 */ +/* Written by Philip Hazel. */ + +/* This module contains code for processing a paragraph by looking for flag +characters and also dealing with literals that must be escaped. */ + +#include "xfpt.h" + + +/************************************************* +* Process an inline macro call * +*************************************************/ + +/* This function is called when we encounter & followed by a name and an +opening parenthesis. This signifies an inline macro call. + +Arguments: + p points to the start of the macro name + q points to the opening parenthesis + +Returns: updated value for p to continue processing +*/ + +static uschar * +para_macro_process(uschar *p, uschar *q) +{ +int length = q - p; +argstr **pp; +macrodef *md; +macroexe *me; + +for (md = macrolist; md != NULL; md = md->next) + { + if (length == md->namelength && Ustrncmp(p, md->name, length) == 0) break; + } + +if (md == NULL) + { + error(23, length, p); + (void)fprintf(outfile, "&"); + return p; + } + +/* Set up the macro and its arguments on the input stack, just as we do for a +macro called as a directive, though the arguments are comma-separated here. */ + +me = misc_malloc(sizeof(macroexe)); +me->prev = macrocurrent; +macrocurrent = me; +me->macro = md; +me->nextline = md->lines; +from_type[++from_type_ptr] = FROM_MACRO; + +me->args = NULL; +pp = &(me->args); + +while (isspace(*(++q))); +while (*q != 0 && *q != ')') + { + argstr *as = misc_malloc(sizeof(argstr)); + as->next = NULL; + *pp = as; + pp = &(as->next); + as->string = misc_readitem(q, US",)", &length, NULL, 0); + q += length; + if (*q == ',') while (isspace(*(++q))); + } + +if (*q != ')') + { + error(24, p); + (void)fprintf(outfile, "&"); + return p; + } + +/* Bump the count indicating that we are in an inline macro, and then process +the lines of the macro. It's a count rather than a flag, because the macro data +may also reference inline macros. Each line is processed and output, but +without the terminating newline. */ + +para_inline_macro++; + +for (;;) + { + uschar buffer[INBUFFSIZE]; + + read_process_macroline(macrocurrent->nextline->string, buffer); + + /* A directive such as .eacharg can skip to the end of the macro if there + is no .endeach. Detect this by looking for a change of macrocurrent value, + because there may be an enclosing macro. */ + + if (*buffer == '.') + { + dot_process(buffer); + if (macrocurrent != me) break; + } + + /* Process a data line */ + + else + { + uschar *qq = buffer + Ustrlen(buffer); + while (qq > buffer && isspace(qq[-1])) qq--; + *qq = 0; + para_process(buffer); + } + + /* Advance to the next macro line, exiting the loop when we hit the + end of the macro. */ + + macrocurrent->nextline = macrocurrent->nextline->next; + if (macrocurrent->nextline == NULL) + { + macroexe *temp = macrocurrent; + macrocurrent = macrocurrent->prev; + free(temp); + from_type_ptr--; + break; + } + } + +/* Unstack one level of inline macro, and return the position to carry on +from in the original input. */ + +para_inline_macro--; +return q + 1; +} + + + + +/************************************************* +* Check a flag string for literal * +*************************************************/ + +/* This function is called to scan flag replacement strings to check for +<literal> and <literal/> so that we can avoid messing with single quotes in +literal text. + +Arguments: + s the flag string + b a boolean that is set TRUE, FALSE, or left alone + +Returns: nothing +*/ + +static void +check_literal(uschar *s, BOOL *b) +{ +while (*s != 0) + { + s = Ustrchr(s, '<'); + if (s == NULL) return; + + if (Ustrncmp(s, "<literal", 8) == 0 && (s[8] == '>' || isspace(s[8]))) + *b = TRUE; + else if (Ustrncmp(s, "</literal", 9) == 0 && (s[9] == '>' || isspace(s[9]))) + *b = FALSE; + + while (*s != 0 && *s != '>') + { + if (*s == '"' || *s == '\'') + { + int t = *s++; + while (*s != 0 && *s != t) s++; + if (*s == 0) return; + } + s++; + } + + if (*s++ == 0) return; + } +} + + + +/************************************************* +* Process a paragraph * +*************************************************/ + +/* This is used both for a complete paragraph that may consist of many lines, +and for literal layout lines that nevertheless need to be scanned for flags. +However, it is not used for literal text. + +Argument: the text to be processed +Returns: nothing +*/ + +void +para_process(uschar *p) +{ +flagstr *f; +flagstr *fstack[FLAGSTACKSIZE]; +int fstackcount = 0; +BOOL inliteraltext = FALSE; + +while (*p != 0) + { + int c, i; + + /* Check for the closing flag sequence for any outstanding flag pairs. If we + find one that isn't at the top of the stack, there's a nesting error. */ + + for (i = fstackcount - 1; i >= 0; i--) + { + f = fstack[i]; + if (Ustrncmp(f->flag2, p, f->length2) == 0) + { + int j; + for (j = i + 1; j < fstackcount; j++) + error(8, fstack[j]->flag2, f->flag2); + fstackcount = i; + (void)fprintf(outfile, "%s", CS f->rep2); + check_literal(f->rep2, &inliteraltext); + p += f->length2; + i = fstackcount; /* Reset in case another follows immediately */ + continue; + } + } + + /* We may be at the end of string if we've just passed a closing flag + sequence. */ + + if (*p == 0) break; + + /* Otherwise, scan character by character. Angle brackets are escaped, + single quotes are mapped except in literal text, and then everything other + than ampersand is treated literally. */ + + c = *p++; + if (c == '<') { (void)fprintf(outfile, "<"); continue; } + if (c == '>') { (void)fprintf(outfile, ">"); continue; } + + if (!inliteraltext) + { + if (c == '`') + { + (void)fprintf(outfile, "‘"); + continue; + } + + if (c == '\'') + { + (void)fprintf(outfile, "’"); + continue; + } + } + + if (c != '&') { (void)fputc(c, outfile); continue; } + + /* Ampersand must be followed by something. */ + + if (*p == 0 || *p == '\n') + { + error(25); + continue; + } + + /* Handle all the fancy stuff that starts with ampersand. First, all the + cases where a letter is next. */ + + if (isalpha(*p)) + { + int entlen; + uschar *q = p + 1; + while (isalnum(*q) || *q == '.') q++; + + /* Check for an inline macro call; handle out-of line as the code is + non-trivial. */ + + if (*q == '(') + { + p = para_macro_process(p, q); + continue; + } + + /* Otherwise, if it is not XML entity reference syntax there's an error. We + support some special entities that start with "&xfpt." for inserting local + data. We also allow local entities to be defined. If we don't recognize an + entity name, it is passed through untouched, assuming it is a defined XML + entity. */ + + entlen = q - p; + + if (*q != ';') + { + error (3, entlen, p); + (void)fprintf(outfile, "&"); + continue; + } + + /* This special provides support for the .revision directive. */ + + if (Ustrncmp(p, "xfpt.rev", entlen) == 0) + { + if (revision != NULL && *revision != 0) + (void)fprintf(outfile, " revisionflag=\"%s\"", revision); + } + + /* Search for a locally defined entitity */ + + else + { + tree_node *t; + *q = 0; + t = tree_search(entities, p); + *q = ';'; + if (t != NULL) + (void)fprintf(outfile, "%s", CS t->data); + else + (void)fprintf(outfile, "&%.*s;", entlen, p); + } + + if (*q == ';') q++; + p = q; + continue; + } + + /* Ampersand followed by # might be an XML numerical entity. If not, we fall + through in case it's a flag. */ + + if (*p == '#') + { + uschar *q = p + 1; + if (isdigit(*q)) + { + for (q++; isdigit(*q); q++); + if (*q == ';') + { + (void)fprintf(outfile, "&%.*s", q - p, p); + p = q; + continue; + } + } + + else if (*q == 'x') + { + for (q++; isxdigit(*q); q++); + if (*q == ';') + { + (void)fprintf(outfile, "&%.*s", q - p, p); + p = q; + continue; + } + } + } + + /* If not an XML entity, search out defined flag sequences */ + + for (f = flaglist; f != NULL; f = f->next) + { if (Ustrncmp(p, f->flag1, f->length1) == 0) break; } + + if (f == NULL) + { + error(6, *p); + (void)fprintf(outfile, "&"); + continue; + } + + /* If the flag is part of a pair, put it onto a stack. Then write out the + replacement for the first flag, and move past the flag characters. */ + + if (f->length2 != 0) fstack[fstackcount++] = f; + (void)fprintf(outfile, "%s", CS f->rep1); + check_literal(f->rep1, &inliteraltext); + p += f->length1; + } + +/* If there is anything left on the stack at the end of the string, there is a +missing flag partner. */ + +while (fstackcount > 0) + { + f = fstack[--fstackcount]; + error(7, f->flag2); + } +} + + +/* End of para.c */ |