=================================================================== RCS file: /cvs/mandoc/read.c,v retrieving revision 1.10 retrieving revision 1.42 diff -u -p -r1.10 -r1.42 --- mandoc/read.c 2011/04/03 10:11:25 1.10 +++ mandoc/read.c 2014/01/06 00:53:33 1.42 @@ -1,7 +1,8 @@ -/* $Id: read.c,v 1.10 2011/04/03 10:11:25 kristaps Exp $ */ +/* $Id: read.c,v 1.42 2014/01/06 00:53:33 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,13 +16,21 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include -#include +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_MMAP +# include +# include +#endif + #include #include +#include #include #include +#include #include #include #include @@ -31,11 +40,8 @@ #include "libmandoc.h" #include "mdoc.h" #include "man.h" +#include "main.h" -#ifndef MAP_FILE -#define MAP_FILE 0 -#endif - #define REPARSE_LIMIT 1000 struct buf { @@ -53,20 +59,22 @@ struct mparse { 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 */ mandocmsg mmsg; /* warning/error message handler */ - void *arg; /* argument to mmsg */ const char *file; + struct buf *secondary; + char *defos; /* default operating system */ + int quick; /* abort the parse early */ }; 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, @@ -87,6 +95,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "no title in document", "document title should be all caps", "unknown manual section", + "unknown manual volume or arch", "date missing, using today's date", "cannot parse date, using it verbatim", "prologue macros out of order", @@ -98,14 +107,14 @@ static const char * const mandocerrs[MANDOCERR_MAX] = ".so is fragile, better use ln(1)", "NAME section must come first", "bad NAME section contents", - "manual name not yet set", "sections out of conventional order", "duplicate section name", - "section not in conventional manual section", + "section header suited to sections 2, 3, and 9 only", /* related to macros and nesting */ "skipping obsolete macro", "skipping paragraph macro", + "moving paragraph macro out of list", "skipping no-space macro", "blocks badly nested", "child violates parent syntax", @@ -138,11 +147,21 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "tab in non-literal context", "end of line whitespace", "bad comment style", - "unknown escape sequence", + "bad escape sequence", "unterminated quoted string", + + /* related to equations */ + "unexpected literal in equation", "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", @@ -156,10 +175,12 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "input stack limit exceeded, infinite loop?", "skipping bad character", "escaped character not allowed in a name", + "manual name not yet set", "skipping text before the first section header", "skipping unknown macro", "NOT IMPLEMENTED, please use groff: skipping request", "argument count wrong", + "skipping column outside column list", "skipping end of block that is not open", "missing end of block", "scope open on exit", @@ -167,16 +188,17 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "macro requires line argument(s)", "macro requires body argument(s)", "macro requires argument(s)", + "request requires a numeric argument", "missing list type", "line argument(s) will be lost", "body argument(s) will be lost", "generic fatal error", + "input too large", "not a manual", "column syntax is inconsistent", "NOT IMPLEMENTED: .Bd -file", - "line scope broken, syntax violated", "argument count wrong, violates syntax", "child violates parent syntax", "argument count wrong, violates syntax", @@ -184,6 +206,11 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "no document body", "no document prologue", "static buffer exhausted", + + /* system errors */ + "cannot open file", + "cannot stat file", + "cannot read file", }; static const char * const mandoclevels[MANDOCLEVEL_MAX] = { @@ -231,13 +258,15 @@ pset(const char *buf, int pos, struct mparse *curp) switch (curp->inttype) { case (MPARSE_MDOC): if (NULL == curp->pmdoc) - curp->pmdoc = mdoc_alloc(&curp->regs, curp); + curp->pmdoc = mdoc_alloc(curp->roff, curp, + curp->defos, curp->quick); assert(curp->pmdoc); curp->mdoc = curp->pmdoc; return; case (MPARSE_MAN): if (NULL == curp->pman) - curp->pman = man_alloc(&curp->regs, curp); + curp->pman = man_alloc(curp->roff, curp, + curp->quick); assert(curp->pman); curp->man = curp->pman; return; @@ -247,14 +276,15 @@ pset(const char *buf, int pos, struct mparse *curp) if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) { if (NULL == curp->pmdoc) - curp->pmdoc = mdoc_alloc(&curp->regs, curp); + curp->pmdoc = mdoc_alloc(curp->roff, curp, + curp->defos, curp->quick); assert(curp->pmdoc); curp->mdoc = curp->pmdoc; return; } if (NULL == curp->pman) - curp->pman = man_alloc(&curp->regs, curp); + curp->pman = man_alloc(curp->roff, curp, curp->quick); assert(curp->pman); curp->man = curp->pman; } @@ -306,13 +336,22 @@ 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]; @@ -320,16 +359,15 @@ 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, "ignoring byte"); + curp->line, pos, NULL); 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; } @@ -350,7 +388,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) { @@ -371,11 +409,21 @@ 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_msg(MANDOCERR_BADCHAR, curp, + curp->line, pos, NULL); + i += 2; + ln.buf[pos++] = '?'; + continue; + } + + /* Some other escape sequence, copy & cont. */ + ln.buf[pos++] = blk.buf[i++]; ln.buf[pos++] = blk.buf[i++]; } @@ -396,6 +444,27 @@ 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, @@ -422,7 +491,14 @@ rerun: assert(MANDOCLEVEL_FATAL <= curp->file_status); break; case (ROFF_SO): - mparse_readfd_r(curp, -1, ln.buf + of, 1); + /* + * 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) break; pos = 0; @@ -441,7 +517,7 @@ 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. */ @@ -486,7 +562,8 @@ rerun: if (0 == rc) { assert(MANDOCLEVEL_FATAL <= curp->file_status); break; - } + } else if (2 == rc) + break; /* Temporary buffers typically are not full. */ @@ -501,45 +578,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; +#ifdef 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); } @@ -552,16 +604,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 @@ -575,7 +630,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); @@ -586,7 +645,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; @@ -624,41 +687,86 @@ mparse_end(struct mparse *curp) } 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) { const char *svfile; + static int recursion_depth; - if (-1 == fd) - if (-1 == (fd = open(file, O_RDONLY, 0))) { - perror(file); - curp->file_status = MANDOCLEVEL_SYSERR; - return; - } + if (64 < recursion_depth) { + mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, 0, NULL); + return; + } + /* Line number is per-file. */ svfile = curp->file; curp->file = file; + curp->line = 1; + recursion_depth++; - pdesc(curp, file, fd); + mparse_buf_r(curp, blk, 1); - 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->file = 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); + +#ifdef 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); } struct mparse * -mparse_alloc(enum mparset inttype, enum mandoclevel wlevel, mandocmsg mmsg, void *arg) +mparse_alloc(enum mparset inttype, enum mandoclevel wlevel, + mandocmsg mmsg, char *defos, int quick) { struct mparse *curp; @@ -668,10 +776,11 @@ mparse_alloc(enum mparset inttype, enum mandoclevel wl curp->wlevel = wlevel; curp->mmsg = mmsg; - curp->arg = arg; curp->inttype = inttype; + curp->defos = defos; + curp->quick = quick; - curp->roff = roff_alloc(&curp->regs, curp); + curp->roff = roff_alloc(inttype, curp); return(curp); } @@ -679,14 +788,14 @@ 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; @@ -703,7 +812,10 @@ 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); } @@ -762,4 +874,20 @@ 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); }