=================================================================== RCS file: /cvs/mandoc/read.c,v retrieving revision 1.204 retrieving revision 1.219 diff -u -p -r1.204 -r1.219 --- mandoc/read.c 2018/12/14 01:24:49 1.204 +++ mandoc/read.c 2020/04/24 12:02:33 1.219 @@ -1,7 +1,7 @@ -/* $Id: read.c,v 1.204 2018/12/14 01:24:49 schwarze Exp $ */ +/* $Id: read.c,v 1.219 2020/04/24 12:02:33 schwarze Exp $ */ /* + * Copyright (c) 2010-2020 Ingo Schwarze * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons - * Copyright (c) 2010-2018 Ingo Schwarze * Copyright (c) 2010, 2012 Joerg Sonnenberger * * Permission to use, copy, modify, and distribute this software for any @@ -15,6 +15,12 @@ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Top-level functions of the mandoc(3) parser: + * Parser and input encoding selection, decompression, + * handling of input bytes, characters, lines, and files, + * handling of roff(7) loops and file inclusion, + * and steering of the various parsers. */ #include "config.h" @@ -41,13 +47,13 @@ #include "mandoc_parse.h" #include "libmandoc.h" #include "roff_int.h" +#include "tag.h" #define REPARSE_LIMIT 1000 struct mparse { struct roff *roff; /* roff parser (!NULL) */ struct roff_man *man; /* man parser */ - char *sodest; /* filename pointed to by .so */ struct buf *primary; /* buffer currently being parsed */ struct buf *secondary; /* copy of top level input */ struct buf *loop; /* open .while request line */ @@ -65,8 +71,6 @@ static void resize_buf(struct buf *, size_t); 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 void @@ -125,15 +129,15 @@ choose_parser(struct mparse *curp) } if (format == MPARSE_MDOC) { - curp->man->macroset = MACROSET_MDOC; + curp->man->meta.macroset = MACROSET_MDOC; if (curp->man->mdocmac == NULL) curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); } else { - curp->man->macroset = MACROSET_MAN; + curp->man->meta.macroset = MACROSET_MAN; if (curp->man->manmac == NULL) curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); } - curp->man->first->tok = TOKEN_NONE; + curp->man->meta.first->tok = TOKEN_NONE; } /* @@ -160,7 +164,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size ln.sz = 256; ln.buf = mandoc_malloc(ln.sz); ln.next = NULL; - firstln = loop = NULL; + firstln = lastln = loop = NULL; lnn = curp->line; pos = 0; inloop = 0; @@ -210,7 +214,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size if (c & 0x80) { if ( ! (curp->filenc && preconv_encode( &blk, &i, &ln, &pos, &curp->filenc))) { - mandoc_vmsg(MANDOCERR_CHAR_BAD, curp, + mandoc_msg(MANDOCERR_CHAR_BAD, curp->line, pos, "0x%x", c); ln.buf[pos++] = '?'; i++; @@ -223,10 +227,10 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size */ if (c == 0x7f || (c < 0x20 && c != 0x09)) { - mandoc_vmsg(c == 0x00 || c == 0x04 || + mandoc_msg(c == 0x00 || c == 0x04 || c > 0x0a ? MANDOCERR_CHAR_BAD : MANDOCERR_CHAR_UNSUPP, - curp, curp->line, pos, "0x%x", c); + curp->line, pos, "0x%x", c); i++; if (c != '\r') ln.buf[pos++] = '?'; @@ -258,6 +262,8 @@ mparse_buf_r(struct mparse *curp, struct buf blk, size /* XXX Ugly hack to mark the end of the input. */ if (i == blk.sz || blk.buf[i] == '\0') { + if (pos + 2 > ln.sz) + resize_buf(&ln, 256); ln.buf[pos++] = '\n'; ln.buf[pos] = '\0'; } @@ -300,7 +306,7 @@ rerun: if (loop == curp->loop) break; mandoc_msg(MANDOCERR_WHILE_NEST, - curp, curp->line, pos, NULL); + curp->line, pos, NULL); } curp->loop = thisln; loop = NULL; @@ -310,12 +316,12 @@ rerun: case ROFF_LOOPEXIT: if (curp->loop == NULL) { mandoc_msg(MANDOCERR_WHILE_FAIL, - curp, curp->line, pos, NULL); + curp->line, pos, NULL); break; } if (inloop == 0) { mandoc_msg(MANDOCERR_WHILE_INTO, - curp, curp->line, pos, NULL); + curp->line, pos, NULL); curp->loop = loop = NULL; break; } @@ -336,9 +342,9 @@ rerun: case ROFF_IGN: break; case ROFF_CONT: - if (curp->man->macroset == MACROSET_NONE) + if (curp->man->meta.macroset == MACROSET_NONE) choose_parser(curp); - if ((curp->man->macroset == MACROSET_MDOC ? + if ((curp->man->meta.macroset == MACROSET_MDOC ? mdoc_parseln(curp->man, curp->line, ln.buf, of) : man_parseln(curp->man, curp->line, ln.buf, of) ) == 2) @@ -350,7 +356,7 @@ rerun: if (++curp->reparse_count > REPARSE_LIMIT) { /* Abort and return to the top level. */ result = ROFF_IGN; - mandoc_msg(MANDOCERR_ROFFLOOP, curp, + mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, pos, NULL); goto out; } @@ -367,16 +373,17 @@ rerun: case ROFF_SO: if ( ! (curp->options & MPARSE_SO) && (i >= blk.sz || blk.buf[i] == '\0')) { - curp->sodest = mandoc_strdup(ln.buf + of); + curp->man->meta.sodest = + mandoc_strdup(ln.buf + of); goto out; } if ((fd = mparse_open(curp, ln.buf + of)) != -1) { mparse_readfd(curp, fd, ln.buf + of); close(fd); } else { - mandoc_vmsg(MANDOCERR_SO_FAIL, - curp, curp->line, pos, - ".so %s", ln.buf + of); + mandoc_msg(MANDOCERR_SO_FAIL, + curp->line, of, ".so %s: %s", + ln.buf + of, strerror(errno)); ln.sz = mandoc_asprintf(&cp, ".sp\nSee the file %s.\n.sp", ln.buf + of); @@ -411,7 +418,7 @@ rerun: out: if (inloop) { if (result != ROFF_USERRET) - mandoc_msg(MANDOCERR_WHILE_OUTOF, curp, + mandoc_msg(MANDOCERR_WHILE_OUTOF, curp->line, pos, NULL); curp->loop = NULL; } @@ -431,9 +438,8 @@ read_whole_file(struct mparse *curp, int fd, struct bu int gzerrnum, retval; if (fstat(fd, &st) == -1) { - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, - "fstat: %s", strerror(errno)); - return 0; + mandoc_msg(MANDOCERR_FSTAT, 0, 0, "%s", strerror(errno)); + return -1; } /* @@ -445,14 +451,14 @@ read_whole_file(struct mparse *curp, int fd, struct bu if (curp->gzip == 0 && S_ISREG(st.st_mode)) { if (st.st_size > 0x7fffffff) { - mandoc_msg(MANDOCERR_TOOLARGE, curp, 0, 0, NULL); - return 0; + mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL); + return -1; } *with_mmap = 1; fb->sz = (size_t)st.st_size; fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0); if (fb->buf != MAP_FAILED) - return 1; + return 0; } if (curp->gzip) { @@ -464,15 +470,15 @@ read_whole_file(struct mparse *curp, int fd, struct bu * which this function must not do. */ if ((fd = dup(fd)) == -1) { - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, - "dup: %s", strerror(errno)); - return 0; + mandoc_msg(MANDOCERR_DUP, 0, 0, + "%s", strerror(errno)); + return -1; } if ((gz = gzdopen(fd, "rb")) == NULL) { - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, - "gzdopen: %s", strerror(errno)); + mandoc_msg(MANDOCERR_GZDOPEN, 0, 0, + "%s", strerror(errno)); close(fd); - return 0; + return -1; } } else gz = NULL; @@ -484,14 +490,13 @@ read_whole_file(struct mparse *curp, int fd, struct bu *with_mmap = 0; off = 0; - retval = 0; + retval = -1; fb->sz = 0; fb->buf = NULL; for (;;) { if (off == fb->sz) { if (fb->sz == (1U << 31)) { - mandoc_msg(MANDOCERR_TOOLARGE, curp, - 0, 0, NULL); + mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL); break; } resize_buf(fb, 65536); @@ -501,13 +506,13 @@ read_whole_file(struct mparse *curp, int fd, struct bu read(fd, fb->buf + (int)off, fb->sz - off); if (ssz == 0) { fb->sz = off; - retval = 1; + retval = 0; break; } if (ssz == -1) { if (curp->gzip) (void)gzerror(gz, &gzerrnum); - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, "read: %s", + mandoc_msg(MANDOCERR_READ, 0, 0, "%s", curp->gzip && gzerrnum != Z_ERRNO ? zError(gzerrnum) : strerror(errno)); break; @@ -516,10 +521,10 @@ read_whole_file(struct mparse *curp, int fd, struct bu } if (curp->gzip && (gzerrnum = gzclose(gz)) != Z_OK) - mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, "gzclose: %s", + mandoc_msg(MANDOCERR_GZCLOSE, 0, 0, "%s", gzerrnum == Z_ERRNO ? strerror(errno) : zError(gzerrnum)); - if (retval == 0) { + if (retval == -1) { free(fb->buf); fb->buf = NULL; } @@ -529,35 +534,57 @@ read_whole_file(struct mparse *curp, int fd, struct bu static void mparse_end(struct mparse *curp) { - if (curp->man->macroset == MACROSET_NONE) - curp->man->macroset = MACROSET_MAN; - if (curp->man->macroset == MACROSET_MDOC) + if (curp->man->meta.macroset == MACROSET_NONE) + curp->man->meta.macroset = MACROSET_MAN; + if (curp->man->meta.macroset == MACROSET_MDOC) mdoc_endparse(curp->man); else man_endparse(curp->man); 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) { - mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, 0, NULL); + struct buf blk; + struct buf *save_primary; + const char *save_filename, *cp; + size_t offset; + int save_filenc, save_lineno; + int with_mmap; + + if (recursion_depth > 64) { + mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, 0, NULL); return; - } + } else if (recursion_depth == 0 && + (cp = strrchr(filename, '.')) != NULL && + cp[1] >= '1' && cp[1] <= '9') + curp->man->filesec = cp[1]; + else + curp->man->filesec = '\0'; - /* Line number is per-file. */ - svfile = mandoc_msg_getinfilename(); - mandoc_msg_setinfilename(file); - svprimary = curp->primary; + if (read_whole_file(curp, fd, &blk, &with_mmap) == -1) + return; + + /* + * 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 && @@ -569,45 +596,32 @@ 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; - if (svfile != NULL) - mandoc_msg_setinfilename(svfile); -} + /* + * Clean up and restore saved parent properties. + */ -/* - * 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 *file) -{ - struct buf blk; - int with_mmap; - int save_filenc; + if (with_mmap) + munmap(blk.buf, blk.sz); + else + free(blk.buf); - if (read_whole_file(curp, 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); - } + curp->primary = save_primary; + curp->filenc = save_filenc; + curp->line = save_lineno; + if (save_filename != NULL) + mandoc_msg_setinfilename(save_filename); } int mparse_open(struct mparse *curp, const char *file) { char *cp; - int fd; + int fd, save_errno; cp = strrchr(file, '.'); curp->gzip = (cp != NULL && ! strcmp(cp + 1, "gz")); @@ -623,9 +637,11 @@ mparse_open(struct mparse *curp, const char *file) */ if ( ! curp->gzip) { + save_errno = errno; mandoc_asprintf(&cp, "%s.gz", file); fd = open(cp, O_RDONLY); free(cp); + errno = save_errno; if (fd != -1) { curp->gzip = 1; return fd; @@ -634,7 +650,6 @@ mparse_open(struct mparse *curp, const char *file) /* Neither worked, give up. */ - mandoc_msg(MANDOCERR_FILE, curp, 0, 0, strerror(errno)); return -1; } @@ -648,58 +663,60 @@ mparse_alloc(int options, enum mandoc_os os_e, const c curp->options = options; curp->os_s = os_s; - curp->roff = roff_alloc(curp, options); - curp->man = roff_man_alloc(curp->roff, curp, curp->os_s, + curp->roff = roff_alloc(options); + curp->man = roff_man_alloc(curp->roff, curp->os_s, curp->options & MPARSE_QUICK ? 1 : 0); if (curp->options & MPARSE_MDOC) { - curp->man->macroset = MACROSET_MDOC; + curp->man->meta.macroset = MACROSET_MDOC; if (curp->man->mdocmac == NULL) curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX); } else if (curp->options & MPARSE_MAN) { - curp->man->macroset = MACROSET_MAN; + curp->man->meta.macroset = MACROSET_MAN; if (curp->man->manmac == NULL) curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX); } - curp->man->first->tok = TOKEN_NONE; + curp->man->meta.first->tok = TOKEN_NONE; curp->man->meta.os_e = os_e; + tag_alloc(); return curp; } void mparse_reset(struct mparse *curp) { + tag_free(); roff_reset(curp->roff); roff_man_reset(curp->man); free_buf_list(curp->secondary); curp->secondary = NULL; - free(curp->sodest); - curp->sodest = NULL; curp->gzip = 0; + tag_alloc(); } void mparse_free(struct mparse *curp) { + tag_free(); roffhash_free(curp->man->mdocmac); roffhash_free(curp->man->manmac); roff_man_free(curp->man); roff_free(curp->roff); free_buf_list(curp->secondary); - free(curp->sodest); free(curp); } -void -mparse_result(struct mparse *curp, struct roff_man **man, - char **sodest) +struct roff_meta * +mparse_result(struct mparse *curp) { - - if (sodest && NULL != (*sodest = curp->sodest)) { - *man = NULL; - return; + roff_state_reset(curp->man); + if (curp->options & MPARSE_VALIDATE) { + if (curp->man->meta.macroset == MACROSET_MDOC) + mdoc_validate(curp->man); + else + man_validate(curp->man); + tag_postprocess(curp->man, curp->man->meta.first); } - if (man) - *man = curp->man; + return &curp->man->meta; } void