=================================================================== RCS file: /cvs/mandoc/read.c,v retrieving revision 1.198 retrieving revision 1.205 diff -u -p -r1.198 -r1.205 --- mandoc/read.c 2018/08/23 19:33:27 1.198 +++ mandoc/read.c 2018/12/14 02:16:21 1.205 @@ -1,4 +1,4 @@ -/* $Id: read.c,v 1.198 2018/08/23 19:33:27 schwarze Exp $ */ +/* $Id: read.c,v 1.205 2018/12/14 02:16:21 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2010-2018 Ingo Schwarze @@ -38,7 +38,9 @@ #include "roff.h" #include "mdoc.h" #include "man.h" +#include "mandoc_parse.h" #include "libmandoc.h" +#include "roff_int.h" #define REPARSE_LIMIT 1000 @@ -46,13 +48,10 @@ struct mparse { struct roff *roff; /* roff parser (!NULL) */ struct roff_man *man; /* man parser */ char *sodest; /* filename pointed to by .so */ - const char *file; /* filename of current input file */ struct buf *primary; /* buffer currently being parsed */ struct buf *secondary; /* copy of top level input */ + struct buf *loop; /* open .while request line */ const char *os_s; /* default operating system */ - mandocmsg mmsg; /* warning/error message handler */ - enum mandoclevel file_status; /* status of current parse */ - enum mandocerr mmin; /* ignore messages below this */ int options; /* parser options */ int gzip; /* current input file is gzipped */ int filenc; /* encoding of the current file */ @@ -63,225 +62,11 @@ struct mparse { static void choose_parser(struct mparse *); static void free_buf_list(struct buf *); static void resize_buf(struct buf *, size_t); -static enum rofferr mparse_buf_r(struct mparse *, struct buf, size_t, int); -static int read_whole_file(struct mparse *, const char *, int, - struct buf *, int *); +static int mparse_buf_r(struct mparse *, struct buf, size_t, int); +static int read_whole_file(struct mparse *, int, struct buf *, int *); static void mparse_end(struct mparse *); -static void mparse_parse_buffer(struct mparse *, struct buf, - const char *); -static const enum mandocerr mandoclimits[MANDOCLEVEL_MAX] = { - MANDOCERR_OK, - MANDOCERR_OK, - MANDOCERR_WARNING, - MANDOCERR_ERROR, - MANDOCERR_UNSUPP, - MANDOCERR_MAX, - MANDOCERR_MAX -}; -static const char * const mandocerrs[MANDOCERR_MAX] = { - "ok", - - "base system convention", - - "Mdocdate found", - "Mdocdate missing", - "unknown architecture", - "operating system explicitly specified", - "RCS id missing", - "referenced manual not found", - - "generic style suggestion", - - "legacy man(7) date format", - "normalizing date format to", - "lower case character in document title", - "duplicate RCS id", - "possible typo in section name", - "unterminated quoted argument", - "useless macro", - "consider using OS macro", - "errnos out of order", - "duplicate errno", - "trailing delimiter", - "no blank before trailing delimiter", - "fill mode already enabled, skipping", - "fill mode already disabled, skipping", - "verbatim \"--\", maybe consider using \\(em", - "function name without markup", - "whitespace at end of input line", - "bad comment style", - - "generic warning", - - /* related to the prologue */ - "missing manual title, using UNTITLED", - "missing manual title, using \"\"", - "missing manual section, using \"\"", - "unknown manual section", - "missing date, using today's date", - "cannot parse date, using it verbatim", - "date in the future, using it anyway", - "missing Os macro, using \"\"", - "late prologue macro", - "prologue macros out of order", - - /* related to document structure */ - ".so is fragile, better use ln(1)", - "no document body", - "content before first section header", - "first section is not \"NAME\"", - "NAME section without Nm before Nd", - "NAME section without description", - "description not at the end of NAME", - "bad NAME section content", - "missing comma before name", - "missing description line, using \"\"", - "description line outside NAME section", - "sections out of conventional order", - "duplicate section title", - "unexpected section", - "cross reference to self", - "unusual Xr order", - "unusual Xr punctuation", - "AUTHORS section without An macro", - - /* related to macros and nesting */ - "obsolete macro", - "macro neither callable nor escaped", - "skipping paragraph macro", - "moving paragraph macro out of list", - "skipping no-space macro", - "blocks badly nested", - "nested displays are not portable", - "moving content out of list", - "first macro on line", - "line scope broken", - "skipping blank line in line scope", - - /* related to missing macro arguments */ - "skipping empty request", - "conditional request controls empty scope", - "skipping empty macro", - "empty block", - "empty argument, using 0n", - "missing display type, using -ragged", - "list type is not the first argument", - "missing -width in -tag list, using 6n", - "missing utility name, using \"\"", - "missing function name, using \"\"", - "empty head in list item", - "empty list item", - "missing argument, using next line", - "missing font type, using \\fR", - "unknown font type, using \\fR", - "nothing follows prefix", - "empty reference block", - "missing section argument", - "missing -std argument, adding it", - "missing option string, using \"\"", - "missing resource identifier, using \"\"", - "missing eqn box, using \"\"", - - /* related to bad macro arguments */ - "duplicate argument", - "skipping duplicate argument", - "skipping duplicate display type", - "skipping duplicate list type", - "skipping -width argument", - "wrong number of cells", - "unknown AT&T UNIX version", - "comma in function argument", - "parenthesis in function name", - "unknown library name", - "invalid content in Rs block", - "invalid Boolean argument", - "unknown font, skipping request", - "odd number of characters in request", - - /* related to plain text */ - "blank line in fill mode, using .sp", - "tab in filled text", - "new sentence, new line", - "invalid escape sequence", - "undefined string, using \"\"", - - /* related to tables */ - "tbl line starts with span", - "tbl column starts with span", - "skipping vertical bar in tbl layout", - - "generic error", - - /* related to tables */ - "non-alphabetic character in tbl options", - "skipping unknown tbl option", - "missing tbl option argument", - "wrong tbl option argument size", - "empty tbl layout", - "invalid character in tbl layout", - "unmatched parenthesis in tbl layout", - "tbl without any data cells", - "ignoring data in spanned tbl cell", - "ignoring extra tbl data cells", - "data block open at end of tbl", - - /* related to document structure and macros */ - NULL, - "duplicate prologue macro", - "skipping late title macro", - "input stack limit exceeded, infinite loop?", - "skipping bad character", - "skipping unknown macro", - "ignoring request outside macro", - "skipping insecure request", - "skipping item outside list", - "skipping column outside column list", - "skipping end of block that is not open", - "fewer RS blocks open, skipping", - "inserting missing end of block", - "appending missing end of block", - - /* related to request and macro arguments */ - "escaped character not allowed in a name", - "using macro argument outside macro", - "argument number is not numeric", - "NOT IMPLEMENTED: Bd -file", - "skipping display without arguments", - "missing list type, using -item", - "argument is not numeric, using 1", - "missing manual name, using \"\"", - "uname(3) system call failed, using UNKNOWN", - "unknown standard specifier", - "skipping request without numeric argument", - "excessive shift", - "NOT IMPLEMENTED: .so with absolute path or \"..\"", - ".so request failed", - "skipping all arguments", - "skipping excess arguments", - "divide by zero", - - "unsupported feature", - "input too large", - "unsupported control character", - "unsupported roff request", - "eqn delim option in tbl", - "unsupported tbl layout modifier", - "ignoring macro in table", -}; - -static const char * const mandoclevels[MANDOCLEVEL_MAX] = { - "SUCCESS", - "STYLE", - "WARNING", - "ERROR", - "UNSUPP", - "BADARG", - "SYSERR" -}; - - static void resize_buf(struct buf *buf, size_t initial) { @@ -356,32 +141,30 @@ choose_parser(struct mparse *curp) * macros, inline equations, and input line traps) * and indirectly (for .so file inclusion). */ -static enum rofferr +static int mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) { struct buf ln; - struct buf *firstln, *lastln, *thisln; - const char *save_file; + struct buf *firstln, *lastln, *thisln, *loop; char *cp; size_t pos; /* byte number in the ln buffer */ - enum rofferr line_result, result; + int line_result, result; int of; int lnn; /* line number in the real file */ int fd; + int inloop; /* Saw .while on this level. */ unsigned char c; ln.sz = 256; ln.buf = mandoc_malloc(ln.sz); ln.next = NULL; - firstln = NULL; + firstln = loop = NULL; lnn = curp->line; pos = 0; + inloop = 0; result = ROFF_CONT; - while (i < blk.sz) { - if (0 == pos && '\0' == blk.buf[i]) - break; - + while (i < blk.sz && (blk.buf[i] != '\0' || pos != 0)) { if (start) { curp->line = lnn; curp->reparse_count = 0; @@ -490,54 +273,105 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size rerun: line_result = roff_parseln(curp->roff, curp->line, &ln, &of); - switch (line_result) { + /* Process options. */ + + if (line_result & ROFF_APPEND) + assert(line_result == (ROFF_IGN | ROFF_APPEND)); + + if (line_result & ROFF_USERCALL) + assert((line_result & ROFF_MASK) == ROFF_REPARSE); + + if (line_result & ROFF_USERRET) { + assert(line_result == (ROFF_IGN | ROFF_USERRET)); + if (start == 0) { + /* Return from the current macro. */ + result = ROFF_USERRET; + goto out; + } + } + + switch (line_result & ROFF_LOOPMASK) { + case ROFF_IGN: + break; + case ROFF_WHILE: + if (curp->loop != NULL) { + if (loop == curp->loop) + break; + mandoc_msg(MANDOCERR_WHILE_NEST, + curp, curp->line, pos, NULL); + } + curp->loop = thisln; + loop = NULL; + inloop = 1; + break; + case ROFF_LOOPCONT: + case ROFF_LOOPEXIT: + if (curp->loop == NULL) { + mandoc_msg(MANDOCERR_WHILE_FAIL, + curp, curp->line, pos, NULL); + break; + } + if (inloop == 0) { + mandoc_msg(MANDOCERR_WHILE_INTO, + curp, curp->line, pos, NULL); + curp->loop = loop = NULL; + break; + } + if (line_result & ROFF_LOOPCONT) + loop = curp->loop; + else { + curp->loop = loop = NULL; + inloop = 0; + } + break; + default: + abort(); + } + + /* Process the main instruction from the roff parser. */ + + switch (line_result & ROFF_MASK) { + case ROFF_IGN: + break; + case ROFF_CONT: + if (curp->man->macroset == MACROSET_NONE) + choose_parser(curp); + if ((curp->man->macroset == MACROSET_MDOC ? + mdoc_parseln(curp->man, curp->line, ln.buf, of) : + man_parseln(curp->man, curp->line, ln.buf, of) + ) == 2) + goto out; + break; + case ROFF_RERUN: + goto rerun; case ROFF_REPARSE: - case ROFF_USERCALL: if (++curp->reparse_count > REPARSE_LIMIT) { + /* Abort and return to the top level. */ result = ROFF_IGN; mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, pos, NULL); - } else { - result = mparse_buf_r(curp, ln, of, 0); - if (line_result == ROFF_USERCALL) { - if (result == ROFF_USERRET) - result = ROFF_CONT; - roff_userret(curp->roff); - } - if (start || result == ROFF_CONT) { - pos = 0; - continue; - } + goto out; } - goto out; - case ROFF_USERRET: - if (start) { - pos = 0; - continue; + result = mparse_buf_r(curp, ln, of, 0); + if (line_result & ROFF_USERCALL) { + roff_userret(curp->roff); + /* Continue normally. */ + if (result & ROFF_USERRET) + result = ROFF_CONT; } - result = ROFF_USERRET; - goto out; - case ROFF_APPEND: - pos = strlen(ln.buf); - continue; - case ROFF_RERUN: - goto rerun; - case ROFF_IGN: - pos = 0; - continue; + if (start == 0 && result != ROFF_CONT) + goto out; + break; case ROFF_SO: if ( ! (curp->options & MPARSE_SO) && (i >= blk.sz || blk.buf[i] == '\0')) { curp->sodest = mandoc_strdup(ln.buf + of); goto out; } - save_file = curp->file; if ((fd = mparse_open(curp, ln.buf + of)) != -1) { mparse_readfd(curp, fd, ln.buf + of); close(fd); - curp->file = save_file; } else { - curp->file = save_file; mandoc_vmsg(MANDOCERR_SO_FAIL, curp, curp->line, pos, ".so %s", ln.buf + of); @@ -549,30 +383,36 @@ rerun: of = 0; mparse_buf_r(curp, ln, of, 0); } - pos = 0; - continue; - default: break; + default: + abort(); } - if (curp->man->macroset == MACROSET_NONE) - choose_parser(curp); + /* Start the next input line. */ - if ((curp->man->macroset == MACROSET_MDOC ? - mdoc_parseln(curp->man, curp->line, ln.buf, of) : - man_parseln(curp->man, curp->line, ln.buf, of)) == 2) - break; + if (loop != NULL && + (line_result & ROFF_LOOPMASK) == ROFF_IGN) + loop = loop->next; - /* Temporary buffers typically are not full. */ + if (loop != NULL) { + if ((line_result & ROFF_APPEND) == 0) + *ln.buf = '\0'; + if (ln.sz < loop->sz) + resize_buf(&ln, loop->sz); + (void)strlcat(ln.buf, loop->buf, ln.sz); + of = 0; + goto rerun; + } - if (0 == start && '\0' == blk.buf[i]) - break; - - /* Start the next input line. */ - - pos = 0; + pos = (line_result & ROFF_APPEND) ? strlen(ln.buf) : 0; } out: + if (inloop) { + if (result != ROFF_USERRET) + mandoc_msg(MANDOCERR_WHILE_OUTOF, curp, + curp->line, pos, NULL); + curp->loop = NULL; + } free(ln.buf); if (firstln != curp->secondary) free_buf_list(firstln); @@ -580,8 +420,7 @@ out: } static int -read_whole_file(struct mparse *curp, const char *file, int fd, - struct buf *fb, int *with_mmap) +read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap) { struct stat st; gzFile gz; @@ -697,26 +536,42 @@ mparse_end(struct mparse *curp) roff_endparse(curp->roff); } -static void -mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file) +/* + * Read the whole file into memory and call the parsers. + * Called recursively when an .so request is encountered. + */ +void +mparse_readfd(struct mparse *curp, int fd, const char *filename) { - struct buf *svprimary; - const char *svfile; - size_t offset; static int recursion_depth; - if (64 < recursion_depth) { + struct buf blk; + struct buf *save_primary; + const char *save_filename; + size_t offset; + int save_filenc, save_lineno; + int with_mmap; + + if (recursion_depth > 64) { mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, 0, NULL); return; } + if (read_whole_file(curp, fd, &blk, &with_mmap) == 0) + return; - /* Line number is per-file. */ - svfile = curp->file; - curp->file = file; - svprimary = curp->primary; + /* + * Save some properties of the parent file. + */ + + save_primary = curp->primary; + save_filenc = curp->filenc; + save_lineno = curp->line; + save_filename = mandoc_msg_getinfilename(); + curp->primary = &blk; + curp->filenc = curp->options & (MPARSE_UTF8 | MPARSE_LATIN1); curp->line = 1; - recursion_depth++; + mandoc_msg_setinfilename(filename); /* Skip an UTF-8 byte order mark. */ if (curp->filenc & MPARSE_UTF8 && blk.sz > 2 && @@ -728,60 +583,33 @@ mparse_parse_buffer(struct mparse *curp, struct buf bl } else offset = 0; + recursion_depth++; mparse_buf_r(curp, blk, offset, 1); - if (--recursion_depth == 0) mparse_end(curp); - curp->primary = svprimary; - curp->file = svfile; -} + /* + * Clean up and restore saved parent properties. + */ -enum mandoclevel -mparse_readmem(struct mparse *curp, void *buf, size_t len, - const char *file) -{ - struct buf blk; + if (with_mmap) + munmap(blk.buf, blk.sz); + else + free(blk.buf); - blk.buf = buf; - blk.sz = len; - - mparse_parse_buffer(curp, blk, file); - return curp->file_status; + curp->primary = save_primary; + curp->filenc = save_filenc; + curp->line = save_lineno; + if (save_filename != NULL) + mandoc_msg_setinfilename(save_filename); } -/* - * Read the whole file into memory and call the parsers. - * Called recursively when an .so request is encountered. - */ -enum mandoclevel -mparse_readfd(struct mparse *curp, int fd, const char *file) -{ - struct buf blk; - int with_mmap; - int save_filenc; - - if (read_whole_file(curp, file, fd, &blk, &with_mmap)) { - save_filenc = curp->filenc; - curp->filenc = curp->options & - (MPARSE_UTF8 | MPARSE_LATIN1); - mparse_parse_buffer(curp, blk, file); - curp->filenc = save_filenc; - if (with_mmap) - munmap(blk.buf, blk.sz); - else - free(blk.buf); - } - return curp->file_status; -} - int mparse_open(struct mparse *curp, const char *file) { char *cp; int fd; - curp->file = file; cp = strrchr(file, '.'); curp->gzip = (cp != NULL && ! strcmp(cp + 1, "gz")); @@ -812,16 +640,13 @@ mparse_open(struct mparse *curp, const char *file) } struct mparse * -mparse_alloc(int options, enum mandocerr mmin, mandocmsg mmsg, - enum mandoc_os os_e, const char *os_s) +mparse_alloc(int options, enum mandoc_os os_e, const char *os_s) { struct mparse *curp; curp = mandoc_calloc(1, sizeof(struct mparse)); curp->options = options; - curp->mmin = mmin; - curp->mmsg = mmsg; curp->os_s = os_s; curp->roff = roff_alloc(curp, options); @@ -848,11 +673,8 @@ mparse_reset(struct mparse *curp) roff_man_reset(curp->man); free_buf_list(curp->secondary); curp->secondary = NULL; - free(curp->sodest); curp->sodest = NULL; - - curp->file_status = MANDOCLEVEL_OK; curp->gzip = 0; } @@ -879,60 +701,6 @@ mparse_result(struct mparse *curp, struct roff_man **m } if (man) *man = curp->man; -} - -void -mparse_updaterc(struct mparse *curp, enum mandoclevel *rc) -{ - if (curp->file_status > *rc) - *rc = curp->file_status; -} - -void -mandoc_vmsg(enum mandocerr t, struct mparse *m, - int ln, int pos, const char *fmt, ...) -{ - char buf[256]; - va_list ap; - - va_start(ap, fmt); - (void)vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - mandoc_msg(t, m, ln, pos, buf); -} - -void -mandoc_msg(enum mandocerr er, struct mparse *m, - int ln, int col, const char *msg) -{ - enum mandoclevel level; - - if (er < m->mmin && er != MANDOCERR_FILE) - return; - - level = MANDOCLEVEL_UNSUPP; - while (er < mandoclimits[level]) - level--; - - if (m->mmsg) - (*m->mmsg)(er, level, m->file, ln, col, msg); - - if (m->file_status < level) - m->file_status = level; -} - -const char * -mparse_strerror(enum mandocerr er) -{ - - return mandocerrs[er]; -} - -const char * -mparse_strlevel(enum mandoclevel lvl) -{ - return mandoclevels[lvl]; } void