=================================================================== RCS file: /cvs/mandoc/read.c,v retrieving revision 1.93 retrieving revision 1.108 diff -u -p -r1.93 -r1.108 --- mandoc/read.c 2014/10/25 01:03:52 1.93 +++ mandoc/read.c 2015/01/14 22:02:50 1.108 @@ -1,7 +1,7 @@ -/* $Id: read.c,v 1.93 2014/10/25 01:03:52 schwarze Exp $ */ +/* $Id: read.c,v 1.108 2015/01/14 22:02:50 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons - * Copyright (c) 2010-2014 Ingo Schwarze + * Copyright (c) 2010-2015 Ingo Schwarze * Copyright (c) 2010, 2012 Joerg Sonnenberger * * Permission to use, copy, modify, and distribute this software for any @@ -41,7 +41,6 @@ #include "libmandoc.h" #include "mdoc.h" #include "man.h" -#include "main.h" #define REPARSE_LIMIT 1000 @@ -51,6 +50,7 @@ struct mparse { struct man *man; /* man parser */ struct mdoc *mdoc; /* mdoc parser */ struct roff *roff; /* roff parser (!NULL) */ + const struct mchars *mchars; /* character table */ char *sodest; /* filename pointed to by .so */ const char *file; /* filename of current input file */ struct buf *primary; /* buffer currently being parsed */ @@ -63,11 +63,12 @@ struct mparse { int filenc; /* encoding of the current file */ int reparse_count; /* finite interp. stack */ int line; /* line number in the file */ + pid_t child; /* the gunzip(1) process */ }; 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_buf_r(struct mparse *, struct buf, size_t, int); static int read_whole_file(struct mparse *, const char *, int, struct buf *, int *); static void mparse_end(struct mparse *); @@ -95,7 +96,6 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "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 \"\"", @@ -119,6 +119,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = /* related to macros and nesting */ "obsolete macro", + "macro neither callable nor escaped", "skipping paragraph macro", "moving paragraph macro out of list", "skipping no-space macro", @@ -144,6 +145,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "empty list item", "missing font type, using \\fR", "unknown font type, using \\fR", + "nothing follows prefix", "missing -std argument, adding it", "missing eqn box, using \"\"", @@ -186,8 +188,10 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "ignore data in cell", "data block still open", "ignoring extra data cells", + "ignoring macro in table", /* related to document structure and macros */ + NULL, "input stack limit exceeded, infinite loop?", "skipping bad character", "skipping unknown macro", @@ -200,6 +204,7 @@ static const char * const mandocerrs[MANDOCERR_MAX] = /* related to request and macro arguments */ "escaped character not allowed in a name", "argument count wrong", + "NOT IMPLEMENTED: Bd -file", "missing list type, using -item", "missing manual name, using \"\"", "uname(3) system call failed, using UNKNOWN", @@ -212,21 +217,8 @@ static const char * const mandocerrs[MANDOCERR_MAX] = "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] = { @@ -295,34 +287,37 @@ choose_parser(struct mparse *curp) /* Fall back to man(7) as a last resort. */ if (NULL == curp->pman) - curp->pman = man_alloc(curp->roff, curp, + curp->pman = man_alloc( + curp->roff, curp, curp->defos, MPARSE_QUICK & curp->options ? 1 : 0); assert(curp->pman); curp->man = curp->pman; } /* - * Main parse routine for an opened file. This is called for each - * opened file and simply loops around the full input file, possibly - * nesting (i.e., with `so'). + * Main parse routine for a buffer. + * It assumes encoding and line numbering are already set up. + * It can recurse directly (for invocations of user-defined + * macros, inline equations, and input line traps) + * and indirectly (for .so file inclusion). */ static void -mparse_buf_r(struct mparse *curp, struct buf blk, int start) +mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start) { const struct tbl_span *span; struct buf ln; + size_t pos; /* byte number in the ln buffer */ enum rofferr rr; - int i, of, rc; - int pos; /* byte number in the ln buffer */ + int of; int lnn; /* line number in the real file */ unsigned char c; - memset(&ln, 0, sizeof(struct buf)); + memset(&ln, 0, sizeof(ln)); lnn = curp->line; pos = 0; - for (i = blk.offs; i < (int)blk.sz; ) { + while (i < blk.sz) { if (0 == pos && '\0' == blk.buf[i]) break; @@ -332,13 +327,11 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int if (lnn < 3 && curp->filenc & MPARSE_UTF8 && - curp->filenc & MPARSE_LATIN1) { - blk.offs = i; - curp->filenc = preconv_cue(&blk); - } + curp->filenc & MPARSE_LATIN1) + curp->filenc = preconv_cue(&blk, i); } - while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) { + while (i < blk.sz && (start || blk.buf[i] != '\0')) { /* * When finding an unescaped newline character, @@ -346,7 +339,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int * Skip a preceding carriage return, if any. */ - if ('\r' == blk.buf[i] && i + 1 < (int)blk.sz && + if ('\r' == blk.buf[i] && i + 1 < blk.sz && '\n' == blk.buf[i + 1]) ++i; if ('\n' == blk.buf[i]) { @@ -360,7 +353,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int * case of 11 bytes: "\\[u10ffff]\0" */ - if (pos + 11 > (int)ln.sz) + if (pos + 11 > ln.sz) resize_buf(&ln, 256); /* @@ -369,13 +362,8 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int c = blk.buf[i]; if (c & 0x80) { - blk.offs = i; - ln.offs = pos; - if (curp->filenc && preconv_encode( - &blk, &ln, &curp->filenc)) { - pos = ln.offs; - i = blk.offs; - } else { + if ( ! (curp->filenc && preconv_encode( + &blk, &i, &ln, &pos, &curp->filenc))) { mandoc_vmsg(MANDOCERR_BADCHAR, curp, curp->line, pos, "0x%x", c); @@ -399,7 +387,7 @@ 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 (blk.buf[i] != '\\' || i + 1 == blk.sz) { ln.buf[pos++] = blk.buf[i++]; continue; } @@ -411,7 +399,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int * skip that one as well. */ - if ('\r' == blk.buf[i + 1] && i + 2 < (int)blk.sz && + if ('\r' == blk.buf[i + 1] && i + 2 < blk.sz && '\n' == blk.buf[i + 2]) ++i; if ('\n' == blk.buf[i + 1]) { @@ -423,7 +411,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) { i += 2; /* Comment, skip to end of line */ - for (; i < (int)blk.sz; ++i) { + for (; i < blk.sz; ++i) { if ('\n' == blk.buf[i]) { ++i; ++lnn; @@ -460,7 +448,7 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int ln.buf[pos++] = blk.buf[i++]; } - if (pos >= (int)ln.sz) + if (pos >= ln.sz) resize_buf(&ln, 256); ln.buf[pos] = '\0'; @@ -497,20 +485,19 @@ 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, &of); switch (rr) { case ROFF_REPARSE: if (REPARSE_LIMIT >= ++curp->reparse_count) - mparse_buf_r(curp, ln, 0); + mparse_buf_r(curp, ln, of, 0); else mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, pos, NULL); pos = 0; continue; case ROFF_APPEND: - pos = (int)strlen(ln.buf); + pos = strlen(ln.buf); continue; case ROFF_RERUN: goto rerun; @@ -521,8 +508,8 @@ rerun: assert(MANDOCLEVEL_FATAL <= curp->file_status); break; case ROFF_SO: - if (0 == (MPARSE_SO & curp->options) && - (i >= (int)blk.sz || '\0' == blk.buf[i])) { + if ( ! (curp->options & MPARSE_SO) && + (i >= blk.sz || blk.buf[i] == '\0')) { curp->sodest = mandoc_strdup(ln.buf + of); free(ln.buf); return; @@ -574,35 +561,22 @@ rerun: * Do the same for ROFF_EQN. */ - rc = -1; + if (rr == ROFF_TBL) { + while ((span = roff_span(curp->roff)) != NULL) + if (curp->man == NULL) + mdoc_addspan(curp->mdoc, span); + else + man_addspan(curp->man, span); + } else if (rr == ROFF_EQN) { + if (curp->man == NULL) + mdoc_addeqn(curp->mdoc, roff_eqn(curp->roff)); + else + man_addeqn(curp->man, roff_eqn(curp->roff)); + } else if ((curp->man == NULL ? + mdoc_parseln(curp->mdoc, curp->line, ln.buf, of) : + man_parseln(curp->man, curp->line, ln.buf, of)) == 2) + break; - if (ROFF_TBL == rr) - while (NULL != (span = roff_span(curp->roff))) { - rc = curp->man ? - 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)); - 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); - - if (0 == rc) { - assert(MANDOCLEVEL_FATAL <= curp->file_status); - break; - } else if (2 == rc) - break; - /* Temporary buffers typically are not full. */ if (0 == start && '\0' == blk.buf[i]) @@ -626,11 +600,8 @@ read_whole_file(struct mparse *curp, const char *file, #if HAVE_MMAP struct stat st; if (-1 == fstat(fd, &st)) { - curp->file_status = MANDOCLEVEL_SYSERR; - if (curp->mmsg) - (*curp->mmsg)(MANDOCERR_SYSSTAT, curp->file_status, - file, 0, 0, strerror(errno)); - return(0); + perror(file); + exit((int)MANDOCLEVEL_SYSERR); } /* @@ -649,7 +620,6 @@ read_whole_file(struct mparse *curp, const char *file, return(0); } *with_mmap = 1; - fb->offs = 0; fb->sz = (size_t)st.st_size; fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0); if (fb->buf != MAP_FAILED) @@ -681,16 +651,11 @@ read_whole_file(struct mparse *curp, const char *file, ssz = read(fd, fb->buf + (int)off, fb->sz - off); if (ssz == 0) { fb->sz = off; - fb->offs = 0; return(1); } if (ssz == -1) { - curp->file_status = MANDOCLEVEL_SYSERR; - if (curp->mmsg) - (*curp->mmsg)(MANDOCERR_SYSREAD, - curp->file_status, file, 0, 0, - strerror(errno)); - break; + perror(file); + exit((int)MANDOCLEVEL_SYSERR); } off += (size_t)ssz; } @@ -714,7 +679,8 @@ mparse_end(struct mparse *curp) curp->mdoc = curp->pmdoc; else { if (curp->pman == NULL) - curp->pman = man_alloc(curp->roff, curp, + curp->pman = man_alloc( + curp->roff, curp, curp->defos, curp->options & MPARSE_QUICK ? 1 : 0); curp->man = curp->pman; } @@ -738,6 +704,7 @@ mparse_parse_buffer(struct mparse *curp, struct buf bl { struct buf *svprimary; const char *svfile; + size_t offset; static int recursion_depth; if (64 < recursion_depth) { @@ -758,11 +725,12 @@ mparse_parse_buffer(struct mparse *curp, struct buf bl (unsigned char)blk.buf[0] == 0xef && (unsigned char)blk.buf[1] == 0xbb && (unsigned char)blk.buf[2] == 0xbf) { - blk.offs = 3; + offset = 3; curp->filenc &= ~MPARSE_LATIN1; - } + } else + offset = 0; - mparse_buf_r(curp, blk, 1); + mparse_buf_r(curp, blk, offset, 1); if (0 == --recursion_depth && MANDOCLEVEL_FATAL > curp->file_status) mparse_end(curp); @@ -772,42 +740,38 @@ mparse_parse_buffer(struct mparse *curp, struct buf bl } enum mandoclevel -mparse_readmem(struct mparse *curp, const void *buf, size_t len, +mparse_readmem(struct mparse *curp, void *buf, size_t len, const char *file) { struct buf blk; - blk.buf = UNCONST(buf); + blk.buf = buf; blk.sz = len; - blk.offs = 0; mparse_parse_buffer(curp, blk, file); return(curp->file_status); } +/* + * If a file descriptor is given, use it and assume it points + * to the named file. Otherwise, open the named file. + * Read the whole file into memory and call the parsers. + * Called recursively when an .so request is encountered. + */ enum mandoclevel mparse_readfd(struct mparse *curp, int fd, const char *file) { struct buf blk; int with_mmap; int save_filenc; + pid_t save_child; - 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)); - return(curp->file_status); - } + save_child = curp->child; + if (fd != -1) + curp->child = 0; + else if (mparse_open(curp, &fd, file) != MANDOCLEVEL_OK) + 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)) { save_filenc = curp->filenc; curp->filenc = curp->options & @@ -822,100 +786,107 @@ mparse_readfd(struct mparse *curp, int fd, const char free(blk.buf); } - if (STDIN_FILENO != fd && -1 == close(fd)) + if (fd != STDIN_FILENO && close(fd) == -1) perror(file); + mparse_wait(curp); +out: + curp->child = save_child; return(curp->file_status); } enum mandoclevel -mparse_open(struct mparse *curp, int *fd, const char *file, - pid_t *child_pid) +mparse_open(struct mparse *curp, int *fd, const char *file) { int pfd[2]; + int save_errno; char *cp; - enum mandocerr err; - pfd[1] = -1; curp->file = file; + + /* Unless zipped, try to just open the 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); + curp->child = 0; + if ((*fd = open(file, O_RDONLY)) != -1) + return(MANDOCLEVEL_OK); + + /* Open failed; try to append ".gz". */ + + mandoc_asprintf(&cp, "%s.gz", file); + file = cp; + } else + cp = NULL; + + /* Before forking, make sure the file can be read. */ + + save_errno = errno; + if (access(file, R_OK) == -1) { + if (cp != NULL) + errno = save_errno; + free(cp); + *fd = -1; + curp->child = 0; + mandoc_msg(MANDOCERR_FILE, curp, 0, 0, strerror(errno)); + return(MANDOCLEVEL_ERROR); } + /* Run gunzip(1). */ + if (pipe(pfd) == -1) { - err = MANDOCERR_SYSPIPE; - goto out; + perror("pipe"); + exit((int)MANDOCLEVEL_SYSERR); } - switch (*child_pid = fork()) { + switch (curp->child = fork()) { case -1: - err = MANDOCERR_SYSFORK; - close(pfd[0]); - close(pfd[1]); - pfd[1] = -1; - break; + perror("fork"); + exit((int)MANDOCLEVEL_SYSERR); case 0: close(pfd[0]); if (dup2(pfd[1], STDOUT_FILENO) == -1) { - err = MANDOCERR_SYSDUP; - break; + perror("dup"); + exit((int)MANDOCLEVEL_SYSERR); } execlp("gunzip", "gunzip", "-c", file, NULL); - err = MANDOCERR_SYSEXEC; - break; + perror("exec"); + exit((int)MANDOCLEVEL_SYSERR); 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) +mparse_wait(struct mparse *curp) { 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 (curp->child == 0) + return(MANDOCLEVEL_OK); + + if (waitpid(curp->child, &status, 0) == -1) { + perror("wait"); + exit((int)MANDOCLEVEL_SYSERR); } if (WIFSIGNALED(status)) { - mandoc_vmsg(MANDOCERR_SYSSIG, curp, 0, 0, - "%d", WTERMSIG(status)); - curp->file_status = MANDOCLEVEL_SYSERR; - return(curp->file_status); + mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, + "gunzip died from signal %d", WTERMSIG(status)); + return(MANDOCLEVEL_ERROR); } if (WEXITSTATUS(status)) { - mandoc_vmsg(MANDOCERR_SYSEXIT, curp, 0, 0, - "%d", WEXITSTATUS(status)); - curp->file_status = MANDOCLEVEL_SYSERR; - return(curp->file_status); + mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0, + "gunzip failed with code %d", WEXITSTATUS(status)); + return(MANDOCLEVEL_ERROR); } return(MANDOCLEVEL_OK); } struct mparse * -mparse_alloc(int options, enum mandoclevel wlevel, - mandocmsg mmsg, const char *defos) +mparse_alloc(int options, enum mandoclevel wlevel, mandocmsg mmsg, + const struct mchars *mchars, const char *defos) { struct mparse *curp; @@ -928,13 +899,15 @@ mparse_alloc(int options, enum mandoclevel wlevel, curp->mmsg = mmsg; curp->defos = defos; - curp->roff = roff_alloc(curp, options); + curp->mchars = mchars; + curp->roff = roff_alloc(curp, curp->mchars, 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->pman = man_alloc( + curp->roff, curp, curp->defos, curp->options & MPARSE_QUICK ? 1 : 0); return(curp); @@ -1019,7 +992,7 @@ mandoc_msg(enum mandocerr er, struct mparse *m, while (er < mandoclimits[level]) level--; - if (level < m->wlevel) + if (level < m->wlevel && er != MANDOCERR_FILE) return; if (m->mmsg)