diff options
Diffstat (limited to '3rd-party/xfpt/src/read.c')
-rw-r--r-- | 3rd-party/xfpt/src/read.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/3rd-party/xfpt/src/read.c b/3rd-party/xfpt/src/read.c new file mode 100644 index 000000000..4aa8757fa --- /dev/null +++ b/3rd-party/xfpt/src/read.c @@ -0,0 +1,388 @@ +/************************************************* +* xfpt - Simple ASCII->Docbook processor * +*************************************************/ + +/* Copyright (c) University of Cambridge, 2010 */ +/* Written by Philip Hazel. */ + +/* This module contains code for reading the input. */ + +#include "xfpt.h" + + + + +/************************************************* +* Process macro line * +*************************************************/ + +/* This is the place where macro arguments are substituted. In a section +delimited by .eacharg/.endeach, the variable macro_argbase is set to the first +of the relative arguments. This function is also called from para.c in order to +handle inline macro calls. + +Arguments: + p the macro input line + b where to put the result + +Returns: nothing +*/ + +void +read_process_macroline(uschar *p, uschar *b) +{ +int optend = 0; + +while (*p != 0) + { + int i; + int argn = 0; + argstr *argbase, *arg; + + /* If we are including an optional substring, when we get to the terminator, + just skip it. */ + + if (*p == optend) + { + optend = 0; + p++; + continue; + } + + /* Until we hit a dollar, just copy verbatim */ + + if (*p != '$') { *b++ = *p++; continue; } + + /* If the character after $ is another $, insert a literal $. */ + + if (p[1] == '$') { p++; *b++ = *p++; continue; } + + /* If the character after $ is +, we are dealing with arguments + relative to macro_arg0 in a ".eacharg" section. Otherwise, we are dealing + with an absolute argument number. */ + + if (p[1] == '+') + { + p++; + if (macro_argbase == NULL) /* Not in a .eacharg section */ + { + error(18); + *b++ = '+'; + *b++ = *p++; + continue; + } + argbase = macro_argbase; + } + else argbase = macrocurrent->args; + + /* $= introduces an optional substring */ + + if (p[1] == '=') + { + p++; + if (!isdigit(p[1])) + { + error(17, p[1], "$="); + *b++ = '$'; + *b++ = *p++; + continue; + } + while (isdigit(*(++p))) argn = argn * 10 + *p - '0'; + + optend = *p++; + + arg = argbase; + for (i = 1; i < argn; i++) + { + if (arg == NULL) break; + arg = arg->next; + } + + if (arg == NULL || arg->string[0] == 0) + { + while (*p != 0 && *p != optend) p++; + if (*p == optend) p++; + } + + continue; + } + + /* Not '=' after $; this is an argument substitution */ + + if (!isdigit(p[1])) + { + error(17, p[1], "$"); + *b++ = *p++; + continue; + } + + while (isdigit(*(++p))) argn = argn * 10 + *p - '0'; + + /* Handle $0 - currently no meaning */ + + if (argn == 0) + { + continue; + } + + /* Seek an argument in this invocation */ + + arg = argbase; + for (i = 1; i < argn; i++) + { + if (arg == NULL) break; + arg = arg->next; + } + + /* If not found, seek a default argument for an absolute substitution, but + not for a relative one. */ + + if (arg == NULL && argbase == macrocurrent->args) + { + arg = macrocurrent->macro->args; + for (i = 1; i < argn; i++) + { + if (arg == NULL) break; + arg = arg->next; + } + } + + /* If we have found an argument, substitute it. */ + + if (arg != NULL) b += sprintf(CS b, "%s", arg->string); + } + +*b = 0; +} + + + +/************************************************* +* Get the next line of input * +*************************************************/ + +/* There may be a saved line already in the buffer, following the reading of a +paragraph or a .nonl directive. Otherwise, take the next line from one of three +sources: + + (1) If popto is not negative, get an appropropriate line off the stack. + (2) If we are in a macro, get the next macro line. + (3) If we are in a file, read a new line from a file and handle any + continuations. + +There can be arbitrary nesting of macros and files, because a .include +directive may appear inside a macro. The current from_type vector is used to +keep track of what is current. + +Arguments: none +Returns: pointer to the next line or NULL +*/ + +uschar * +read_nextline(void) +{ +int len; +uschar *p, *q; + +/* Handle a dot line that terminated a paragraph, or a .nonl line */ + +if (next_line != NULL) + { + uschar *yield = next_line; + next_line = NULL; + return yield; + } + +/* Handle a line off the stack */ + +if (popto == 0) + { + pushstr *ps = pushed; + if (ps == NULL) error(12); else + { + popto = -1; + (void)sprintf(CS inbuffer, "%s\n", ps->string); + pushed = ps->next; + free(ps); + return inbuffer; + } + } + +/* Handle a line off the stack when there is a matching line at the top or +below for the given letter. When we reach the matching line, stop popping. The +value of popto is set greater than zero only when it is known that there's a +matching line. */ + +if (popto > 0) + { + pushstr *ps = pushed; + if (ps->letter == popto) popto = -1; + (void)sprintf(CS inbuffer, "%s\n", ps->string); + pushed = ps->next; + free(ps); + return inbuffer; + } + +/* Get the next line from the current macro or the current file. We need a loop +for handling the ends of macros and files. First check for having previously +reached the end of the input. */ + +if (from_type_ptr < 0) return NULL; + +for (;;) + { + if (from_type[from_type_ptr] == FROM_MACRO) + { + if (macrocurrent->nextline == NULL) + { + macroexe *temp = macrocurrent; + macrocurrent = macrocurrent->prev; + free(temp); + } + else + { + read_process_macroline(macrocurrent->nextline->string, inbuffer); + macrocurrent->nextline = macrocurrent->nextline->next; + break; + } + } + + /* When reading from a file, handle continuation lines, but only within the + single file. */ + + else + { + if (Ufgets(inbuffer, INBUFFSIZE, istack->file) == NULL) + { + istackstr *prev = istack->prev; + fclose(istack->file); + free(istack); + istack = prev; + } + else + { + istack->linenumber++; + + q = inbuffer; + len = Ustrlen(q); + + for (;;) + { + p = q + len; + while (p > q && isspace(p[-1])) p--; + + if (p - q < 3 || Ustrncmp(p - 3, "&&&", 3) != 0) break; + + q = p - 3; + *q = 0; + + if (istack == NULL || + Ufgets(q, INBUFFSIZE - (q - inbuffer), istack->file) == NULL) + break; + + istack->linenumber++; + p = q; + while (*p == ' ' || *p == '\t') p++; + len = Ustrlen(p); + if (p > q) memmove(q, p, len + 1); + } + + break; + } + } + + /* We get here if the end of a macro or a file was reached. The appropriate + chain has been popped. Back up the stack of input types before the loop + repeats. When we reach the end of the stack, we have reached the end of all + the input. */ + + if (--from_type_ptr < 0) return NULL; + } + +return inbuffer; +} + + + +/************************************************* +* Complete the reading of a paragraph * +*************************************************/ + +/* This function is called after a line has been identified as the start of a +paragraph. We need to read the rest so that flags can be matched across the +entire paragraph. (If there is nested material such as a footnote, this applies +only to the separate parts, not across the nesting.) The text is copied into +the paragraph buffer. Directives that are encountered in the paragraph are +processed, with two exceptions. + +(1) For .literal, we set next_line so it is processed next, and exit. This is +the end of the paragraph. + +(2) For .nest, we set *nest_info, according to whether it is the start or +end of a nested section, and exit. + +Arguments: + p the first line + nest_info returns NEST_NO, NEST_START, or NEST_END + +Returns: the paragraph +*/ + + +uschar * +read_paragraph(uschar *p, int *nest_info) +{ +uschar *q = parabuffer; +int length = Ustrlen(p); + +memcpy(q, p, length); +q += length; + +*nest_info = NEST_NO; /* Not hit .nest */ + +for (;;) + { + uschar *s; + + if ((p = read_nextline()) == NULL) break; + + if (Ustrncmp(p, ".literal ", 9) == 0) + { + next_line = p; + break; + } + + if (Ustrncmp(p, ".nest ", 6) == 0) + { + p += 6; + while (isspace(*p)) p++; + s = p + Ustrlen(p); + while (s > p && isspace(s[-1])) s--; + *s = 0; + if (Ustrcmp(p, "begin") == 0) *nest_info = NEST_BEGIN; + else if (Ustrcmp(p, "end") == 0) *nest_info = NEST_END; + else error(26, p); + break; + } + + else if (*p == '.') + { + dot_process(p); + continue; + } + + /* End paragraph on encountering a completely blank line */ + + for (s = p; *s == ' ' || *s == '\t'; s++); + if (*s == '\n') break; + + length = Ustrlen(p); + memcpy(q, p, length); + q += length; + } + +*q = 0; +return parabuffer; +} + +/* End of read.c */ |