-/* $Id: read.c,v 1.120 2015/01/28 21:11:54 schwarze Exp $ */
+/* $Id: read.c,v 1.159 2017/02/03 17:56:59 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010-2015 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2017 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
#include "config.h"
#include <sys/types.h>
-#if HAVE_MMAP
#include <sys/mman.h>
#include <sys/stat.h>
-#endif
-#include <sys/wait.h>
#include <assert.h>
#include <ctype.h>
+#if HAVE_ERR
+#include <err.h>
+#endif
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <zlib.h>
-#include "mandoc.h"
#include "mandoc_aux.h"
-#include "libmandoc.h"
+#include "mandoc.h"
+#include "roff.h"
#include "mdoc.h"
#include "man.h"
+#include "libmandoc.h"
+#include "roff_int.h"
#define REPARSE_LIMIT 1000
struct mparse {
- struct man *pman; /* persistent man parser */
- struct mdoc *pmdoc; /* persistent mdoc parser */
- struct man *man; /* man parser */
- struct mdoc *mdoc; /* mdoc parser */
+ struct roff_man *man; /* man 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 */
enum mandoclevel file_status; /* status of current parse */
enum mandoclevel wlevel; /* ignore messages below this */
int options; /* parser options */
+ int gzip; /* current input file is gzipped */
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 *);
"no document body",
"content before first section header",
"first section is not \"NAME\"",
- "bad NAME section contents",
+ "NAME section without Nm before Nd",
+ "NAME section without description",
+ "description not at the end of NAME",
+ "bad NAME section content",
+ "missing comma before name",
+ "missing description line, using \"\"",
"sections out of conventional order",
"duplicate section title",
"unexpected section",
"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",
"skipping empty request",
"conditional request controls empty scope",
"skipping empty macro",
+ "empty block",
"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 -width in -tag list, using 6n",
"missing utility name, using \"\"",
+ "missing function name, using \"\"",
"empty head in list item",
"empty list item",
"missing font type, using \\fR",
"unknown font type, using \\fR",
"nothing follows prefix",
+ "empty reference block",
+ "missing section argument",
"missing -std argument, adding it",
+ "missing option string, using \"\"",
+ "missing resource identifier, using \"\"",
"missing eqn box, using \"\"",
/* related to bad macro arguments */
"skipping duplicate display type",
"skipping duplicate list type",
"skipping -width argument",
+ "wrong number of cells",
"unknown AT&T UNIX version",
"comma in function argument",
"parenthesis in function name",
"invalid content in Rs block",
"invalid Boolean argument",
"unknown font, skipping request",
+ "odd number of characters in request",
/* related to plain text */
"blank line in fill mode, using .sp",
"tab in filled text",
"whitespace at end of input line",
+ "new sentence, new line",
"bad comment style",
"invalid escape sequence",
"undefined string, using \"\"",
/* related to request and macro arguments */
"escaped character not allowed in a name",
- "argument count wrong",
"NOT IMPLEMENTED: Bd -file",
+ "skipping display without arguments",
"missing list type, using -item",
"missing manual name, using \"\"",
"uname(3) system call failed, using UNKNOWN",
}
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;
+ mdoc_hash_init();
+ curp->man->macroset = MACROSET_MDOC;
+ curp->man->first->tok = TOKEN_NONE;
+ } else {
+ man_hash_init();
+ curp->man->macroset = MACROSET_MAN;
+ curp->man->first->tok = TOKEN_NONE;
}
-
- /* Fall back to man(7) as a last resort. */
-
- if (NULL == curp->pman)
- curp->pman = man_alloc(
- curp->roff, curp, curp->defos,
- MPARSE_QUICK & curp->options ? 1 : 0);
- assert(curp->pman);
- curp->man = curp->pman;
}
/*
const char *save_file;
char *cp;
size_t pos; /* byte number in the ln buffer */
+ size_t j; /* auxiliary byte number in the blk buffer */
enum rofferr rr;
int of;
int lnn; /* line number in the real file */
int fd;
- pid_t save_child;
unsigned char c;
memset(&ln, 0, sizeof(ln));
MANDOCERR_CHAR_UNSUPP,
curp, curp->line, pos, "0x%x", c);
i++;
- ln.buf[pos++] = '?';
+ if (c != '\r')
+ ln.buf[pos++] = '?';
continue;
}
}
if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) {
+ j = i;
i += 2;
/* Comment, skip to end of line */
for (; i < blk.sz; ++i) {
- if ('\n' == blk.buf[i]) {
- ++i;
- ++lnn;
- break;
- }
+ if (blk.buf[i] != '\n')
+ continue;
+ if (blk.buf[i - 1] == ' ' ||
+ blk.buf[i - 1] == '\t')
+ mandoc_msg(
+ MANDOCERR_SPACE_EOL,
+ curp, curp->line,
+ pos + i-1 - j, NULL);
+ ++i;
+ ++lnn;
+ break;
}
/* Backout trailing whitespaces */
if (curp->secondary)
curp->secondary->sz -= pos + 1;
save_file = curp->file;
- save_child = curp->child;
- if (mparse_open(curp, &fd, ln.buf + of) ==
- MANDOCLEVEL_OK) {
+ if ((fd = mparse_open(curp, ln.buf + of)) != -1) {
mparse_readfd(curp, fd, ln.buf + of);
+ close(fd);
curp->file = save_file;
} else {
curp->file = save_file;
of = 0;
mparse_buf_r(curp, ln, of, 0);
}
- curp->child = save_child;
pos = 0;
continue;
default:
break;
}
- /*
- * If input parsers have not been allocated, do so now.
- * 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))
+ if (curp->man->macroset == MACROSET_NONE)
choose_parser(curp);
/*
* Do the same for ROFF_EQN.
*/
- if (rr == ROFF_TBL) {
+ 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) :
+ roff_addtbl(curp->man, span);
+ else if (rr == ROFF_EQN)
+ roff_addeqn(curp->man, roff_eqn(curp->roff));
+ else if ((curp->man->macroset == MACROSET_MDOC ?
+ mdoc_parseln(curp->man, curp->line, ln.buf, of) :
man_parseln(curp->man, curp->line, ln.buf, of)) == 2)
break;
read_whole_file(struct mparse *curp, const char *file, int fd,
struct buf *fb, int *with_mmap)
{
+ gzFile gz;
size_t off;
ssize_t ssz;
-#if HAVE_MMAP
struct stat st;
- if (-1 == fstat(fd, &st)) {
- perror(file);
- exit((int)MANDOCLEVEL_SYSERR);
- }
+
+ if (fstat(fd, &st) == -1)
+ err((int)MANDOCLEVEL_SYSERR, "%s", file);
/*
* If we're a regular file, try just reading in the whole entry
* concerned that this is going to tank any machines.
*/
- if (S_ISREG(st.st_mode)) {
- if (st.st_size >= (1U << 31)) {
+ if (curp->gzip == 0 && S_ISREG(st.st_mode)) {
+ if (st.st_size > 0x7fffffff) {
mandoc_msg(MANDOCERR_TOOLARGE, curp, 0, 0, NULL);
- return(0);
+ return 0;
}
*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 1;
}
-#endif
+
+ if (curp->gzip) {
+ if ((gz = gzdopen(fd, "rb")) == NULL)
+ err((int)MANDOCLEVEL_SYSERR, "%s", file);
+ } else
+ gz = NULL;
/*
* If this isn't a regular file (like, say, stdin), then we must
}
resize_buf(fb, 65536);
}
- ssz = read(fd, fb->buf + (int)off, fb->sz - off);
+ ssz = curp->gzip ?
+ gzread(gz, fb->buf + (int)off, fb->sz - off) :
+ read(fd, fb->buf + (int)off, fb->sz - off);
if (ssz == 0) {
fb->sz = off;
- return(1);
- }
- if (ssz == -1) {
- perror(file);
- exit((int)MANDOCLEVEL_SYSERR);
+ return 1;
}
+ if (ssz == -1)
+ err((int)MANDOCLEVEL_SYSERR, "%s", file);
off += (size_t)ssz;
}
free(fb->buf);
fb->buf = NULL;
- return(0);
+ return 0;
}
static void
mparse_end(struct mparse *curp)
{
-
- 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->defos,
- curp->options & MPARSE_QUICK ? 1 : 0);
- curp->man = curp->pman;
- }
- }
- if (curp->mdoc)
- mdoc_endparse(curp->mdoc);
- if (curp->man)
+ if (curp->man->macroset == MACROSET_NONE)
+ curp->man->macroset = MACROSET_MAN;
+ if (curp->man->macroset == MACROSET_MDOC)
+ mdoc_endparse(curp->man);
+ else
man_endparse(curp->man);
roff_endparse(curp->roff);
}
blk.sz = len;
mparse_parse_buffer(curp, blk, file);
- return(curp->file_status);
+ return curp->file_status;
}
/*
(MPARSE_UTF8 | MPARSE_LATIN1);
mparse_parse_buffer(curp, blk, file);
curp->filenc = save_filenc;
-#if HAVE_MMAP
if (with_mmap)
munmap(blk.buf, blk.sz);
else
-#endif
free(blk.buf);
}
-
- if (fd != STDIN_FILENO && close(fd) == -1)
- perror(file);
-
- mparse_wait(curp);
- return(curp->file_status);
+ return curp->file_status;
}
-enum mandoclevel
-mparse_open(struct mparse *curp, int *fd, const char *file)
+int
+mparse_open(struct mparse *curp, const char *file)
{
- int pfd[2];
- int save_errno;
char *cp;
+ int fd;
curp->file = file;
+ cp = strrchr(file, '.');
+ curp->gzip = (cp != NULL && ! strcmp(cp + 1, "gz"));
- /* Unless zipped, try to just open the file. */
+ /* First try to use the filename as it is. */
- if ((cp = strrchr(file, '.')) == NULL ||
- strcmp(cp + 1, "gz")) {
- curp->child = 0;
- if ((*fd = open(file, O_RDONLY)) != -1)
- return(MANDOCLEVEL_OK);
+ if ((fd = open(file, O_RDONLY)) != -1)
+ return fd;
- /* Open failed; try to append ".gz". */
+ /*
+ * If that doesn't work and the filename doesn't
+ * already end in .gz, try appending .gz.
+ */
+ if ( ! curp->gzip) {
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;
+ fd = open(cp, O_RDONLY);
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) {
- perror("pipe");
- exit((int)MANDOCLEVEL_SYSERR);
- }
-
- switch (curp->child = fork()) {
- case -1:
- perror("fork");
- exit((int)MANDOCLEVEL_SYSERR);
- case 0:
- close(pfd[0]);
- if (dup2(pfd[1], STDOUT_FILENO) == -1) {
- perror("dup");
- exit((int)MANDOCLEVEL_SYSERR);
+ if (fd != -1) {
+ curp->gzip = 1;
+ return fd;
}
- execlp("gunzip", "gunzip", "-c", file, NULL);
- perror("exec");
- exit((int)MANDOCLEVEL_SYSERR);
- default:
- close(pfd[1]);
- *fd = pfd[0];
- return(MANDOCLEVEL_OK);
}
-}
-enum mandoclevel
-mparse_wait(struct mparse *curp)
-{
- int status;
+ /* Neither worked, give up. */
- 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_FILE, curp, 0, 0,
- "gunzip died from signal %d", WTERMSIG(status));
- return(MANDOCLEVEL_ERROR);
- }
- if (WEXITSTATUS(status)) {
- mandoc_vmsg(MANDOCERR_FILE, curp, 0, 0,
- "gunzip failed with code %d", WEXITSTATUS(status));
- return(MANDOCLEVEL_ERROR);
- }
- return(MANDOCLEVEL_OK);
+ mandoc_msg(MANDOCERR_FILE, curp, 0, 0, strerror(errno));
+ return -1;
}
struct mparse *
mparse_alloc(int options, enum mandoclevel wlevel, mandocmsg mmsg,
- const struct mchars *mchars, const char *defos)
+ const char *defos)
{
struct mparse *curp;
curp->mmsg = mmsg;
curp->defos = defos;
- 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->defos,
- curp->options & MPARSE_QUICK ? 1 : 0);
-
- return(curp);
+ curp->roff = roff_alloc(curp, options);
+ curp->man = roff_man_alloc( curp->roff, curp, curp->defos,
+ curp->options & MPARSE_QUICK ? 1 : 0);
+ if (curp->options & MPARSE_MDOC) {
+ mdoc_hash_init();
+ curp->man->macroset = MACROSET_MDOC;
+ } else if (curp->options & MPARSE_MAN) {
+ man_hash_init();
+ curp->man->macroset = MACROSET_MAN;
+ }
+ curp->man->first->tok = TOKEN_NONE;
+ return curp;
}
void
mparse_reset(struct mparse *curp)
{
-
roff_reset(curp->roff);
-
- if (curp->mdoc)
- mdoc_reset(curp->mdoc);
- if (curp->man)
- man_reset(curp->man);
+ roff_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;
+ curp->gzip = 0;
}
void
mparse_free(struct mparse *curp)
{
- if (curp->pmdoc)
- mdoc_free(curp->pmdoc);
- if (curp->pman)
- man_free(curp->pman);
+ roff_man_free(curp->man);
if (curp->roff)
roff_free(curp->roff);
if (curp->secondary)
}
void
-mparse_result(struct mparse *curp,
- struct mdoc **mdoc, struct man **man, char **sodest)
+mparse_result(struct mparse *curp, struct roff_man **man,
+ char **sodest)
{
if (sodest && NULL != (*sodest = curp->sodest)) {
- *mdoc = NULL;
*man = NULL;
return;
}
- if (mdoc)
- *mdoc = curp->mdoc;
if (man)
*man = curp->man;
}
+void
+mparse_updaterc(struct mparse *curp, enum mandoclevel *rc)
+{
+ if (curp->file_status > *rc)
+ *rc = curp->file_status;
+}
+
void
mandoc_vmsg(enum mandocerr t, struct mparse *m,
int ln, int pos, const char *fmt, ...)
mparse_strerror(enum mandocerr er)
{
- return(mandocerrs[er]);
+ return mandocerrs[er];
}
const char *
mparse_strlevel(enum mandoclevel lvl)
{
- return(mandoclevels[lvl]);
+ return mandoclevels[lvl];
}
void
{
assert(p->secondary);
- return(p->secondary->sz ? p->secondary->buf : NULL);
+ return p->secondary->sz ? p->secondary->buf : NULL;
}