source@mandoc.bsd.lv
 help / color / mirror / Atom feed
* mdocml: Implement a simple, consistent user interface for error
@ 2010-08-20  1:02 schwarze
  2010-08-20  6:02 ` Kristaps Dzonsons
  0 siblings, 1 reply; 2+ messages in thread
From: schwarze @ 2010-08-20  1:02 UTC (permalink / raw)
  To: source

Log Message:
-----------
Implement a simple, consistent user interface for error handling.
We now have sufficient practical experience to know what we want,
so this is intended to be final:
- provide -Wlevel (warning, error or fatal) to select what you care about
- provide -Wstop to stop after parsing a file with warnings you care about
- provide consistent exit status codes for those warnings you care about
- fully document what warnings, errors and fatal errors mean
- remove all other cruft from the user interface, less is more:
- remove all -f knobs along with the whole -f option
- remove the old -Werror because calling warnings "fatal" is silly
- always finish parsing each file, unless fatal errors prevent that
This commit also includes a couple of related simplifications behind
the scenes regarding error handling.
Feedback and OK  kristaps@;  Joerg Sonnenberger (NetBSD) and
Sascha Wildner (DragonFly BSD) agree with the general direction.

Modified Files:
--------------
    mdocml:
        chars.c
        html.c
        libman.h
        libmdoc.h
        main.c
        man.3
        man.c
        man.h
        man_validate.c
        mandoc.1
        mandoc.c
        mandoc.h
        mdoc.3
        mdoc.c
        mdoc.h
        mdoc_html.c
        mdoc_validate.c
        roff.3
        roff.c
        roff.h
        term.c
        term_ps.c

Revision Data
-------------
Index: roff.3
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/roff.3,v
retrieving revision 1.7
retrieving revision 1.8
diff -Lroff.3 -Lroff.3 -u -p -r1.7 -r1.8
--- roff.3
+++ roff.3
@@ -31,8 +31,8 @@
 .Ft "struct roff *"
 .Fo roff_alloc
 .Fa "struct regset *regs"
-.Fa "mandocmsg msgs"
 .Fa "void *data"
+.Fa "mandocmsg msgs"
 .Fc
 .Ft int
 .Fn roff_endparse "struct roff *roff"
@@ -111,10 +111,6 @@ The
 .Fa data
 pointer is passed to
 .Fa msgs .
-The
-.Fa pflags
-arguments are defined in
-.Pa roff.h .
 Returns NULL on failure.
 If non-NULL, the pointer must be freed with
 .Fn roff_free .
Index: chars.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/chars.c,v
retrieving revision 1.26
retrieving revision 1.27
diff -Lchars.c -Lchars.c -u -p -r1.26 -r1.27
--- chars.c
+++ chars.c
@@ -94,13 +94,13 @@ chars_init(enum chars type)
 	tab = malloc(sizeof(struct tbl));
 	if (NULL == tab) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	htab = calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
 	if (NULL == htab) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	for (i = 0; i < LINES_MAX; i++) {
Index: html.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/html.c,v
retrieving revision 1.110
retrieving revision 1.111
diff -Lhtml.c -Lhtml.c -u -p -r1.110 -r1.111
--- html.c
+++ html.c
@@ -116,7 +116,7 @@ ml_alloc(char *outopts, enum htmltype ty
 	h = calloc(1, sizeof(struct html));
 	if (NULL == h) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	h->type = type;
@@ -398,7 +398,7 @@ print_otag(struct html *h, enum htmltag 
 		t = malloc(sizeof(struct tag));
 		if (NULL == t) {
 			perror(NULL);
-			exit(EXIT_FAILURE);
+			exit(MANDOCLEVEL_SYSERR);
 		}
 		t->tag = tag;
 		t->next = h->tags.head;
Index: term_ps.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/term_ps.c,v
retrieving revision 1.41
retrieving revision 1.42
diff -Lterm_ps.c -Lterm_ps.c -u -p -r1.41 -r1.42
--- term_ps.c
+++ term_ps.c
@@ -29,6 +29,7 @@
 #include <time.h>
 #include <unistd.h>
 
+#include "mandoc.h"
 #include "out.h"
 #include "main.h"
 #include "term.h"
@@ -371,7 +372,7 @@ ps_growbuf(struct termp *p, size_t sz)
 	
 	if (NULL == p->engine.ps.psmarg) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 }
 
@@ -608,7 +609,7 @@ pdf_obj(struct termp *p, size_t obj)
 			 p->engine.ps.pdfobjsz * sizeof(size_t));
 		if (NULL == p->engine.ps.pdfobjs) {
 			perror(NULL);
-			exit(EXIT_FAILURE);
+			exit(MANDOCLEVEL_SYSERR);
 		}
 	}
 
Index: mandoc.h
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/mandoc.h,v
retrieving revision 1.17
retrieving revision 1.18
diff -Lmandoc.h -Lmandoc.h -u -p -r1.17 -r1.18
--- mandoc.h
+++ mandoc.h
@@ -26,6 +26,17 @@
 
 __BEGIN_DECLS
 
+enum	mandoclevel {
+	MANDOCLEVEL_OK = 0,
+	MANDOCLEVEL_RESERVED,
+	MANDOCLEVEL_WARNING,
+	MANDOCLEVEL_ERROR,
+	MANDOCLEVEL_FATAL,
+	MANDOCLEVEL_BADARG,
+	MANDOCLEVEL_SYSERR,
+	MANDOCLEVEL_MAX
+};
+
 enum	mandocerr {
 	MANDOCERR_OK,
 
@@ -105,7 +116,7 @@ enum	mandocerr {
 	MANDOCERR_NODOCBODY, /* no document body */
 	MANDOCERR_NODOCPROLOG, /* no document prologue */
 	MANDOCERR_UTSNAME, /* utsname system call failed */
-	MANDOCERR_MEM, /* memory exhausted */
+	MANDOCERR_MEM, /* static buffer exhausted */
 
 	MANDOCERR_MAX
 };
Index: libman.h
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/libman.h,v
retrieving revision 1.42
retrieving revision 1.43
diff -Llibman.h -Llibman.h -u -p -r1.42 -r1.43
--- libman.h
+++ libman.h
@@ -27,7 +27,6 @@ enum	man_next {
 struct	man {
 	void		*data; /* private application data */
 	mandocmsg	 msg; /* output message handler */
-	int		 pflags; /* parse flags (see man.h) */
 	int		 flags; /* parse flags */
 #define	MAN_HALT	(1 << 0) /* badness happened: die */
 #define	MAN_ELINE	(1 << 1) /* Next-line element scope. */
Index: main.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/main.c,v
retrieving revision 1.102
retrieving revision 1.103
diff -Lmain.c -Lmain.c -u -p -r1.102 -r1.103
--- main.c
+++ main.c
@@ -79,17 +79,8 @@ enum	outt {
 struct	curparse {
 	const char	 *file;		/* Current parse. */
 	int		  fd;		/* Current parse. */
-	int		  wflags;
-	/* FIXME: set by max error */
-#define	WARN_WALL	 (1 << 0)	/* All-warnings mask. */
-#define	WARN_WERR	 (1 << 2)	/* Warnings->errors. */
-	int		  fflags;
-#define	FL_IGN_SCOPE	 (1 << 0) 	/* Ignore scope errors. */
-#define	FL_NIGN_ESCAPE	 (1 << 1) 	/* Don't ignore bad escapes. */
-#define	FL_NIGN_MACRO	 (1 << 2) 	/* Don't ignore bad macros. */
-#define	FL_IGN_ERRORS	 (1 << 4)	/* Ignore failed parse. */
-#define	FL_STRICT	  FL_NIGN_ESCAPE | \
-			  FL_NIGN_MACRO /* ignore nothing */
+	enum mandoclevel  wlevel;	/* Ignore messages below this. */
+	int		  wstop;	/* Stop after a file with a warning. */
 	enum intt	  inttype;	/* which parser to use */
 	struct man	 *man;		/* man parser */
 	struct mdoc	 *mdoc;		/* mdoc parser */
@@ -103,6 +94,26 @@ struct	curparse {
 	char		  outopts[BUFSIZ]; /* buf of output opts */
 };
 
+static	const char * const	mandoclevels[MANDOCLEVEL_MAX] = {
+	"SUCCESS",
+	"RESERVED",
+	"WARNING",
+	"ERROR",
+	"FATAL",
+	"BADARG",
+	"SYSERR"
+};
+
+static	const enum mandocerr	mandoclimits[MANDOCLEVEL_MAX] = {
+	MANDOCERR_OK,
+	MANDOCERR_WARNING,
+	MANDOCERR_WARNING,
+	MANDOCERR_ERROR,
+	MANDOCERR_FATAL,
+	MANDOCERR_MAX,
+	MANDOCERR_MAX
+};
+
 static	const char * const	mandocerrs[MANDOCERR_MAX] = {
 	"ok",
 
@@ -179,28 +190,23 @@ static	const char * const	mandocerrs[MAN
 	"no document body",
 	"no document prologue",
 	"utsname system call failed",
-	"memory exhausted",
+	"static buffer exhausted",
 };
 
 static	void		  fdesc(struct curparse *);
 static	void		  ffile(const char *, struct curparse *);
-static	int		  foptions(int *, char *);
-static	struct man	 *man_init(struct curparse *);
-static	struct mdoc	 *mdoc_init(struct curparse *);
-static	struct roff	 *roff_init(struct curparse *);
 static	int		  moptions(enum intt *, char *);
 static	int		  mmsg(enum mandocerr, void *, 
 				int, int, const char *);
-static	int		  pset(const char *, int, struct curparse *,
+static	void		  pset(const char *, int, struct curparse *,
 				struct man **, struct mdoc **);
 static	int		  toptions(struct curparse *, char *);
 static	void		  usage(void) __attribute__((noreturn));
 static	void		  version(void) __attribute__((noreturn));
-static	int		  woptions(int *, char *);
+static	int		  woptions(struct curparse *, char *);
 
 static	const char	 *progname;
-static	int		  with_fatal;
-static	int		  with_error;
+static	enum mandoclevel  exit_status = MANDOCLEVEL_OK;
 
 int
 main(int argc, char *argv[])
@@ -218,17 +224,14 @@ main(int argc, char *argv[])
 
 	curp.inttype = INTT_AUTO;
 	curp.outtype = OUTT_ASCII;
+	curp.wlevel  = MANDOCLEVEL_FATAL;
 
 	/* LINTED */
-	while (-1 != (c = getopt(argc, argv, "f:m:O:T:VW:")))
+	while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
 		switch (c) {
-		case ('f'):
-			if ( ! foptions(&curp.fflags, optarg))
-				return(EXIT_FAILURE);
-			break;
 		case ('m'):
 			if ( ! moptions(&curp.inttype, optarg))
-				return(EXIT_FAILURE);
+				return(MANDOCLEVEL_BADARG);
 			break;
 		case ('O'):
 			(void)strlcat(curp.outopts, optarg, BUFSIZ);
@@ -236,11 +239,11 @@ main(int argc, char *argv[])
 			break;
 		case ('T'):
 			if ( ! toptions(&curp, optarg))
-				return(EXIT_FAILURE);
+				return(MANDOCLEVEL_BADARG);
 			break;
 		case ('W'):
-			if ( ! woptions(&curp.wflags, optarg))
-				return(EXIT_FAILURE);
+			if ( ! woptions(&curp, optarg))
+				return(MANDOCLEVEL_BADARG);
 			break;
 		case ('V'):
 			version();
@@ -262,8 +265,7 @@ main(int argc, char *argv[])
 
 	while (*argv) {
 		ffile(*argv, &curp);
-
-		if (with_fatal && !(curp.fflags & FL_IGN_ERRORS))
+		if (MANDOCLEVEL_OK != exit_status && curp.wstop)
 			break;
 		++argv;
 	}
@@ -277,8 +279,7 @@ main(int argc, char *argv[])
 	if (curp.roff)
 		roff_free(curp.roff);
 
-	return((with_fatal || with_error) ? 
-			EXIT_FAILURE :  EXIT_SUCCESS);
+	return(exit_status);
 }
 
 
@@ -287,7 +288,7 @@ version(void)
 {
 
 	(void)printf("%s %s\n", progname, VERSION);
-	exit(EXIT_SUCCESS);
+	exit(MANDOCLEVEL_OK);
 }
 
 
@@ -298,53 +299,7 @@ usage(void)
 	(void)fprintf(stderr, "usage: %s [-V] [-foption] "
 			"[-mformat] [-Ooption] [-Toutput] "
 			"[-Werr] [file...]\n", progname);
-	exit(EXIT_FAILURE);
-}
-
-
-static struct man *
-man_init(struct curparse *curp)
-{
-	int		 pflags;
-
-	/* Defaults from mandoc.1. */
-
-	pflags = MAN_IGN_MACRO | MAN_IGN_ESCAPE;
-
-	if (curp->fflags & FL_NIGN_MACRO)
-		pflags &= ~MAN_IGN_MACRO;
-	if (curp->fflags & FL_NIGN_ESCAPE)
-		pflags &= ~MAN_IGN_ESCAPE;
-
-	return(man_alloc(&curp->regs, curp, pflags, mmsg));
-}
-
-
-static struct roff *
-roff_init(struct curparse *curp)
-{
-
-	return(roff_alloc(&curp->regs, mmsg, curp));
-}
-
-
-static struct mdoc *
-mdoc_init(struct curparse *curp)
-{
-	int		 pflags;
-
-	/* Defaults from mandoc.1. */
-
-	pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE;
-
-	if (curp->fflags & FL_IGN_SCOPE)
-		pflags |= MDOC_IGN_SCOPE;
-	if (curp->fflags & FL_NIGN_ESCAPE)
-		pflags &= ~MDOC_IGN_ESCAPE;
-	if (curp->fflags & FL_NIGN_MACRO)
-		pflags &= ~MDOC_IGN_MACRO;
-
-	return(mdoc_alloc(&curp->regs, curp, pflags, mmsg));
+	exit(MANDOCLEVEL_BADARG);
 }
 
 
@@ -355,7 +310,7 @@ ffile(const char *file, struct curparse 
 	curp->file = file;
 	if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
 		perror(curp->file);
-		with_fatal = 1;
+		exit_status = MANDOCLEVEL_SYSERR;
 		return;
 	}
 
@@ -366,24 +321,16 @@ ffile(const char *file, struct curparse 
 }
 
 
-static int
+static void
 resize_buf(struct buf *buf, size_t initial)
 {
-	void *tmp;
-	size_t sz;
 
-	if (buf->sz == 0)
-		sz = initial;
-	else
-		sz = 2 * buf->sz;
-	tmp = realloc(buf->buf, sz);
-	if (NULL == tmp) {
+	buf->sz = buf->sz ? 2 * buf->sz : initial;
+	buf->buf = realloc(buf->buf, buf->sz);
+	if (NULL == buf->buf) {
 		perror(NULL);
-		return(0);
+		exit(MANDOCLEVEL_SYSERR);
 	}
-	buf->buf = tmp;
-	buf->sz = sz;
-	return(1);
 }
 
 
@@ -396,7 +343,6 @@ read_whole_file(struct curparse *curp, s
 
 	if (-1 == fstat(curp->fd, &st)) {
 		perror(curp->file);
-		with_fatal = 1;
 		return(0);
 	}
 
@@ -411,7 +357,6 @@ read_whole_file(struct curparse *curp, s
 		if (st.st_size >= (1U << 31)) {
 			fprintf(stderr, "%s: input too large\n", 
 					curp->file);
-			with_fatal = 1;
 			return(0);
 		}
 		*with_mmap = 1;
@@ -438,8 +383,7 @@ read_whole_file(struct curparse *curp, s
 						curp->file);
 				break;
 			}
-			if (! resize_buf(fb, 65536))
-				break;
+			resize_buf(fb, 65536);
 		}
 		ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);
 		if (ssz == 0) {
@@ -455,7 +399,6 @@ read_whole_file(struct curparse *curp, s
 
 	free(fb->buf);
 	fb->buf = NULL;
-	with_fatal = 1;
 	return(0);
 }
 
@@ -482,13 +425,15 @@ fdesc(struct curparse *curp)
 	 * memory mapped.  ln is a line buffer and grows on-demand.
 	 */
 
-	if ( ! read_whole_file(curp, &blk, &with_mmap))
+	if ( ! read_whole_file(curp, &blk, &with_mmap)) {
+		exit_status = MANDOCLEVEL_SYSERR;
 		return;
+	}
 
 	if (NULL == curp->roff) 
-		curp->roff = roff_init(curp);
-	if (NULL == (roff = curp->roff))
-		goto bailout;
+		curp->roff = roff_alloc(&curp->regs, curp, mmsg);
+	assert(curp->roff);
+	roff = curp->roff;
 
 	for (i = 0, lnn = 1; i < (int)blk.sz;) {
 		pos = 0;
@@ -511,10 +456,8 @@ fdesc(struct curparse *curp)
 
 			c = (unsigned char) blk.buf[i];
 			if ( ! (isascii(c) && (isgraph(c) || isblank(c)))) {
-				if ( ! mmsg(MANDOCERR_BADCHAR, curp, 
-						lnn_start, pos, 
-						"ignoring byte"))
-					goto bailout;
+				mmsg(MANDOCERR_BADCHAR, curp, 
+				    lnn_start, pos, "ignoring byte");
 				i++;
 				continue;
 			}
@@ -522,8 +465,7 @@ fdesc(struct curparse *curp)
 			/* Trailing backslash is like a plain character. */
 			if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
 				if (pos >= (int)ln.sz)
-					if (! resize_buf(&ln, 256))
-						goto bailout;
+					resize_buf(&ln, 256);
 				ln.buf[pos++] = blk.buf[i++];
 				continue;
 			}
@@ -555,16 +497,14 @@ fdesc(struct curparse *curp)
 			}
 			/* Some other escape sequence, copy and continue. */
 			if (pos + 1 >= (int)ln.sz)
-				if (! resize_buf(&ln, 256))
-					goto bailout;
+				resize_buf(&ln, 256);
 
 			ln.buf[pos++] = blk.buf[i++];
 			ln.buf[pos++] = blk.buf[i++];
 		}
 
  		if (pos >= (int)ln.sz)
-			if (! resize_buf(&ln, 256))
-				goto bailout;
+			resize_buf(&ln, 256);
 		ln.buf[pos] = '\0';
 
 		/*
@@ -582,10 +522,12 @@ fdesc(struct curparse *curp)
 					&ln.buf, &ln.sz, of, &of);
 		} while (ROFF_RERUN == re);
 
-		if (ROFF_IGN == re)
+		if (ROFF_IGN == re) {
 			continue;
-		else if (ROFF_ERR == re)
-			goto bailout;
+		} else if (ROFF_ERR == re) {
+			assert(MANDOCLEVEL_FATAL <= exit_status);
+			goto cleanup;
+		}
 
 		/*
 		 * If input parsers have not been allocated, do so now.
@@ -595,32 +537,50 @@ fdesc(struct curparse *curp)
 		 */
 
 		if ( ! (man || mdoc))
-			if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc))
-				goto bailout;
+			pset(ln.buf + of, pos - of, curp, &man, &mdoc);
 
 		/* Lastly, push down into the parsers themselves. */
 
-		if (man && ! man_parseln(man, lnn_start, ln.buf, of))
-			goto bailout;
-		if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of))
-			goto bailout;
+		if (man && ! man_parseln(man, lnn_start, ln.buf, of)) {
+			assert(MANDOCLEVEL_FATAL <= exit_status);
+			goto cleanup;
+		}
+		if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of)) {
+			assert(MANDOCLEVEL_FATAL <= exit_status);
+			goto cleanup;
+		}
 	}
 
+	/*
+	 * With -Wstop and warnings or errors of at least
+	 * the requested level, do not produce output.
+	 */
+
+	if (MANDOCLEVEL_OK != exit_status && curp->wstop)
+		goto cleanup;
+
 	/* NOTE a parser may not have been assigned, yet. */
 
 	if ( ! (man || mdoc)) {
 		fprintf(stderr, "%s: Not a manual\n", curp->file);
-		goto bailout;
+		exit_status = MANDOCLEVEL_FATAL;
+		goto cleanup;
 	}
 
 	/* Clean up the parse routine ASTs. */
 
-	if (mdoc && ! mdoc_endparse(mdoc))
-		goto bailout;
-	if (man && ! man_endparse(man))
-		goto bailout;
-	if (roff && ! roff_endparse(roff))
-		goto bailout;
+	if (mdoc && ! mdoc_endparse(mdoc)) {
+		assert(MANDOCLEVEL_FATAL <= exit_status);
+		goto cleanup;
+	}
+	if (man && ! man_endparse(man)) {
+		assert(MANDOCLEVEL_FATAL <= exit_status);
+		goto cleanup;
+	}
+	if (roff && ! roff_endparse(roff)) {
+		assert(MANDOCLEVEL_FATAL <= exit_status);
+		goto cleanup;
+	}
 
 	/* If unset, allocate output dev now (if applicable). */
 
@@ -696,14 +656,10 @@ fdesc(struct curparse *curp)
 		free(blk.buf);
 
 	return;
-
- bailout:
-	with_fatal = 1;
-	goto cleanup;
 }
 
 
-static int
+static void
 pset(const char *buf, int pos, struct curparse *curp,
 		struct man **man, struct mdoc **mdoc)
 {
@@ -721,40 +677,39 @@ pset(const char *buf, int pos, struct cu
 		for (i = 1; buf[i]; i++)
 			if (' ' != buf[i] && '\t' != buf[i])
 				break;
-		if (0 == buf[i])
-			return(1);
+		if ('\0' == buf[i])
+			return;
 	}
 
 	switch (curp->inttype) {
 	case (INTT_MDOC):
 		if (NULL == curp->mdoc) 
-			curp->mdoc = mdoc_init(curp);
-		if (NULL == (*mdoc = curp->mdoc))
-			return(0);
-		return(1);
+			curp->mdoc = mdoc_alloc(&curp->regs, curp, mmsg);
+		assert(curp->mdoc);
+		*mdoc = curp->mdoc;
+		return;
 	case (INTT_MAN):
 		if (NULL == curp->man) 
-			curp->man = man_init(curp);
-		if (NULL == (*man = curp->man))
-			return(0);
-		return(1);
+			curp->man = man_alloc(&curp->regs, curp, mmsg);
+		assert(curp->man);
+		*man = curp->man;
+		return;
 	default:
 		break;
 	}
 
 	if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {
 		if (NULL == curp->mdoc) 
-			curp->mdoc = mdoc_init(curp);
-		if (NULL == (*mdoc = curp->mdoc))
-			return(0);
-		return(1);
+			curp->mdoc = mdoc_alloc(&curp->regs, curp, mmsg);
+		assert(curp->mdoc);
+		*mdoc = curp->mdoc;
+		return;
 	} 
 
 	if (NULL == curp->man) 
-		curp->man = man_init(curp);
-	if (NULL == (*man = curp->man))
-		return(0);
-	return(1);
+		curp->man = man_alloc(&curp->regs, curp, mmsg);
+	assert(curp->man);
+	*man = curp->man;
 }
 
 
@@ -785,8 +740,7 @@ toptions(struct curparse *curp, char *ar
 		curp->outtype = OUTT_ASCII;
 	else if (0 == strcmp(arg, "lint")) {
 		curp->outtype = OUTT_LINT;
-		curp->wflags |= WARN_WALL;
-		curp->fflags |= FL_STRICT;
+		curp->wlevel  = MANDOCLEVEL_WARNING;
 	}
 	else if (0 == strcmp(arg, "tree"))
 		curp->outtype = OUTT_TREE;
@@ -808,71 +762,37 @@ toptions(struct curparse *curp, char *ar
 
 
 static int
-foptions(int *fflags, char *arg)
+woptions(struct curparse *curp, char *arg)
 {
 	char		*v, *o;
-	const char	*toks[8];
+	const char	*toks[6]; 
 
-	toks[0] = "ign-scope";
-	toks[1] = "no-ign-escape";
-	toks[2] = "no-ign-macro";
-	toks[3] = "ign-errors";
-	toks[4] = "strict";
-	toks[5] = "ign-escape";
-	toks[6] = NULL;
+	toks[0] = "stop";
+	toks[1] = "all";
+	toks[2] = "warning";
+	toks[3] = "error";
+	toks[4] = "fatal";
+	toks[5] = NULL;
 
 	while (*arg) {
 		o = arg;
 		switch (getsubopt(&arg, UNCONST(toks), &v)) {
 		case (0):
-			*fflags |= FL_IGN_SCOPE;
+			curp->wstop = 1;
 			break;
 		case (1):
-			*fflags |= FL_NIGN_ESCAPE;
-			break;
+			/* FALLTHROUGH */
 		case (2):
-			*fflags |= FL_NIGN_MACRO;
+			curp->wlevel = MANDOCLEVEL_WARNING;
 			break;
 		case (3):
-			*fflags |= FL_IGN_ERRORS;
+			curp->wlevel = MANDOCLEVEL_ERROR;
 			break;
 		case (4):
-			*fflags |= FL_STRICT;
-			break;
-		case (5):
-			*fflags &= ~FL_NIGN_ESCAPE;
+			curp->wlevel = MANDOCLEVEL_FATAL;
 			break;
 		default:
-			fprintf(stderr, "%s: Bad argument\n", o);
-			return(0);
-		}
-	}
-
-	return(1);
-}
-
-
-static int
-woptions(int *wflags, char *arg)
-{
-	char		*v, *o;
-	const char	*toks[3]; 
-
-	toks[0] = "all";
-	toks[1] = "error";
-	toks[2] = NULL;
-
-	while (*arg) {
-		o = arg;
-		switch (getsubopt(&arg, UNCONST(toks), &v)) {
-		case (0):
-			*wflags |= WARN_WALL;
-			break;
-		case (1):
-			*wflags |= WARN_WERR;
-			break;
-		default:
-			fprintf(stderr, "%s: Bad argument\n", o);
+			fprintf(stderr, "-W%s: Bad argument\n", o);
 			return(0);
 		}
 	}
@@ -885,37 +805,24 @@ static int
 mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
 {
 	struct curparse *cp;
-	const char *level;
-	int rc;
+	enum mandoclevel level;
 
-	cp = (struct curparse *)arg;
-	level = NULL;
-	rc = 1;
+	level = MANDOCLEVEL_FATAL;
+	while (t < mandoclimits[level])
+		level--;
 
-	if (t >= MANDOCERR_FATAL) {
-		with_fatal = 1;
-		level = "FATAL";
-		rc = 0;
-	} else {
-		if ( ! (WARN_WALL & cp->wflags))
-			return(1);
-		if (t >= MANDOCERR_ERROR) {
-			with_error = 1;
-			level = "ERROR";
-		}
-		if (WARN_WERR & cp->wflags) {
-			with_fatal = 1;
-			rc = 0;
-		}
-	}
+	cp = (struct curparse *)arg;
+	if (level < cp->wlevel)
+		return(1);
 
-	fprintf(stderr, "%s:%d:%d:", cp->file, ln, col + 1);
-	if (level)
-		fprintf(stderr, " %s:", level);
-	fprintf(stderr, " %s", mandocerrs[t]);
+	fprintf(stderr, "%s:%d:%d: %s: %s",
+	    cp->file, ln, col + 1, mandoclevels[level], mandocerrs[t]);
 	if (msg)
 		fprintf(stderr, ": %s", msg);
 	fputc('\n', stderr);
 
-	return(rc);
+	if (exit_status < level)
+		exit_status = level;
+	
+	return(level < MANDOCLEVEL_FATAL);
 }
Index: mdoc_validate.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/mdoc_validate.c,v
retrieving revision 1.114
retrieving revision 1.115
diff -Lmdoc_validate.c -Lmdoc_validate.c -u -p -r1.114 -r1.115
--- mdoc_validate.c
+++ mdoc_validate.c
@@ -481,12 +481,8 @@ check_text(struct mdoc *m, int ln, int p
 		if (c) {
 			p += c - 1;
 			pos += c - 1;
-			continue;
-		}
-
-		c = mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE);
-		if ( ! (MDOC_IGN_ESCAPE & m->pflags) && ! c)
-			return(c);
+		} else
+			mdoc_pmsg(m, ln, pos, MANDOCERR_BADESCAPE);
 	}
 
 	return(1);
Index: man.h
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/man.h,v
retrieving revision 1.42
retrieving revision 1.43
diff -Lman.h -Lman.h -u -p -r1.42 -r1.43
--- man.h
+++ man.h
@@ -97,9 +97,6 @@ struct	man_node {
 	struct man_node	*body;
 };
 
-#define	MAN_IGN_MACRO	 (1 << 0)
-#define	MAN_IGN_ESCAPE	 (1 << 2)
-
 extern	const char *const *man_macronames;
 
 __BEGIN_DECLS
@@ -107,7 +104,7 @@ __BEGIN_DECLS
 struct	man;
 
 void	 	  man_free(struct man *);
-struct	man	 *man_alloc(struct regset *, void *, int, mandocmsg);
+struct	man	 *man_alloc(struct regset *, void *, mandocmsg);
 void		  man_reset(struct man *);
 int	 	  man_parseln(struct man *, int, char *, int);
 int		  man_endparse(struct man *);
Index: mdoc.h
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/mdoc.h,v
retrieving revision 1.102
retrieving revision 1.103
diff -Lmdoc.h -Lmdoc.h -u -p -r1.102 -r1.103
--- mdoc.h
+++ mdoc.h
@@ -352,10 +352,6 @@ struct	mdoc_node {
 	} data;
 };
 
-#define	MDOC_IGN_SCOPE	 (1 << 0) /* Ignore scope violations. */
-#define	MDOC_IGN_ESCAPE	 (1 << 1) /* Ignore bad escape sequences. */
-#define	MDOC_IGN_MACRO	 (1 << 2) /* Ignore unknown macros. */
-
 /* See mdoc.3 for documentation. */
 
 extern	const char *const *mdoc_macronames;
@@ -368,7 +364,7 @@ struct	mdoc;
 /* See mdoc.3 for documentation. */
 
 void	 	  mdoc_free(struct mdoc *);
-struct	mdoc	 *mdoc_alloc(struct regset *, void *, int, mandocmsg);
+struct	mdoc	 *mdoc_alloc(struct regset *, void *, mandocmsg);
 void		  mdoc_reset(struct mdoc *);
 int	 	  mdoc_parseln(struct mdoc *, int, char *, int);
 const struct mdoc_node *mdoc_node(const struct mdoc *);
Index: man_validate.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/man_validate.c,v
retrieving revision 1.48
retrieving revision 1.49
diff -Lman_validate.c -Lman_validate.c -u -p -r1.48 -r1.49
--- man_validate.c
+++ man_validate.c
@@ -233,12 +233,8 @@ check_text(CHKARGS) 
 		if (c) {
 			p += c - 1;
 			pos += c - 1;
-			continue;
-		}
-
-		c = man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE);
-		if ( ! (MAN_IGN_ESCAPE & m->pflags) && ! c)
-			return(c);
+		} else
+			man_pmsg(m, n->line, pos, MANDOCERR_BADESCAPE);
 	}
 
 	return(1);
Index: man.3
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/man.3,v
retrieving revision 1.23
retrieving revision 1.24
diff -Lman.3 -Lman.3 -u -p -r1.23 -r1.24
--- man.3
+++ man.3
@@ -35,7 +35,6 @@
 .Fo man_alloc
 .Fa "struct regset *regs"
 .Fa "void *data"
-.Fa "int pflags"
 .Fa "mandocmsg msgs"
 .Fc
 .Ft int
@@ -172,10 +171,6 @@ The
 .Fa data
 pointer is passed to
 .Fa msgs .
-The
-.Fa pflags
-arguments are defined in
-.Pa man.h .
 Returns NULL on failure.
 If non-NULL, the pointer must be freed with
 .Fn man_free .
@@ -298,7 +293,7 @@ int line;
 
 bzero(&regs, sizeof(struct regset));
 line = 1;
-man = man_alloc(&regs, NULL, 0, NULL);
+man = man_alloc(&regs, NULL, NULL);
 buf = NULL;
 alloc_len = 0;
 
Index: term.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/term.c,v
retrieving revision 1.166
retrieving revision 1.167
diff -Lterm.c -Lterm.c -u -p -r1.166 -r1.167
--- term.c
+++ term.c
@@ -83,7 +83,7 @@ term_alloc(enum termenc enc)
 	p = calloc(1, sizeof(struct termp));
 	if (NULL == p) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	p->enc = enc;
@@ -577,7 +577,7 @@ adjbuf(struct termp *p, size_t sz)
 	p->buf = realloc(p->buf, p->maxcols);
 	if (NULL == p->buf) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 }
 
Index: mandoc.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/mandoc.c,v
retrieving revision 1.28
retrieving revision 1.29
diff -Lmandoc.c -Lmandoc.c -u -p -r1.28 -r1.29
--- mandoc.c
+++ mandoc.c
@@ -199,7 +199,7 @@ mandoc_calloc(size_t num, size_t size)
 	ptr = calloc(num, size);
 	if (NULL == ptr) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	return(ptr);
@@ -214,7 +214,7 @@ mandoc_malloc(size_t size)
 	ptr = malloc(size);
 	if (NULL == ptr) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	return(ptr);
@@ -228,7 +228,7 @@ mandoc_realloc(void *ptr, size_t size)
 	ptr = realloc(ptr, size);
 	if (NULL == ptr) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	return(ptr);
@@ -243,7 +243,7 @@ mandoc_strdup(const char *ptr)
 	p = strdup(ptr);
 	if (NULL == p) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	return(p);
Index: libmdoc.h
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/libmdoc.h,v
retrieving revision 1.61
retrieving revision 1.62
diff -Llibmdoc.h -Llibmdoc.h -u -p -r1.61 -r1.62
--- libmdoc.h
+++ libmdoc.h
@@ -36,7 +36,6 @@ struct	mdoc {
 #define	MDOC_PPHRASE	 (1 << 5) /* within a partial phrase */
 #define	MDOC_FREECOL	 (1 << 6) /* `It' invocation should close */
 #define	MDOC_SYNOPSIS	 (1 << 7) /* SYNOPSIS-style formatting */
