=================================================================== RCS file: /cvs/mandoc/read.c,v retrieving revision 1.59 retrieving revision 1.86 diff -u -p -r1.59 -r1.86 --- mandoc/read.c 2014/07/03 23:24:56 1.59 +++ mandoc/read.c 2014/09/07 23:25:01 1.86 @@ -1,4 +1,4 @@ -/* $Id: read.c,v 1.59 2014/07/03 23:24:56 schwarze Exp $ */ +/* $Id: read.c,v 1.86 2014/09/07 23:25:01 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2010-2014 Ingo Schwarze @@ -16,14 +16,14 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_MMAP -# include -# include +#include +#if HAVE_MMAP +#include +#include #endif +#include #include #include @@ -51,26 +51,27 @@ struct buf { }; struct mparse { - enum mandoclevel file_status; /* status of current parse */ - enum mandoclevel wlevel; /* ignore messages below this */ - int line; /* line number in the file */ - int options; /* parser options */ struct man *pman; /* persistent man parser */ struct mdoc *pmdoc; /* persistent mdoc parser */ struct man *man; /* man parser */ struct mdoc *mdoc; /* mdoc parser */ struct roff *roff; /* roff parser (!NULL) */ char *sodest; /* filename pointed to by .so */ - int reparse_count; /* finite interp. stack */ + const char *file; /* filename of current input file */ + struct buf *primary; /* buffer currently being parsed */ + struct buf *secondary; /* preprocessed copy of input */ + const char *defos; /* default operating system */ mandocmsg mmsg; /* warning/error message handler */ - const char *file; - struct buf *secondary; - char *defos; /* default operating system */ + enum mandoclevel file_status; /* status of current parse */ + enum mandoclevel wlevel; /* ignore messages below this */ + int options; /* parser options */ + int reparse_count; /* finite interp. stack */ + int line; /* line number in the file */ }; +static void choose_parser(struct mparse *); static void resize_buf(struct buf *, size_t); static void mparse_buf_r(struct mparse *, struct buf, int); -static void pset(const char *, int, struct mparse *); static int read_whole_file(struct mparse *, const char *, int, struct buf *, int *); static void mparse_end(struct mparse *); @@ -93,16 +94,19 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "generic warning", /* related to the prologue */ - "missing .TH macro, using \"unknown 1\"", + "missing manual title, using UNTITLED", + "missing manual title, using \"\"", "lower case character in document title", + "missing manual section, using \"\"", "unknown manual section", "unknown manual volume or arch", "missing date, using today's date", "cannot parse date, using it verbatim", - "prologue macros out of order", + "missing Os macro, using \"\"", "duplicate prologue macro", - "incomplete prologue, terminated by", - "skipping prologue macro in body", + "late prologue macro", + "skipping late title macro", + "prologue macros out of order", /* related to document structure */ ".so is fragile, better use ln(1)", @@ -113,6 +117,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "sections out of conventional order", "duplicate section title", "unexpected section", + "AUTHORS section without An macro", /* related to macros and nesting */ "obsolete macro", @@ -123,39 +128,45 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "nested displays are not portable", "moving content out of list", ".Vt block has child macro", - "fill mode already enabled, skipping .fi", - "fill mode already disabled, skipping .nf", + "fill mode already enabled, skipping", + "fill mode already disabled, skipping", "line scope broken", /* related to missing macro arguments */ "skipping empty request", "conditional request controls empty scope", "skipping empty macro", + "empty argument, using 0n", "argument count wrong", - "missing display type", - "list type must come first", - "tag lists require a width argument", - "missing head in list item", - "missing font type", + "missing display type, using -ragged", + "list type is not the first argument", + "missing -width in -tag list, using 8n", + "missing utility name, using \"\"", + "empty head in list item", + "empty list item", + "missing font type, using \\fR", + "unknown font type, using \\fR", + "missing -std argument, adding it", /* related to bad macro arguments */ - "skipping argument", + "unterminated quoted argument", "duplicate argument", - "duplicate display type", - "duplicate list type", + "skipping duplicate argument", + "skipping duplicate display type", + "skipping duplicate list type", + "skipping -width argument", "unknown AT&T UNIX version", - "bad Boolean value", - "unknown font", - "unknown standard specifier", - "bad width argument", + "invalid content in Rs block", + "invalid Boolean argument", + "unknown font, skipping request", /* related to plain text */ - "blank line in non-literal context", - "tab in non-literal context", - "end of line whitespace", + "blank line in fill mode, using .sp", + "tab in filled text", + "whitespace at end of input line", "bad comment style", - "bad escape sequence", - "unterminated quoted string", + "invalid escape sequence", + "undefined string, using \"\"", "generic error", @@ -176,44 +187,45 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "data block still open", "ignoring extra data cells", + /* related to document structure and macros */ "input stack limit exceeded, infinite loop?", "skipping bad character", - "escaped character not allowed in a name", - "manual name not yet set", - "skipping text before first section header", "skipping unknown macro", - "NOT IMPLEMENTED, please use groff: skipping request", - "argument count wrong", - "skipping invalid content in .Rs block", + "skipping item outside list", "skipping column outside column list", "skipping end of block that is not open", - "missing end of block", - "scope open on exit", - "uname(3) system call failed", - "macro requires body argument(s)", - "macro requires argument(s)", - "request requires a numeric argument", - "missing list type", - "line argument(s) will be lost", + "inserting missing end of block", + "appending missing end of block", + /* related to request and macro arguments */ + "escaped character not allowed in a name", + "argument count wrong", + "missing list type, using -item", + "missing manual name, using \"\"", + "uname(3) system call failed, using UNKNOWN", + "unknown standard specifier", + "skipping request without numeric argument", + "skipping all arguments", + "skipping excess arguments", + "generic fatal error", "input too large", - "not a manual", - "column syntax is inconsistent", - "NOT IMPLEMENTED: .Bd -file", - "argument count wrong, violates syntax", - "child violates parent syntax", - "argument count wrong, violates syntax", + "NOT IMPLEMENTED: Bd -file", "NOT IMPLEMENTED: .so with absolute path or \"..\"", ".so request failed", - "no document prologue", - "static buffer exhausted", /* system errors */ + "cannot dup file descriptor", + "cannot exec", + "gunzip failed with code", + "cannot fork", NULL, - "cannot stat file", + "cannot open pipe", "cannot read file", + "gunzip died from signal", + "cannot stat file", + "wait failed", }; static const char * const mandoclevels[MANDOCLEVEL_MAX] = { @@ -236,30 +248,40 @@ resize_buf(struct buf *buf, size_t initial) } static void -pset(const char *buf, int pos, struct mparse *curp) +choose_parser(struct mparse *curp) { - int i; + char *cp, *ep; + int format; /* - * Try to intuit which kind of manual parser should be used. If - * passed in by command-line (-man, -mdoc), then use that - * explicitly. If passed as -mandoc, then try to guess from the - * line: either skip dot-lines, use -mdoc when finding `.Dt', or - * default to -man, which is more lenient. - * - * Separate out pmdoc/pman from mdoc/man: the first persists - * through all parsers, while the latter is used per-parse. + * If neither command line arguments -mdoc or -man select + * a parser nor the roff parser found a .Dd or .TH macro + * yet, look ahead in the main input buffer. */ - if ('.' == buf[0] || '\'' == buf[0]) { - for (i = 1; buf[i]; i++) - if (' ' != buf[i] && '\t' != buf[i]) + if ((format = roff_getformat(curp->roff)) == 0) { + cp = curp->primary->buf; + ep = cp + curp->primary->sz; + while (cp < ep) { + if (*cp == '.' || *cp == '\'') { + cp++; + if (cp[0] == 'D' && cp[1] == 'd') { + format = MPARSE_MDOC; + break; + } + if (cp[0] == 'T' && cp[1] == 'H') { + format = MPARSE_MAN; + break; + } + } + cp = memchr(cp, '\n', ep - cp); + if (cp == NULL) break; - if ('\0' == buf[i]) - return; + cp++; + } } - if (MPARSE_MDOC & curp->options) { + if (format == MPARSE_MDOC) { if (NULL == curp->pmdoc) curp->pmdoc = mdoc_alloc( curp->roff, curp, curp->defos, @@ -267,24 +289,9 @@ pset(const char *buf, int pos, struct mparse *curp) assert(curp->pmdoc); curp->mdoc = curp->pmdoc; return; - } else if (MPARSE_MAN & curp->options) { - if (NULL == curp->pman) - curp->pman = man_alloc(curp->roff, curp, - MPARSE_QUICK & curp->options ? 1 : 0); - assert(curp->pman); - curp->man = curp->pman; - return; } - if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) { - if (NULL == curp->pmdoc) - curp->pmdoc = mdoc_alloc( - curp->roff, curp, curp->defos, - MPARSE_QUICK & curp->options ? 1 : 0); - assert(curp->pmdoc); - curp->mdoc = curp->pmdoc; - return; - } + /* Fall back to man(7) as a last resort. */ if (NULL == curp->pman) curp->pman = man_alloc(curp->roff, curp, @@ -362,8 +369,8 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int if ( ! (isascii(c) && (isgraph(c) || isblank(c)))) { - mandoc_msg(MANDOCERR_BADCHAR, curp, - curp->line, pos, NULL); + mandoc_vmsg(MANDOCERR_BADCHAR, curp, + curp->line, pos, "0x%x", c); i++; ln.buf[pos++] = '?'; continue; @@ -419,8 +426,8 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int if ( ! (isascii(c) && (isgraph(c) || isblank(c)))) { - mandoc_msg(MANDOCERR_BADCHAR, curp, - curp->line, pos, NULL); + mandoc_vmsg(MANDOCERR_BADCHAR, curp, + curp->line, pos, "0x%x", c); i += 2; ln.buf[pos++] = '?'; continue; @@ -535,12 +542,10 @@ rerun: */ if ( ! (curp->man || curp->mdoc)) - pset(ln.buf + of, pos - of, curp); + choose_parser(curp); /* - * Lastly, push down into the parsers themselves. One - * of these will have already been set in the pset() - * routine. + * Lastly, push down into the parsers themselves. * If libroff returns ROFF_TBL, then add it to the * currently open parse. Since we only get here if * there does exist data (see tbl_data.c), we're @@ -597,7 +602,7 @@ read_whole_file(struct mparse *curp, const char *file, size_t off; ssize_t ssz; -#ifdef HAVE_MMAP +#if HAVE_MMAP struct stat st; if (-1 == fstat(fd, &st)) { curp->file_status = MANDOCLEVEL_SYSERR; @@ -679,6 +684,19 @@ mparse_end(struct mparse *curp) if (MANDOCLEVEL_FATAL <= curp->file_status) return; + if (curp->mdoc == NULL && + curp->man == NULL && + curp->sodest == NULL) { + if (curp->options & MPARSE_MDOC) + curp->mdoc = curp->pmdoc; + else { + if (curp->pman == NULL) + curp->pman = man_alloc(curp->roff, curp, + curp->options & MPARSE_QUICK ? 1 : 0); + curp->man = curp->pman; + } + } + if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) { assert(MANDOCLEVEL_FATAL <= curp->file_status); return; @@ -689,18 +707,13 @@ mparse_end(struct mparse *curp) return; } - if ( ! (curp->mdoc || curp->man || curp->sodest)) { - mandoc_msg(MANDOCERR_NOTMANUAL, curp, 0, 0, NULL); - curp->file_status = MANDOCLEVEL_FATAL; - return; - } - roff_endparse(curp->roff); } static void mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file) { + struct buf *svprimary; const char *svfile; static int recursion_depth; @@ -712,6 +725,8 @@ mparse_parse_buffer(struct mparse *curp, struct buf bl /* Line number is per-file. */ svfile = curp->file; curp->file = file; + svprimary = curp->primary; + curp->primary = &blk; curp->line = 1; recursion_depth++; @@ -720,6 +735,7 @@ mparse_parse_buffer(struct mparse *curp, struct buf bl if (0 == --recursion_depth && MANDOCLEVEL_FATAL > curp->file_status) mparse_end(curp); + curp->primary = svprimary; curp->file = svfile; } @@ -763,7 +779,7 @@ mparse_readfd(struct mparse *curp, int fd, const char mparse_parse_buffer(curp, blk, file); -#ifdef HAVE_MMAP +#if HAVE_MMAP if (with_mmap) munmap(blk.buf, blk.sz); else @@ -776,9 +792,94 @@ out: return(curp->file_status); } +enum mandoclevel +mparse_open(struct mparse *curp, int *fd, const char *file, + pid_t *child_pid) +{ + int pfd[2]; + char *cp; + enum mandocerr err; + + pfd[1] = -1; + curp->file = file; + if ((cp = strrchr(file, '.')) == NULL || + strcmp(cp + 1, "gz")) { + *child_pid = 0; + if ((*fd = open(file, O_RDONLY)) == -1) { + err = MANDOCERR_SYSOPEN; + goto out; + } + return(MANDOCLEVEL_OK); + } + + if (pipe(pfd) == -1) { + err = MANDOCERR_SYSPIPE; + goto out; + } + + switch (*child_pid = fork()) { + case -1: + err = MANDOCERR_SYSFORK; + close(pfd[0]); + close(pfd[1]); + pfd[1] = -1; + break; + case 0: + close(pfd[0]); + if (dup2(pfd[1], STDOUT_FILENO) == -1) { + err = MANDOCERR_SYSDUP; + break; + } + execlp("gunzip", "gunzip", "-c", file, NULL); + err = MANDOCERR_SYSEXEC; + break; + default: + close(pfd[1]); + *fd = pfd[0]; + return(MANDOCLEVEL_OK); + } + +out: + *fd = -1; + *child_pid = 0; + curp->file_status = MANDOCLEVEL_SYSERR; + if (curp->mmsg) + (*curp->mmsg)(err, curp->file_status, file, + 0, 0, strerror(errno)); + if (pfd[1] != -1) + exit(1); + return(curp->file_status); +} + +enum mandoclevel +mparse_wait(struct mparse *curp, pid_t child_pid) +{ + int status; + + if (waitpid(child_pid, &status, 0) == -1) { + mandoc_msg(MANDOCERR_SYSWAIT, curp, 0, 0, + strerror(errno)); + curp->file_status = MANDOCLEVEL_SYSERR; + return(curp->file_status); + } + if (WIFSIGNALED(status)) { + mandoc_vmsg(MANDOCERR_SYSSIG, curp, 0, 0, + "%d", WTERMSIG(status)); + curp->file_status = MANDOCLEVEL_SYSERR; + return(curp->file_status); + } + if (WEXITSTATUS(status)) { + mandoc_vmsg(MANDOCERR_SYSEXIT, curp, 0, 0, + "%d", WEXITSTATUS(status)); + curp->file_status = MANDOCLEVEL_SYSERR; + return(curp->file_status); + } + return(MANDOCLEVEL_OK); +} + struct mparse * mparse_alloc(int options, enum mandoclevel wlevel, - mandocmsg mmsg, char *defos) + mandocmsg mmsg, const char *defos) { struct mparse *curp; @@ -792,6 +893,14 @@ mparse_alloc(int options, enum mandoclevel wlevel, curp->defos = defos; curp->roff = roff_alloc(curp, options); + if (curp->options & MPARSE_MDOC) + curp->pmdoc = mdoc_alloc( + curp->roff, curp, curp->defos, + curp->options & MPARSE_QUICK ? 1 : 0); + if (curp->options & MPARSE_MAN) + curp->pman = man_alloc(curp->roff, curp, + curp->options & MPARSE_QUICK ? 1 : 0); + return(curp); }