=================================================================== RCS file: /cvs/mandoc/read.c,v retrieving revision 1.32 retrieving revision 1.57 diff -u -p -r1.32 -r1.57 --- mandoc/read.c 2012/11/16 22:21:05 1.32 +++ mandoc/read.c 2014/07/02 13:10:45 1.57 @@ -1,7 +1,8 @@ -/* $Id: read.c,v 1.32 2012/11/16 22:21:05 schwarze Exp $ */ +/* $Id: read.c,v 1.57 2014/07/02 13:10:45 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 @@ -26,6 +27,7 @@ #include #include +#include #include #include #include @@ -35,19 +37,16 @@ #include #include "mandoc.h" +#include "mandoc_aux.h" #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 { - char *buf; /* binary input buffer */ + char *buf; /* binary input buffer */ size_t sz; /* size of binary buffer */ }; @@ -55,26 +54,28 @@ struct mparse { enum mandoclevel file_status; /* status of current parse */ enum mandoclevel wlevel; /* ignore messages below this */ int line; /* line number in the file */ - enum mparset inttype; /* which parser to use */ + 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 */ mandocmsg mmsg; /* warning/error message handler */ - void *arg; /* argument to mmsg */ - const char *file; + const char *file; struct buf *secondary; char *defos; /* default operating system */ }; 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 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, @@ -92,34 +93,38 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "generic warning", /* related to the prologue */ - "no title in document", - "document title should be all caps", + "missing .TH macro, using \"unknown 1\"", + "lower case character in document title", "unknown manual section", "unknown manual volume or arch", - "date missing, using today's date", + "missing date, using today's date", "cannot parse date, using it verbatim", "prologue macros out of order", "duplicate prologue macro", - "macro not allowed in prologue", - "macro not allowed in body", + "incomplete prologue, terminated by", + "skipping prologue macro in body", /* related to document structure */ ".so is fragile, better use ln(1)", - "NAME section must come first", + "no document body", + "content before first section header", + "first section is not \"NAME\"", "bad NAME section contents", "sections out of conventional order", - "duplicate section name", - "section not in conventional manual section", + "duplicate section title", + "unexpected section", /* related to macros and nesting */ - "skipping obsolete macro", + "obsolete macro", "skipping paragraph macro", "moving paragraph macro out of list", "skipping no-space macro", "blocks badly nested", - "child violates parent syntax", "nested displays are not portable", - "already in literal mode", + "moving content out of list", + ".Vt block has child macro", + "fill mode already enabled, skipping .fi", + "fill mode already disabled, skipping .nf", "line scope broken", /* related to missing macro arguments */ @@ -129,7 +134,6 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "list type must come first", "tag lists require a width argument", "missing font type", - "skipping end of block that is not open", /* related to bad macro arguments */ "skipping argument", @@ -150,9 +154,6 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "bad escape sequence", "unterminated quoted string", - /* related to equations */ - "unexpected literal in equation", - "generic error", /* related to equations */ @@ -176,10 +177,12 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "skipping bad character", "escaped character not allowed in a name", "manual name not yet set", - "skipping text before the first section header", + "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 column outside column list", "skipping end of block that is not open", "missing end of block", "scope open on exit", @@ -187,12 +190,13 @@ 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", @@ -200,9 +204,14 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "child violates parent syntax", "argument count wrong, violates syntax", "NOT IMPLEMENTED: .so with absolute path or \"..\"", - "no document body", + ".so request failed", "no document prologue", "static buffer exhausted", + + /* system errors */ + NULL, + "cannot stat file", + "cannot read file", }; static const char * const mandoclevels[MANDOCLEVEL_MAX] = { @@ -215,6 +224,7 @@ static const char * const mandoclevels[MANDOCLEVEL_MAX "SYSERR" }; + static void resize_buf(struct buf *buf, size_t initial) { @@ -247,35 +257,36 @@ pset(const char *buf, int pos, struct mparse *curp) return; } - switch (curp->inttype) { - case (MPARSE_MDOC): - if (NULL == curp->pmdoc) - curp->pmdoc = mdoc_alloc(curp->roff, curp, - curp->defos); + if (MPARSE_MDOC & curp->options) { + 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->roff, curp); + } 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; - default: - break; } if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) { - if (NULL == curp->pmdoc) - curp->pmdoc = mdoc_alloc(curp->roff, curp, - curp->defos); + 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; - } + } - if (NULL == curp->pman) - curp->pman = man_alloc(curp->roff, curp); + if (NULL == curp->pman) + curp->pman = man_alloc(curp->roff, curp, + MPARSE_QUICK & curp->options ? 1 : 0); assert(curp->pman); curp->man = curp->pman; } @@ -298,8 +309,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]) @@ -327,7 +338,16 @@ 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, @@ -338,13 +358,11 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int c = (unsigned char) blk.buf[i]; - if ( ! (isascii(c) && - (isgraph(c) || isblank(c)))) { + if ( ! (isascii(c) && + (isgraph(c) || isblank(c)))) { mandoc_msg(MANDOCERR_BADCHAR, curp, - curp->line, pos, NULL); + curp->line, pos, NULL); i++; - if (pos >= (int)ln.sz) - resize_buf(&ln, 256); ln.buf[pos++] = '?'; continue; } @@ -352,8 +370,6 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int /* 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; } @@ -395,16 +411,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_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++]; } - if (pos >= (int)ln.sz) + if (pos >= (int)ln.sz) resize_buf(&ln, 256); ln.buf[pos] = '\0'; @@ -427,13 +453,12 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int */ 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->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'; @@ -442,41 +467,50 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int [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 mandoc_msg(MANDOCERR_ROFFLOOP, curp, - curp->line, pos, NULL); + 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): + 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) + if (curp->secondary) curp->secondary->sz -= pos + 1; - mparse_readfd_r(curp, -1, ln.buf + of, 1); - if (MANDOCLEVEL_FATAL <= curp->file_status) + 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: @@ -501,7 +535,7 @@ rerun: if ( ! (curp->man || curp->mdoc)) pset(ln.buf + of, pos - of, curp); - /* + /* * Lastly, push down into the parsers themselves. One * of these will have already been set in the pset() * routine. @@ -517,28 +551,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. */ @@ -554,7 +589,8 @@ rerun: } 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) { size_t off; ssize_t ssz; @@ -562,7 +598,10 @@ read_whole_file(const char *file, int fd, struct buf * #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); } @@ -575,13 +614,15 @@ 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); } @@ -599,7 +640,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); @@ -610,7 +655,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; @@ -638,8 +687,8 @@ mparse_end(struct mparse *curp) return; } - if ( ! (curp->man || curp->mdoc)) { - mandoc_msg(MANDOCERR_NOTMANUAL, curp, 1, 0, NULL); + if ( ! (curp->mdoc || curp->man || curp->sodest)) { + mandoc_msg(MANDOCERR_NOTMANUAL, curp, 0, 0, NULL); curp->file_status = MANDOCLEVEL_FATAL; return; } @@ -648,19 +697,25 @@ mparse_end(struct mparse *curp) } static void -mparse_parse_buffer(struct mparse *curp, struct buf blk, 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 (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++; 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); curp->file = svfile; @@ -675,22 +730,25 @@ mparse_readmem(struct mparse *curp, const void *buf, s blk.buf = UNCONST(buf); blk.sz = len; - mparse_parse_buffer(curp, blk, file, 0); + mparse_parse_buffer(curp, blk, file); return(curp->file_status); } -static void -mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re) +enum mandoclevel +mparse_readfd(struct mparse *curp, int fd, const char *file) { struct buf blk; int with_mmap; - if (-1 == fd) - if (-1 == (fd = open(file, O_RDONLY, 0))) { - perror(file); - curp->file_status = MANDOCLEVEL_SYSERR; - return; - } + 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., @@ -698,12 +756,10 @@ mparse_readfd_r(struct mparse *curp, int fd, const cha * the parse phase for the file. */ - if ( ! read_whole_file(file, fd, &blk, &with_mmap)) { - curp->file_status = MANDOCLEVEL_SYSERR; - return; - } + if ( ! read_whole_file(curp, file, fd, &blk, &with_mmap)) + goto out; - mparse_parse_buffer(curp, blk, file, re); + mparse_parse_buffer(curp, blk, file); #ifdef HAVE_MMAP if (with_mmap) @@ -714,19 +770,13 @@ mparse_readfd_r(struct mparse *curp, int fd, const cha if (STDIN_FILENO != fd && -1 == close(fd)) perror(file); -} - -enum mandoclevel -mparse_readfd(struct mparse *curp, int fd, const char *file) -{ - - mparse_readfd_r(curp, fd, file, 0); +out: return(curp->file_status); } struct mparse * -mparse_alloc(enum mparset inttype, enum mandoclevel wlevel, - mandocmsg mmsg, void *arg, char *defos) +mparse_alloc(int options, enum mandoclevel wlevel, + mandocmsg mmsg, char *defos) { struct mparse *curp; @@ -734,13 +784,12 @@ mparse_alloc(enum mparset inttype, enum mandoclevel wl curp = mandoc_calloc(1, sizeof(struct mparse)); + curp->options = options; curp->wlevel = wlevel; curp->mmsg = mmsg; - curp->arg = arg; - curp->inttype = inttype; curp->defos = defos; - curp->roff = roff_alloc(curp); + curp->roff = roff_alloc(curp, options); return(curp); } @@ -760,6 +809,9 @@ mparse_reset(struct mparse *curp) curp->file_status = MANDOCLEVEL_OK; curp->mdoc = NULL; curp->man = NULL; + + free(curp->sodest); + curp->sodest = NULL; } void @@ -776,13 +828,20 @@ mparse_free(struct mparse *curp) 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) { + if (sodest && NULL != (*sodest = curp->sodest)) { + *mdoc = NULL; + *man = NULL; + return; + } if (mdoc) *mdoc = curp->mdoc; if (man) @@ -797,14 +856,14 @@ mandoc_vmsg(enum mandocerr t, struct mparse *m, va_list ap; va_start(ap, fmt); - vsnprintf(buf, sizeof(buf) - 1, fmt, ap); + (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, +mandoc_msg(enum mandocerr er, struct mparse *m, int ln, int col, const char *msg) { enum mandoclevel level;