-	int		  pflags;
 	enum mdoc_next	  next; /* where to put the next node */
 	struct mdoc_node *last; /* the last node parsed */
 	struct mdoc_node *first; /* the first node parsed */
Index: mdoc_html.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/mdoc_html.c,v
retrieving revision 1.101
retrieving revision 1.102
diff -Lmdoc_html.c -Lmdoc_html.c -u -p -r1.101 -r1.102
--- mdoc_html.c
+++ mdoc_html.c
@@ -1183,7 +1183,7 @@ mdoc_bl_pre(MDOC_ARGS)
 	ord = malloc(sizeof(struct ord));
 	if (NULL == ord) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 	ord->cookie = n;
 	ord->pos = 1;
Index: man.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/man.c,v
retrieving revision 1.86
retrieving revision 1.87
diff -Lman.c -Lman.c -u -p -r1.86 -r1.87
--- man.c
+++ man.c
@@ -56,7 +56,6 @@ static	int		 man_ptext(struct man *, int
 static	int		 man_pmacro(struct man *, int, char *, int);
 static	void		 man_free1(struct man *);
 static	void		 man_alloc1(struct man *);
-static	int		 macrowarn(struct man *, int, const char *, int);
 
 
 const struct man_node *
@@ -94,8 +93,7 @@ man_free(struct man *man)
 
 
 struct man *
-man_alloc(struct regset *regs, void *data, 
-		int pflags, mandocmsg msg)
+man_alloc(struct regset *regs, void *data, mandocmsg msg)
 {
 	struct man	*p;
 
@@ -103,7 +101,6 @@ man_alloc(struct regset *regs, void *dat
 
 	man_hash_init();
 	p->data = data;
-	p->pflags = pflags;
 	p->msg = msg;
 	p->regs = regs;
 
@@ -435,19 +432,6 @@ descope:
 }
 
 
-static int
-macrowarn(struct man *m, int ln, const char *buf, int offs)
-{
-	int		 rc;
-
-	rc = man_vmsg(m, MANDOCERR_MACRO, ln, offs, 
-			"unknown macro: %s%s",
-			buf, strlen(buf) > 3 ? "..." : "");
-
-	return(MAN_IGN_MACRO & m->pflags ? rc : 0);
-}
-
-
 int
 man_pmacro(struct man *m, int ln, char *buf, int offs)
 {
@@ -490,15 +474,11 @@ man_pmacro(struct man *m, int ln, char *
 		mac[j++] = buf[i++];
 	mac[j] = '\0';
 
-	if (j == 4 || j < 1) {
-		if ( ! macrowarn(m, ln, mac, ppos))
-			goto err;
-		return(1);
-	}
-	
-	if (MAN_MAX == (tok = man_hash_find(mac))) {
-		if ( ! macrowarn(m, ln, mac, ppos))
-			goto err;
+	tok = (j > 0 && j < 4) ? man_hash_find(mac) : MAN_MAX;
+	if (MAN_MAX == tok) {
+		man_vmsg(m, MANDOCERR_MACRO, ln, ppos, 
+		    "unknown macro: %s%s",
+		    buf, strlen(buf) > 3 ? "..." : "");
 		return(1);
 	}
 
Index: roff.h
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/roff.h,v
retrieving revision 1.17
retrieving revision 1.18
diff -Lroff.h -Lroff.h -u -p -r1.17 -r1.18
--- roff.h
+++ roff.h
@@ -29,7 +29,7 @@ __BEGIN_DECLS
 struct	roff;
 
 void	 	  roff_free(struct roff *);
-struct	roff	 *roff_alloc(struct regset *, mandocmsg, void *);
+struct	roff	 *roff_alloc(struct regset *, void *, mandocmsg);
 void		  roff_reset(struct roff *);
 enum	rofferr	  roff_parseln(struct roff *, int, 
 			char **, size_t *, int, int *);
Index: mdoc.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/mdoc.c,v
retrieving revision 1.162
retrieving revision 1.163
diff -Lmdoc.c -Lmdoc.c -u -p -r1.162 -r1.163
--- mdoc.c
+++ mdoc.c
@@ -98,8 +98,6 @@ static	int		  node_append(struct mdoc *,
 				struct mdoc_node *);
 static	int		  mdoc_ptext(struct mdoc *, int, char *, int);
 static	int		  mdoc_pmacro(struct mdoc *, int, char *, int);
-static	int		  macrowarn(struct mdoc *, int, 
-				const char *, int);
 
 
 const struct mdoc_node *
@@ -191,8 +189,7 @@ mdoc_free(struct mdoc *mdoc)
  * Allocate volatile and non-volatile parse resources.  
  */
 struct mdoc *
-mdoc_alloc(struct regset *regs, void *data, 
-		int pflags, mandocmsg msg)
+mdoc_alloc(struct regset *regs, void *data, mandocmsg msg)
 {
 	struct mdoc	*p;
 
@@ -200,7 +197,6 @@ mdoc_alloc(struct regset *regs, void *da
 
 	p->msg = msg;
 	p->data = data;
-	p->pflags = pflags;
 	p->regs = regs;
 
 	mdoc_hash_init();
@@ -727,21 +723,6 @@ mdoc_ptext(struct mdoc *m, int line, cha
 }
 
 
-static int
-macrowarn(struct mdoc *m, int ln, const char *buf, int offs)
-{
-	int		 rc;
-
-	rc = mdoc_vmsg(m, MANDOCERR_MACRO, ln, offs, 
-			"unknown macro: %s%s", 
-			buf, strlen(buf) > 3 ? "..." : "");
-
-	/* FIXME: logic should be in driver. */
-	/* FIXME: broken, will error out and not omit a message. */
-	return(MDOC_IGN_MACRO & m->pflags ? rc : 0);
-}
-
-
 /*
  * Parse a macro line, that is, a line beginning with the control
  * character.
@@ -785,15 +766,11 @@ mdoc_pmacro(struct mdoc *m, int ln, char
 		mac[j++] = buf[i++];
 	mac[j] = '\0';
 
-	if (j == 4 || j < 2) {
-		if ( ! macrowarn(m, ln, mac, sv))
-			goto err;
-		return(1);
-	} 
-	
-	if (MDOC_MAX == (tok = mdoc_hash_find(mac))) {
-		if ( ! macrowarn(m, ln, mac, sv))
-			goto err;
+	tok = (j > 1 || j < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
+	if (MDOC_MAX == tok) {
+		mdoc_vmsg(m, MANDOCERR_MACRO, ln, sv, 
+		    "unknown macro: %s%s", 
+		    buf, strlen(buf) > 3 ? "..." : "");
 		return(1);
 	}
 
Index: roff.c
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/roff.c,v
retrieving revision 1.97
retrieving revision 1.98
diff -Lroff.c -Lroff.c -u -p -r1.97 -r1.98
--- roff.c
+++ roff.c
@@ -168,7 +168,7 @@ static	void		 roff_free1(struct roff *);
 static	enum rofft	 roff_hash_find(const char *);
 static	void		 roff_hash_init(void);
 static	void		 roffnode_cleanscope(struct roff *);
-static	int		 roffnode_push(struct roff *, 
+static	void		 roffnode_push(struct roff *, 
 				enum rofft, int, int);
 static	void		 roffnode_pop(struct roff *);
 static	enum rofft	 roff_parse(const char *, int *);
@@ -258,16 +258,12 @@ roffnode_pop(struct roff *r)
  * Push a roff node onto the instruction stack.  This must later be
  * removed with roffnode_pop().
  */
-static int
+static void
 roffnode_push(struct roff *r, enum rofft tok, int line, int col)
 {
 	struct roffnode	*p;
 
-	if (NULL == (p = calloc(1, sizeof(struct roffnode)))) {
-		(*r->msg)(MANDOCERR_MEM, r->data, line, col, NULL);
-		return(0);
-	}
-
+	p = mandoc_calloc(1, sizeof(struct roffnode));
 	p->tok = tok;
 	p->parent = r->last;
 	p->line = line;
@@ -275,7 +271,6 @@ roffnode_push(struct roff *r, enum rofft
 	p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
 
 	r->last = p;
-	return(1);
 }
 
 
@@ -307,15 +302,11 @@ roff_free(struct roff *r)
 
 
 struct roff *
-roff_alloc(struct regset *regs, const mandocmsg msg, void *data)
+roff_alloc(struct regset *regs, void *data, const mandocmsg msg)
 {
 	struct roff	*r;
 
-	if (NULL == (r = calloc(1, sizeof(struct roff)))) {
-		(*msg)(MANDOCERR_MEM, data, 0, 0, NULL);
-		return(0);
-	}
-
+	r = mandoc_calloc(1, sizeof(struct roff));
 	r->regs = regs;
 	r->msg = msg;
 	r->data = data;
@@ -650,8 +641,7 @@ roff_block(ROFF_ARGS)
 			pos++;
 	}
 
-	if ( ! roffnode_push(r, tok, ln, ppos))
-		return(ROFF_ERR);
+	roffnode_push(r, tok, ln, ppos);
 
 	if ('\0' == (*bufp)[pos])
 		return(ROFF_IGN);
@@ -673,12 +663,7 @@ roff_block(ROFF_ARGS)
 	if (1 == sz && '.' == (*bufp)[sv])
 		return(ROFF_IGN);
 
-	r->last->end = malloc(sz + 1);
-
-	if (NULL == r->last->end) {
-		(*r->msg)(MANDOCERR_MEM, r->data, ln, pos, NULL);
-		return(ROFF_ERR);
-	}
+	r->last->end = mandoc_malloc(sz + 1);
 
 	memcpy(r->last->end, *bufp + sv, sz);
 	r->last->end[(int)sz] = '\0';
@@ -905,8 +890,7 @@ roff_cond(ROFF_ARGS)
 		return(ROFF_ERR);
 	}
 
-	if ( ! roffnode_push(r, tok, ln, ppos))
-		return(ROFF_ERR);
+	roffnode_push(r, tok, ln, ppos);
 
 	r->last->rule = rule;
 
Index: mandoc.1
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/mandoc.1,v
retrieving revision 1.76
retrieving revision 1.77
diff -Lmandoc.1 -Lmandoc.1 -u -p -r1.76 -r1.77
--- mandoc.1
+++ mandoc.1
@@ -23,11 +23,10 @@
 .Sh SYNOPSIS
 .Nm mandoc
 .Op Fl V
-.Op Fl f Ns Ar option
 .Op Fl m Ns Ar format
 .Op Fl O Ns Ar option
 .Op Fl T Ns Ar output
-.Op Fl W Ns Ar err
+.Op Fl W Ns Ar level
 .Op Ar file...
 .Sh DESCRIPTION
 The
@@ -37,11 +36,6 @@ utility formats
 manual pages for display.
 The arguments are as follows:
 .Bl -tag -width Ds
-.It Fl f Ns Ar option
-Comma-separated compiler options.
-See
-.Sx Compiler Options
-for details.
 .It Fl m Ns Ar format
 Input format.
 See
@@ -60,18 +54,41 @@ Defaults to
 .Fl T Ns Cm ascii .
 .It Fl V
 Print version and exit.
-.It Fl W Ns Ar err
-Comma-separated warning options.
-Use
+.It Fl W Ns Ar level
+Specify the minimum message
+.Ar level
+to be reported on the standard error output and to affect the exit status.
+The
+.Ar level
+can be
+.Cm warning ,
+.Cm error ,
+or
+.Cm fatal .
+The default is
+.Fl W Ns Cm fatal ;
 .Fl W Ns Cm all
-to print warnings,
-.Fl W Ns Cm error
-for warnings to be considered errors and cause utility
-termination.
-Multiple
-.Fl W
-arguments may be comma-separated, such as
-.Fl W Ns Cm error , Ns Cm all .
+is an alias for
+.Fl W Ns Cm warning .
+See
+.Sx EXIT STATUS
+and
+.Sx DIAGNOSTICS
+for details.
+.Pp
+The special option
+.Fl W Ns Cm stop
+tells
+.Nm
+to exit after parsing a file that causes warnings or errors of at least
+the requested level.
+No formatted output will be produced from that file.
+If both a
+.Ar level
+and
+.Cm stop
+are requested, they can be joined with a comma, for example
+.Fl W Ns Cm error , Ns Cm stop .
 .It Ar file
 Read input from zero or more files.
 If unspecified, reads from stdin.
@@ -91,8 +108,6 @@ text from stdin, implying
 and produces
 .Fl T Ns Cm ascii
 output.
-.Pp
-.Ex -std mandoc
 .Ss Input Formats
 The
 .Nm
@@ -136,39 +151,6 @@ specified and
 or
 .Fl m Ns Cm an
 is specified, then this format is used exclusively.
-.Ss Compiler Options
-Default
-.Xr mdoc 7
-and
-.Xr man 7
-compilation behaviour may be overridden with the
-.Fl f
-flag.
-.Bl -tag -width Ds
-.It Fl f Ns Cm ign-errors
-When parsing multiple files, don't halt when one errors out.
-Useful with
-.Fl T Ns Cm lint
-over a large set of manuals passed on the command line.
-.It Fl f Ns Cm ign-escape
-Ignore invalid escape sequences.
-This is the default, but the option can be used to override an earlier
-.Fl f Ns Cm strict .
-.It Fl f Ns Cm ign-scope
-When rewinding the scope of a block macro, forces the compiler to ignore
-scope violations.
-This can seriously mangle the resulting tree.
-.Pq mdoc only
-.It Fl f Ns Cm no-ign-escape
-Do not ignore invalid escape sequences.
-.It Fl f Ns Cm no-ign-macro
-Do not ignore unknown macros at the start of input lines.
-.It Fl f Ns Cm strict
-Implies
-.Fl f Ns Cm no-ign-escape
-and
-.Fl f Ns Cm no-ign-macro .
-.El
 .Ss Output Formats
 The
 .Nm
@@ -189,9 +171,7 @@ See
 .It Fl T Ns Cm lint
 Parse only: produce no output.
 Implies
-.Fl W Ns Cm all
-and
-.Fl f Ns Cm strict .
+.Fl W Ns Cm warning .
 .It Fl T Ns Cm pdf
 Produce PDF output.
 See
@@ -352,10 +332,51 @@ See
 .Sx HTML Output
 for details; beyond generating XHTML tags instead of HTML tags, these
 output modes are identical.
+.Sh EXIT STATUS
+The
+.Nm
+utility exits with one of the following values, controlled by the message
+.Ar level
+associated with the
+.Fl W
+option:
+.Pp
+.Bl -tag -width Ds -compact
+.It 0
+No warnings or errors occurred, or those that did were ignored because
+they were lower than the requested
+.Ar level .
+.It 2
+At least one warning occurred, but no error, and
+.Fl W Ns Cm warning
+was specified.
+.It 3
+At least one parsing error occurred, but no fatal error, and
+.Fl W Ns Cm error
+or
+.Fl W Ns Cm warning
+was specified.
+.It 4
+A fatal parsing error occurred.
+.It 5
+Invalid command line arguments were specified.
+No input files have been read.
+.It 6
+An operating system error occurred, for example memory exhaustion or an
+error accessing input files.
+Such errors cause
+.Nm
+to exit at once, possibly in the middle of parsing or formatting a file.
+.El
+.Pp
+Note that selecting
+.Fl T Ns Cm lint
+output mode implies
+.Fl W Ns Cm warning .
 .Sh EXAMPLES
 To page manuals to the terminal:
 .Pp
-.D1 $ mandoc \-Wall,error \-fstrict mandoc.1 2\*(Gt&1 | less
+.D1 $ mandoc \-Wall,stop mandoc.1 2\*(Gt&1 | less
 .D1 $ mandoc mandoc.1 mdoc.3 mdoc.7 | less
 .Pp
 To produce HTML manuals with
@@ -366,11 +387,74 @@ as the style-sheet:
 .Pp
 To check over a large set of manuals:
 .Pp
-.Dl $ mandoc \-Tlint \-fign-errors `find /usr/src -name \e*\e.[1-9]`
+.Dl $ mandoc \-Tlint `find /usr/src -name \e*\e.[1-9]`
 .Pp
 To produce a series of PostScript manuals for A4 paper:
 .Pp
 .D1 $ mandoc \-Tps \-Opaper=a4 mdoc.7 man.7 \*(Gt manuals.ps
+.Sh DIAGNOSTICS
+Standard error messages reporting parsing errors are prefixed by
+.Pp
+.Sm off
+.D1 Ar file : line : column : \ level :
+.Sm on
+.Pp
+where the fields have the following meanings:
+.Bl -tag -width "column"
+.It Ar file
+The name of the input file causing the message.
+.It Ar line
+The line number in that input file.
+Line numbering starts at 1.
+.It Ar column
+The column number in that input file.
+Column numbering starts at 1.
+If the issue is caused by a word, the column number usually
+points to the first character of the word.
+.It Ar level
+The message level, printed in capital letters.
+.El
+.Pp
+Message levels have the following meanings:
+.Bl -tag -width "warning"
+.It Cm fatal
+The parser is unable to parse a given input file at all.
+No formatted output is produced from that input file.
+.It Cm error
+An input file contains syntax that cannot be safely interpreted,
+either because it is invalid or because
+.Nm
+does not implement it yet.
+By discarding part of the input or inserting missing tokens,
+the parser is able to continue, and the error does not prevent
+generation of formatted output, but typically, preparing that
+output involves information loss, broken document structure
+or unintended formatting.
+.It Cm warning
+An input file uses obsolete, discouraged or non-portable syntax.
+All the same, the meaning of the input is unambiguous and a correct
+rendering can be produced.
+Documents causing warnings may render poorly when using other
+formatting tools instead of
+.Nm .
+.El
+.Pp
+Messages of the
+.Cm warning
+and
+.Cm error
+levels are hidden unless their level, or a lower level, is requested using a
+.Fl W
+option or
+.Fl T Ns Cm lint
+output mode.
+.Pp
+The
+.Nm
+utility may also print messages related to invalid command line arguments
+or operating system errors, for example when memory is exhausted or
+input files cannot be read.  Such messages do not carry the prefix
+described above.
 .Sh COMPATIBILITY
 This section summarises
 .Nm
Index: mdoc.3
===================================================================
RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/mdoc.3,v
retrieving revision 1.48
retrieving revision 1.49
diff -Lmdoc.3 -Lmdoc.3 -u -p -r1.48 -r1.49
--- mdoc.3
+++ mdoc.3
@@ -37,7 +37,6 @@
 .Fo mdoc_alloc
 .Fa "struct regset *regs"
 .Fa "void *data"
-.Fa "int pflags"
 .Fa "mandocmsg msgs"
 .Fc
 .Ft int
@@ -124,10 +123,6 @@ The
 .Fa data
 pointer is passed to
 .Fa msgs .
-The
-.Fa pflags
-arguments are defined in
-.Pa mdoc.h .
 Returns NULL on failure.
 If non-NULL, the pointer must be freed with
 .Fn mdoc_free .
@@ -338,7 +333,7 @@ int line;
 
 bzero(&regs, sizeof(struct regset));
 line = 1;
-mdoc = mdoc_alloc(&regs, NULL, 0, NULL);
+mdoc = mdoc_alloc(&regs, NULL, NULL);
 buf = NULL;
 alloc_len = 0;
 
--
 To unsubscribe send an email to source+unsubscribe@mdocml.bsd.lv

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: mdocml: Implement a simple, consistent user interface for error
  2010-08-20  1:02 mdocml: Implement a simple, consistent user interface for error schwarze
@ 2010-08-20  6:02 ` Kristaps Dzonsons
  0 siblings, 0 replies; 2+ messages in thread
From: Kristaps Dzonsons @ 2010-08-20  6:02 UTC (permalink / raw)
  To: source

> Log Message:
> -----------
> Implement a simple, consistent user interface for error handling.
> We now have sufficient practical experience to know what we want,
> so this is intended to be final:
> - provide -Wlevel (warning, error or fatal) to select what you care about
> - provide -Wstop to stop after parsing a file with warnings you care about
> - provide consistent exit status codes for those warnings you care about
> - fully document what warnings, errors and fatal errors mean
> - remove all other cruft from the user interface, less is more:
> - remove all -f knobs along with the whole -f option
> - remove the old -Werror because calling warnings "fatal" is silly
> - always finish parsing each file, unless fatal errors prevent that
> This commit also includes a couple of related simplifications behind
> the scenes regarding error handling.
> Feedback and OK  kristaps@;  Joerg Sonnenberger (NetBSD) and
> Sascha Wildner (DragonFly BSD) agree with the general direction.

Ingo, a small thing I don't have the time to ferret out:

.Dd $Mdocdate$
.Dt FOO 1
.Os
.Sh NAME
.Nm foo
.Nd bar
.Sh DESCRIPTION
\(
.Bk

Different results:

% ./mandoc -Wall foo.1 ; echo $?
foo.1:8:1: WARNING: bad escape sequence
foo.1:9:2: FATAL: scope broken, syntax violated
4
% ./mandoc -Wall,stop foo.1 ; echo $?
foo.1:8:1: WARNING: bad escape sequence
2

Am I just being dumb here?  Shouldn't the second one roll over the 
WARNING into the FATAL as well?

Kristaps
--
 To unsubscribe send an email to source+unsubscribe@mdocml.bsd.lv

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2010-08-20  6:01 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-20  1:02 mdocml: Implement a simple, consistent user interface for error schwarze
2010-08-20  6:02 ` Kristaps Dzonsons

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).