-/* $Id: read.c,v 1.35 2013/05/30 03:52:59 schwarze Exp $ */
+/* $Id: read.c,v 1.46 2014/03/23 11:25:26 schwarze Exp $ */
/*
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2014 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
#include <assert.h>
#include <ctype.h>
+#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdint.h>
#include <unistd.h>
#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 {
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;
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,
"bad NAME section contents",
"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",
"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",
"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] = {
return;
}
- switch (curp->inttype) {
- case (MPARSE_MDOC):
+ if (MPARSE_MDOC & curp->options) {
if (NULL == curp->pmdoc)
- curp->pmdoc = mdoc_alloc(curp->roff, curp,
- curp->defos);
+ 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):
+ } else if (MPARSE_MAN & curp->options) {
if (NULL == curp->pman)
- curp->pman = man_alloc(curp->roff, curp);
+ 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);
+ 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);
+ curp->pman = man_alloc(curp->roff, curp,
+ MPARSE_QUICK & curp->options ? 1 : 0);
assert(curp->pman);
curp->man = curp->pman;
}
assert(MANDOCLEVEL_FATAL <= curp->file_status);
break;
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
*/
if (curp->secondary)
curp->secondary->sz -= pos + 1;
- mparse_readfd_r(curp, -1, ln.buf + of, 1);
+ mparse_readfd(curp, -1, ln.buf + of);
if (MANDOCLEVEL_FATAL <= curp->file_status)
break;
pos = 0;
if (0 == rc) {
assert(MANDOCLEVEL_FATAL <= curp->file_status);
break;
- }
+ } else if (2 == rc)
+ break;
/* Temporary buffers typically are not full. */
}
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;
#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);
}
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);
}
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);
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;
return;
}
- if ( ! (curp->man || curp->mdoc)) {
+ if ( ! (curp->mdoc || curp->man || curp->sodest)) {
mandoc_msg(MANDOCERR_NOTMANUAL, curp, 1, 0, NULL);
curp->file_status = MANDOCLEVEL_FATAL;
return;
}
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;
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.,
* 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)
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;
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(inttype, curp);
+ curp->roff = roff_alloc(curp, options);
return(curp);
}
curp->file_status = MANDOCLEVEL_OK;
curp->mdoc = NULL;
curp->man = NULL;
+
+ free(curp->sodest);
+ curp->sodest = NULL;
}
void
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)