=================================================================== RCS file: /cvs/mandoc/read.c,v retrieving revision 1.2 retrieving revision 1.86 diff -u -p -r1.2 -r1.86 --- mandoc/read.c 2011/03/20 11:43:06 1.2 +++ mandoc/read.c 2014/09/07 23:25:01 1.86 @@ -1,7 +1,8 @@ -/* $Id: read.c,v 1.2 2011/03/20 11:43:06 kristaps 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, 2011 Ingo Schwarze + * Copyright (c) 2010-2014 Ingo Schwarze + * Copyright (c) 2010, 2012 Joerg Sonnenberger * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -15,59 +16,229 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include "config.h" + +#include +#if HAVE_MMAP #include +#include +#endif +#include #include #include +#include #include +#include +#include #include #include #include #include #include "mandoc.h" +#include "mandoc_aux.h" +#include "libmandoc.h" #include "mdoc.h" #include "man.h" -#include "roff.h" +#include "main.h" -#ifndef MAP_FILE -#define MAP_FILE 0 -#endif - #define REPARSE_LIMIT 1000 struct buf { - char *buf; /* binary input buffer */ + char *buf; /* binary input buffer */ size_t sz; /* size of binary buffer */ }; struct mparse { - enum mandoclevel file_status; /* status of current parse */ - int line; /* line number in the file */ - enum mparset inttype; /* which parser to use */ 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) */ - struct regset regs; /* roff registers */ - int reparse_count; /* finite interp. stack */ + 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; /* preprocessed copy of input */ + const char *defos; /* default operating system */ mandocmsg mmsg; /* warning/error message handler */ - void *arg; /* argument to mmsg */ - mevt_open evt_open; /* file-open event */ - mevt_close evt_close; /* file-close event */ - const char *svfile; + 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 mparse_readfd_r(struct mparse *, int, const char *, int); -static void pset(const char *, int, struct mparse *); -static void pdesc(struct mparse *, const char *, int); -static int read_whole_file(const char *, int, struct buf *, int *); +static int read_whole_file(struct mparse *, const char *, 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_WARNING, + MANDOCERR_WARNING, + MANDOCERR_ERROR, + MANDOCERR_FATAL, + MANDOCERR_MAX, + MANDOCERR_MAX +}; + +static const char * const mandocerrs[MANDOCERR_MAX] = { + "ok", + + "generic warning", + + /* related to the prologue */ + "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", + "missing Os macro, using \"\"", + "duplicate prologue macro", + "late prologue macro", + "skipping late title 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\"", + "bad NAME section contents", + "sections out of conventional order", + "duplicate section title", + "unexpected section", + "AUTHORS section without An macro", + + /* related to macros and nesting */ + "obsolete macro", + "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", + ".Vt block has child macro", + "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, 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 */ + "unterminated quoted argument", + "duplicate argument", + "skipping duplicate argument", + "skipping duplicate display type", + "skipping duplicate list type", + "skipping -width argument", + "unknown AT&T UNIX version", + "invalid content in Rs block", + "invalid Boolean argument", + "unknown font, skipping request", + + /* related to plain text */ + "blank line in fill mode, using .sp", + "tab in filled text", + "whitespace at end of input line", + "bad comment style", + "invalid escape sequence", + "undefined string, using \"\"", + + "generic error", + + /* related to equations */ + "unexpected equation scope closure", + "equation scope open on exit", + "overlapping equation scopes", + "unexpected end of equation", + "equation syntax error", + + /* related to tables */ + "bad table syntax", + "bad table option", + "bad table layout", + "no table layout cells specified", + "no table data cells specified", + "ignore data in cell", + "data block still open", + "ignoring extra data cells", + + /* related to document structure and macros */ + "input stack limit exceeded, infinite loop?", + "skipping bad character", + "skipping unknown macro", + "skipping item outside list", + "skipping column outside column list", + "skipping end of block that is not open", + "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 IMPLEMENTED: Bd -file", + "NOT IMPLEMENTED: .so with absolute path or \"..\"", + ".so request failed", + + /* system errors */ + "cannot dup file descriptor", + "cannot exec", + "gunzip failed with code", + "cannot fork", + NULL, + "cannot open pipe", + "cannot read file", + "gunzip died from signal", + "cannot stat file", + "wait failed", +}; + +static const char * const mandoclevels[MANDOCLEVEL_MAX] = { + "SUCCESS", + "RESERVED", + "WARNING", + "ERROR", + "FATAL", + "BADARG", + "SYSERR" +}; + + static void resize_buf(struct buf *buf, size_t initial) { @@ -77,60 +248,54 @@ 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++; + } } - switch (curp->inttype) { - case (MPARSE_MDOC): - if (NULL == curp->pmdoc) - curp->pmdoc = mdoc_alloc - (&curp->regs, curp->arg, curp->mmsg); + if (format == MPARSE_MDOC) { + 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; - case (MPARSE_MAN): - if (NULL == curp->pman) - curp->pman = man_alloc - (&curp->regs, curp->arg, curp->mmsg); - assert(curp->pman); - curp->man = curp->pman; - return; - default: - break; } - if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) { - if (NULL == curp->pmdoc) - curp->pmdoc = mdoc_alloc - (&curp->regs, curp->arg, curp->mmsg); - 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->regs, curp->arg, curp->mmsg); + if (NULL == curp->pman) + curp->pman = man_alloc(curp->roff, curp, + MPARSE_QUICK & curp->options ? 1 : 0); assert(curp->pman); curp->man = curp->pman; } @@ -153,8 +318,8 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int memset(&ln, 0, sizeof(struct buf)); - lnn = curp->line; - pos = 0; + lnn = curp->line; + pos = 0; for (i = 0; i < (int)blk.sz; ) { if (0 == pos && '\0' == blk.buf[i]) @@ -182,30 +347,38 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int break; } - /* + /* + * Make sure we have space for at least + * one backslash and one other character + * and the trailing NUL byte. + */ + + if (pos + 2 >= (int)ln.sz) + resize_buf(&ln, 256); + + /* * Warn about bogus characters. If you're using * non-ASCII encoding, you're screwing your * readers. Since I'd rather this not happen, - * I'll be helpful and drop these characters so - * we don't display gibberish. Note to manual - * writers: use special characters. + * I'll be helpful and replace these characters + * with "?", so we don't display gibberish. + * Note to manual writers: use special characters. */ c = (unsigned char) blk.buf[i]; - if ( ! (isascii(c) && - (isgraph(c) || isblank(c)))) { - curp->mmsg(MANDOCERR_BADCHAR, curp->arg, - curp->line, pos, "ignoring byte"); + if ( ! (isascii(c) && + (isgraph(c) || isblank(c)))) { + mandoc_vmsg(MANDOCERR_BADCHAR, curp, + curp->line, pos, "0x%x", c); i++; + ln.buf[pos++] = '?'; continue; } /* Trailing backslash = a plain char. */ if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) { - if (pos >= (int)ln.sz) - resize_buf(&ln, 256); ln.buf[pos++] = blk.buf[i++]; continue; } @@ -226,7 +399,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int continue; } - if ('"' == blk.buf[i + 1]) { + if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) { i += 2; /* Comment, skip to end of line */ for (; i < (int)blk.sz; ++i) { @@ -247,16 +420,26 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int break; } - /* Some other escape sequence, copy & cont. */ + /* Catch escaped bogus characters. */ - if (pos + 1 >= (int)ln.sz) - resize_buf(&ln, 256); + c = (unsigned char) blk.buf[i+1]; + if ( ! (isascii(c) && + (isgraph(c) || isblank(c)))) { + mandoc_vmsg(MANDOCERR_BADCHAR, curp, + curp->line, pos, "0x%x", c); + i += 2; + ln.buf[pos++] = '?'; + continue; + } + + /* Some other escape sequence, copy & cont. */ + ln.buf[pos++] = blk.buf[i++]; ln.buf[pos++] = blk.buf[i++]; } - if (pos >= (int)ln.sz) + if (pos >= (int)ln.sz) resize_buf(&ln, 256); ln.buf[pos] = '\0'; @@ -272,35 +455,71 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int of = 0; + /* + * Maintain a lookaside buffer of all parsed lines. We + * only do this if mparse_keep() has been invoked (the + * buffer may be accessed with mparse_getkeep()). + */ + + if (curp->secondary) { + curp->secondary->buf = mandoc_realloc( + curp->secondary->buf, + curp->secondary->sz + pos + 2); + memcpy(curp->secondary->buf + + curp->secondary->sz, + ln.buf, pos); + curp->secondary->sz += pos; + curp->secondary->buf + [curp->secondary->sz] = '\n'; + curp->secondary->sz++; + curp->secondary->buf + [curp->secondary->sz] = '\0'; + } rerun: - rr = roff_parseln - (curp->roff, curp->line, - &ln.buf, &ln.sz, of, &of); + rr = roff_parseln(curp->roff, curp->line, + &ln.buf, &ln.sz, of, &of); switch (rr) { - case (ROFF_REPARSE): + case ROFF_REPARSE: if (REPARSE_LIMIT >= ++curp->reparse_count) mparse_buf_r(curp, ln, 0); else - curp->mmsg(MANDOCERR_ROFFLOOP, curp->arg, - curp->line, pos, NULL); + mandoc_msg(MANDOCERR_ROFFLOOP, curp, + curp->line, pos, NULL); pos = 0; continue; - case (ROFF_APPEND): + case ROFF_APPEND: pos = (int)strlen(ln.buf); continue; - case (ROFF_RERUN): + case ROFF_RERUN: goto rerun; - case (ROFF_IGN): + case ROFF_IGN: pos = 0; continue; - case (ROFF_ERR): + case ROFF_ERR: assert(MANDOCLEVEL_FATAL <= curp->file_status); break; - case (ROFF_SO): - mparse_readfd_r(curp, -1, ln.buf + of, 1); - if (MANDOCLEVEL_FATAL <= curp->file_status) + case ROFF_SO: + if (0 == (MPARSE_SO & curp->options) && + (i >= (int)blk.sz || '\0' == blk.buf[i])) { + curp->sodest = mandoc_strdup(ln.buf + of); + free(ln.buf); + return; + } + /* + * We remove `so' clauses from our lookaside + * buffer because we're going to descend into + * the file recursively. + */ + if (curp->secondary) + curp->secondary->sz -= pos + 1; + mparse_readfd(curp, -1, ln.buf + of); + if (MANDOCLEVEL_FATAL <= curp->file_status) { + mandoc_vmsg(MANDOCERR_SO_FAIL, + curp, curp->line, pos, + ".so %s", ln.buf + of); break; + } pos = 0; continue; default: @@ -317,18 +536,16 @@ rerun: /* * If input parsers have not been allocated, do so now. - * We keep these instanced betwen parsers, but set them + * We keep these instanced between parsers, but set them * locally per parse routine since we can use different * parsers with each one. */ 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 @@ -341,28 +558,29 @@ rerun: if (ROFF_TBL == rr) while (NULL != (span = roff_span(curp->roff))) { rc = curp->man ? - man_addspan(curp->man, span) : - mdoc_addspan(curp->mdoc, span); + man_addspan(curp->man, span) : + mdoc_addspan(curp->mdoc, span); if (0 == rc) break; } else if (ROFF_EQN == rr) - rc = curp->mdoc ? - mdoc_addeqn(curp->mdoc, - roff_eqn(curp->roff)) : - man_addeqn(curp->man, - roff_eqn(curp->roff)); + rc = curp->mdoc ? + mdoc_addeqn(curp->mdoc, + roff_eqn(curp->roff)) : + man_addeqn(curp->man, + roff_eqn(curp->roff)); else if (curp->man || curp->mdoc) rc = curp->man ? - man_parseln(curp->man, - curp->line, ln.buf, of) : - mdoc_parseln(curp->mdoc, - curp->line, ln.buf, of); + man_parseln(curp->man, + curp->line, ln.buf, of) : + mdoc_parseln(curp->mdoc, + curp->line, ln.buf, of); if (0 == rc) { assert(MANDOCLEVEL_FATAL <= curp->file_status); break; - } + } else if (2 == rc) + break; /* Temporary buffers typically are not full. */ @@ -377,45 +595,20 @@ rerun: free(ln.buf); } -static void -pdesc(struct mparse *curp, const char *file, int fd) -{ - struct buf blk; - int with_mmap; - - /* - * Run for each opened file; may be called more than once for - * each full parse sequence if the opened file is nested (i.e., - * from `so'). Simply sucks in the whole file and moves into - * the parse phase for the file. - */ - - if ( ! read_whole_file(file, fd, &blk, &with_mmap)) { - curp->file_status = MANDOCLEVEL_SYSERR; - return; - } - - /* Line number is per-file. */ - - curp->line = 1; - - mparse_buf_r(curp, blk, 1); - - if (with_mmap) - munmap(blk.buf, blk.sz); - else - free(blk.buf); -} - static int -read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap) +read_whole_file(struct mparse *curp, const char *file, int fd, + struct buf *fb, int *with_mmap) { - struct stat st; size_t off; ssize_t ssz; +#if HAVE_MMAP + struct stat st; if (-1 == fstat(fd, &st)) { - perror(file); + curp->file_status = MANDOCLEVEL_SYSERR; + if (curp->mmsg) + (*curp->mmsg)(MANDOCERR_SYSSTAT, curp->file_status, + file, 0, 0, strerror(errno)); return(0); } @@ -428,16 +621,19 @@ read_whole_file(const char *file, int fd, struct buf * if (S_ISREG(st.st_mode)) { if (st.st_size >= (1U << 31)) { - fprintf(stderr, "%s: input too large\n", file); + curp->file_status = MANDOCLEVEL_FATAL; + if (curp->mmsg) + (*curp->mmsg)(MANDOCERR_TOOLARGE, + curp->file_status, file, 0, 0, NULL); return(0); } *with_mmap = 1; fb->sz = (size_t)st.st_size; - fb->buf = mmap(NULL, fb->sz, PROT_READ, - MAP_FILE|MAP_SHARED, fd, 0); + fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0); if (fb->buf != MAP_FAILED) return(1); } +#endif /* * If this isn't a regular file (like, say, stdin), then we must @@ -451,7 +647,11 @@ read_whole_file(const char *file, int fd, struct buf * for (;;) { if (off == fb->sz) { if (fb->sz == (1U << 31)) { - fprintf(stderr, "%s: input too large\n", file); + curp->file_status = MANDOCLEVEL_FATAL; + if (curp->mmsg) + (*curp->mmsg)(MANDOCERR_TOOLARGE, + curp->file_status, + file, 0, 0, NULL); break; } resize_buf(fb, 65536); @@ -462,7 +662,11 @@ read_whole_file(const char *file, int fd, struct buf * return(1); } if (ssz == -1) { - perror(file); + curp->file_status = MANDOCLEVEL_SYSERR; + if (curp->mmsg) + (*curp->mmsg)(MANDOCERR_SYSREAD, + curp->file_status, file, 0, 0, + strerror(errno)); break; } off += (size_t)ssz; @@ -480,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; @@ -490,83 +707,200 @@ mparse_end(struct mparse *curp) return; } -#if 0 - /* NOTE a parser may not have been assigned, yet. */ - - if ( ! (curp->man || curp->mdoc)) { - /* FIXME: make into an mandoc.h error. */ - fprintf(stderr, "%s: Not a manual\n", curp->file); - curp->file_status = MANDOCLEVEL_FATAL; - goto cleanup; - } -#endif - roff_endparse(curp->roff); } static void -mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re) +mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file) { + struct buf *svprimary; const char *svfile; + static int recursion_depth; - if ( ! (*curp->evt_open)(curp->arg, file)) { - curp->file_status = MANDOCLEVEL_SYSERR; + if (64 < recursion_depth) { + mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, 0, NULL); return; } - if (-1 == fd) - if (-1 == (fd = open(file, O_RDONLY, 0))) { - perror(file); - curp->file_status = MANDOCLEVEL_SYSERR; - return; - } + /* Line number is per-file. */ + svfile = curp->file; + curp->file = file; + svprimary = curp->primary; + curp->primary = &blk; + curp->line = 1; + recursion_depth++; - svfile = curp->svfile; - curp->svfile = file; + mparse_buf_r(curp, blk, 1); - pdesc(curp, file, fd); - - if (0 == re && MANDOCLEVEL_FATAL > curp->file_status) + if (0 == --recursion_depth && MANDOCLEVEL_FATAL > curp->file_status) mparse_end(curp); - if (STDIN_FILENO != fd && -1 == close(fd)) - perror(file); + curp->primary = svprimary; + curp->file = svfile; +} - (*curp->evt_close)(curp->arg, svfile); - curp->svfile = svfile; +enum mandoclevel +mparse_readmem(struct mparse *curp, const void *buf, size_t len, + const char *file) +{ + struct buf blk; + + blk.buf = UNCONST(buf); + blk.sz = len; + + mparse_parse_buffer(curp, blk, file); + return(curp->file_status); } enum mandoclevel mparse_readfd(struct mparse *curp, int fd, const char *file) { + struct buf blk; + int with_mmap; - mparse_readfd_r(curp, fd, file, 0); + if (-1 == fd && -1 == (fd = open(file, O_RDONLY, 0))) { + curp->file_status = MANDOCLEVEL_SYSERR; + if (curp->mmsg) + (*curp->mmsg)(MANDOCERR_SYSOPEN, + curp->file_status, + file, 0, 0, strerror(errno)); + goto out; + } + + /* + * Run for each opened file; may be called more than once for + * each full parse sequence if the opened file is nested (i.e., + * from `so'). Simply sucks in the whole file and moves into + * the parse phase for the file. + */ + + if ( ! read_whole_file(curp, file, fd, &blk, &with_mmap)) + goto out; + + mparse_parse_buffer(curp, blk, file); + +#if HAVE_MMAP + if (with_mmap) + munmap(blk.buf, blk.sz); + else +#endif + free(blk.buf); + + if (STDIN_FILENO != fd && -1 == close(fd)) + perror(file); +out: return(curp->file_status); } -void -mparse_setstatus(struct mparse *curp, enum mandoclevel lvl) +enum mandoclevel +mparse_open(struct mparse *curp, int *fd, const char *file, + pid_t *child_pid) { + int pfd[2]; + char *cp; + enum mandocerr err; - if (curp->file_status < lvl) - curp->file_status = lvl; + 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(enum mparset inttype, mevt_open eopen, - mevt_close eclose, mandocmsg mmsg, void *arg) +mparse_alloc(int options, enum mandoclevel wlevel, + mandocmsg mmsg, const char *defos) { struct mparse *curp; + assert(wlevel <= MANDOCLEVEL_FATAL); + curp = mandoc_calloc(1, sizeof(struct mparse)); + curp->options = options; + curp->wlevel = wlevel; curp->mmsg = mmsg; - curp->arg = arg; - curp->inttype = inttype; - curp->evt_open = eopen; - curp->evt_close = eclose; + curp->defos = defos; - curp->roff = roff_alloc(&curp->regs, arg, mmsg); + 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); } @@ -574,18 +908,21 @@ void mparse_reset(struct mparse *curp) { - memset(&curp->regs, 0, sizeof(struct regset)); - roff_reset(curp->roff); if (curp->mdoc) mdoc_reset(curp->mdoc); if (curp->man) man_reset(curp->man); + if (curp->secondary) + curp->secondary->sz = 0; curp->file_status = MANDOCLEVEL_OK; curp->mdoc = NULL; curp->man = NULL; + + free(curp->sodest); + curp->sodest = NULL; } void @@ -598,14 +935,89 @@ mparse_free(struct mparse *curp) man_free(curp->pman); if (curp->roff) roff_free(curp->roff); + if (curp->secondary) + free(curp->secondary->buf); + free(curp->secondary); + free(curp->sodest); free(curp); } void -mparse_result(struct mparse *curp, struct mdoc **mdoc, struct man **man) +mparse_result(struct mparse *curp, + struct mdoc **mdoc, struct man **man, char **sodest) { - *mdoc = curp->mdoc; - *man = curp->man; + if (sodest && NULL != (*sodest = curp->sodest)) { + *mdoc = NULL; + *man = NULL; + return; + } + if (mdoc) + *mdoc = curp->mdoc; + if (man) + *man = curp->man; +} + +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; + + level = MANDOCLEVEL_FATAL; + while (er < mandoclimits[level]) + level--; + + if (level < m->wlevel) + return; + + 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 +mparse_keep(struct mparse *p) +{ + + assert(NULL == p->secondary); + p->secondary = mandoc_calloc(1, sizeof(struct buf)); +} + +const char * +mparse_getkeep(const struct mparse *p) +{ + + assert(p->secondary); + return(p->secondary->sz ? p->secondary->buf : NULL); }