discuss@mandoc.bsd.lv
 help / color / mirror / Atom feed
* more error handling cleanup
@ 2010-08-18 16:12 Ingo Schwarze
  2010-08-18 17:43 ` Kristaps Dzonsons
  2010-08-19  9:38 ` Sascha Wildner
  0 siblings, 2 replies; 13+ messages in thread
From: Ingo Schwarze @ 2010-08-18 16:12 UTC (permalink / raw)
  To: discuss

Hi,

here is more error handling cleanup:

 * Retire -Werror.
   It is not useful.
   When running automatically, that is, in a build,
   you never want to abort due to end-of-line whitespace.
   When running interactively, you can get what you want
   in much simpler ways, without a dedicated option:
    - In case -Wall is generating lots of output, redirect
      it to a file, then process all of it.
      That is much simpler and more efficient than using -Werror
      and re-running mandoc again and again.
    - In case the run takes a long time, use
      mandoc -Tlint * 2>&1 | tee mandoc.log
      and start processing the output in parallel.
    - In case that wastes too much CPU resources and you need them
      for something else, just send SIGINT to the mandoc process
      when you are content with the amount of information you got.
   In no case do you need -Werror.
   Regarding useless knobs, less is more.

 * Improve the message complaining about bad -W arguments.

 * More cleanup in mmsg():
    - Print "WARNING:" just like "ERROR:".  We want the output
      to be easy to understand even for casual users.
    - For uniformity, use a single else-if-cascade,
      instead of nested conditionals.

 * In the manual,
    - Simplify -W documentation accordingly.
      I don't think we ever want to complicate this.
      We have made a clear plan in Rostock what FATAL, ERROR
      and WARNING are supposed to mean.  We should stick with
      that.  There is no point in making this configurable.
      We want one easy set of rules for everybody.
      Remember mandoc is intended to be used by people who like
      to write code and who need not become mdoc(7) specialists.
      This is just a tool, it is supposed to work out of the
      box, and when we start implementing complicated warning
      options, we are certainly on the wrong track.
    - Fully document the EXIT STATUS.
    - Use the DIAGNOSTICS section to explain FATAL, ERROR
      and WARNING.

OK?

Yours,
  Ingo


Index: main.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/main.c,v
retrieving revision 1.44
diff -u -p -r1.44 main.c
--- main.c	18 Aug 2010 01:03:22 -0000	1.44
+++ main.c	18 Aug 2010 16:07:53 -0000
@@ -65,9 +65,7 @@ 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. */
@@ -844,8 +842,7 @@ woptions(int *wflags, char *arg)
 	const char	*toks[3]; 
 
 	toks[0] = "all";
-	toks[1] = "error";
-	toks[2] = NULL;
+	toks[1] = NULL;
 
 	while (*arg) {
 		o = arg;
@@ -853,11 +850,8 @@ woptions(int *wflags, char *arg)
 		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);
 		}
 	}
@@ -881,23 +875,17 @@ mmsg(enum mandocerr t, void *arg, int ln
 		with_fatal = 1;
 		level = "FATAL";
 		rc = 0;
+	} else if ( ! (WARN_WALL & cp->wflags)) {
+		return(1);
+	} else if (t >= MANDOCERR_ERROR) {
+		with_error = 1;
+		level = "ERROR";
 	} 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;
-		}
+		level = "WARNING";
 	}
 
-	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, level, mandocerrs[t]);
 	if (msg)
 		fprintf(stderr, ": %s", msg);
 	fputc('\n', stderr);
Index: mandoc.1
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mandoc.1,v
retrieving revision 1.37
diff -u -p -r1.37 mandoc.1
--- mandoc.1	18 Aug 2010 01:35:01 -0000	1.37
+++ mandoc.1	18 Aug 2010 16:07:54 -0000
@@ -27,7 +27,7 @@
 .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 Cm all
 .Op Ar file...
 .Sh DESCRIPTION
 The
@@ -60,18 +60,14 @@ 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
-.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 .
+.It Fl W Ns Cm all
+Report non-fatal errors and warnings on the standard error output,
+and exit with a non-zero exit status in case non-fatal errors occur.
+See
+.Sx EXIT STATUS
+and
+.Sx DIAGNOSTICS
+for details.
 .It Ar file
 Read input from zero or more files.
 If unspecified, reads from stdin.
@@ -91,8 +87,6 @@ text from stdin, implying
 and produces
 .Fl T Ns Cm ascii
 output.
-.Pp
-.Ex -std mandoc
 .Ss Input Formats
 The
 .Nm
@@ -352,10 +346,23 @@ See
 .Sx HTML Output
 for details; beyond generating XHTML tags instead of HTML tags, these
 output modes are identical.
+.Sh EXIT STATUS
+.Ex -std mandoc
+.Pp
+By default, only fatal errors are considered for this purpose.
+When
+.Fl W Ns Cm all
+is specified, non-fatal errors cause a non-zero exit status, too.
+Warnings never cause a non-zero exit status.
+.Pp
+Note that selecting
+.Fl T Ns Cm lint
+output mode implies
+.Fl W Ns Cm all .
 .Sh EXAMPLES
 To page manuals to the terminal:
 .Pp
-.D1 $ mandoc \-Wall,error \-fstrict mandoc.1 2\*(Gt&1 | less
+.D1 $ mandoc \-Wall \-fstrict mandoc.1 2\*(Gt&1 | less
 .D1 $ mandoc mandoc.1 mdoc.3 mdoc.7 | less
 .Pp
 To produce HTML manuals with
@@ -371,6 +378,40 @@ To check over a large set of manuals:
 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
+Fatal errors occur when the parser is unable to parse a given input
+file at all.
+In this case, no formatted output is produced from that input file,
+and a message containing the string
+.Dq FATAL:
+is printed on the standard error output.
+.Pp
+When
+.Fl W Ns Cm all
+is specified, non-fatal errors and warnings are reported to the
+standard error output as well.
+The former include the string
+.Dq ERROR: ,
+the latter the string
+.Dq WARNING:
+in their message.
+.Pp
+In general, non-fatal errors indicate problems in an input file
+that might cause information loss, broken document structure
+or unintended formatting; yet, the parser is able to continue
+processing, usually by discarding part of the input, or sometimes
+by adding missing tokens.
+Quite often, the parser's guess which information to discard
+or to add will not match the author's intent.
+Thus, it is recommended to correct input documents causing
+non-fatal errors.
+.Pp
+Warnings are issued when the meaning of the input is unambiguous
+and a correct rendering can be produced, but the syntax used is
+obsolete, discouraged, or causes portability issues.
+Documents causing warnings may render poorly when using other
+formatting tools instead of
+.Nm .
 .Sh COMPATIBILITY
 This section summarises
 .Nm


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

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

* Re: more error handling cleanup
  2010-08-18 16:12 more error handling cleanup Ingo Schwarze
@ 2010-08-18 17:43 ` Kristaps Dzonsons
  2010-08-18 18:42   ` Ingo Schwarze
  2010-08-18 19:00   ` Joerg Sonnenberger
  2010-08-19  9:38 ` Sascha Wildner
  1 sibling, 2 replies; 13+ messages in thread
From: Kristaps Dzonsons @ 2010-08-18 17:43 UTC (permalink / raw)
  To: discuss

> here is more error handling cleanup:
> 
>  * Retire -Werror.
>    It is not useful.
>    When running automatically, that is, in a build,
>    you never want to abort due to end-of-line whitespace.
>    When running interactively, you can get what you want
>    in much simpler ways, without a dedicated option:
>     - In case -Wall is generating lots of output, redirect
>       it to a file, then process all of it.
>       That is much simpler and more efficient than using -Werror
>       and re-running mandoc again and again.
>     - In case the run takes a long time, use
>       mandoc -Tlint * 2>&1 | tee mandoc.log
>       and start processing the output in parallel.
>     - In case that wastes too much CPU resources and you need them
>       for something else, just send SIGINT to the mandoc process
>       when you are content with the amount of information you got.
>    In no case do you need -Werror.
>    Regarding useless knobs, less is more.

I agree that -Werror, as it is, isn't helpful.  But I don't think this
is the right way to fix things.

I think "-W" should accept the minimum message level, such as "-Wwarn"
or "-Wfatal".  A "-Whalt" should stipulate that any messages at the
given level should result in error (so the existing "-Werror" would need
to be disambiguated to "-Whalt").  However, it should do so /after/
processing the rest of the manual.  This way we get the whole message
list; the system would halt prior to formatting output.

Thoughts?

>  * Improve the message complaining about bad -W arguments.
> 
>  * More cleanup in mmsg():
>     - Print "WARNING:" just like "ERROR:".  We want the output
>       to be easy to understand even for casual users.
>     - For uniformity, use a single else-if-cascade,
>       instead of nested conditionals.

I agree with this.
--
 To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

* Re: more error handling cleanup
  2010-08-18 17:43 ` Kristaps Dzonsons
@ 2010-08-18 18:42   ` Ingo Schwarze
  2010-08-18 18:52     ` Kristaps Dzonsons
  2010-08-18 19:00   ` Joerg Sonnenberger
  1 sibling, 1 reply; 13+ messages in thread
From: Ingo Schwarze @ 2010-08-18 18:42 UTC (permalink / raw)
  To: discuss

Hi Kristaps,

Kristaps Dzonsons wrote on Wed, Aug 18, 2010 at 07:43:52PM +0200:
> Ingo Schwarze wrote:

>>  * Retire -Werror.
>>    It is not useful.
>>    When running automatically, that is, in a build,
>>    you never want to abort due to end-of-line whitespace.
>>    When running interactively, you can get what you want
>>    in much simpler ways, without a dedicated option:
>>     - In case -Wall is generating lots of output, redirect
>>       it to a file, then process all of it.
>>       That is much simpler and more efficient than using -Werror
>>       and re-running mandoc again and again.
>>     - In case the run takes a long time, use
>>       mandoc -Tlint * 2>&1 | tee mandoc.log
>>       and start processing the output in parallel.
>>     - In case that wastes too much CPU resources and you need them
>>       for something else, just send SIGINT to the mandoc process
>>       when you are content with the amount of information you got.
>>    In no case do you need -Werror.
>>    Regarding useless knobs, less is more.

> I agree that -Werror, as it is, isn't helpful.  But I don't think this
> is the right way to fix things.
> 
> I think "-W" should accept the minimum message level, such as "-Wwarn"
> or "-Wfatal".  A "-Whalt" should stipulate that any messages at the
> given level should result in error (so the existing "-Werror" would need
> to be disambiguated to "-Whalt").  However, it should do so /after/
> processing the rest of the manual.  This way we get the whole message
> list; the system would halt prior to formatting output.

Hm, i see, what you propose is not more complicated than what
i proposed, but a bit more flexible.

My proposal needs to document:

 1. -Wall
 2. fatal is always printed
 3. fatal always causes a non-zero exit status
 4. warning and error are printed with -Wall
 5. error causes a non-zero exit status with -Wall
 6. warning never causes a non-zero exit status

Your proposal just needs:

 1. -Wlevel, level can be warning (with alias all), error, fatal
 2. everything >= level is printed
 3. everything >= level causes a non-zero exit status
 4. -Wfatal is the default
 5. -Whalt (or i think i would prefer -Wstop)

So, that is even simpler.
Of course, changing -Werror badly breaks backward compatibility.
But i think mandoc is still young enough to do that, i can't
imagine anybody already relying on -Werror as it is.

Building the base system would then use -Wfatal (the default),
ports would use -Werror to catch incompatibilities,
and fall back to a groff build dependency,
and when hunting for issues, you would use -Wall as before.
Fine.

It costs us nothing to steer the exit status by the same mechanism:

#define EXIT_SUCCESS	 0
#define EXIT_WARNING	 2
#define EXIT_ERROR	 3
#define EXIT_FATAL	 4
#define EXIT_BADARG	 5
#define EXIT_SYSERR	 6

I have a fuzzy feeling that it might come in handy to reserve
the exit status 1 for future use; probably, i'm wrong, but
again, it costs us nothing.

So, i'm going back to the drawing board.
Expect a new diff.

Yours,
  Ingo
--
 To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

* Re: more error handling cleanup
  2010-08-18 18:42   ` Ingo Schwarze
@ 2010-08-18 18:52     ` Kristaps Dzonsons
  2010-08-19 17:57       ` Ingo Schwarze
  0 siblings, 1 reply; 13+ messages in thread
From: Kristaps Dzonsons @ 2010-08-18 18:52 UTC (permalink / raw)
  To: discuss

>>>  * Retire -Werror.
>>>    It is not useful.
>>>    When running automatically, that is, in a build,
>>>    you never want to abort due to end-of-line whitespace.
>>>    When running interactively, you can get what you want
>>>    in much simpler ways, without a dedicated option:
>>>     - In case -Wall is generating lots of output, redirect
>>>       it to a file, then process all of it.
>>>       That is much simpler and more efficient than using -Werror
>>>       and re-running mandoc again and again.
>>>     - In case the run takes a long time, use
>>>       mandoc -Tlint * 2>&1 | tee mandoc.log
>>>       and start processing the output in parallel.
>>>     - In case that wastes too much CPU resources and you need them
>>>       for something else, just send SIGINT to the mandoc process
>>>       when you are content with the amount of information you got.
>>>    In no case do you need -Werror.
>>>    Regarding useless knobs, less is more.
> 
>> I agree that -Werror, as it is, isn't helpful.  But I don't think this
>> is the right way to fix things.
>>
>> I think "-W" should accept the minimum message level, such as "-Wwarn"
>> or "-Wfatal".  A "-Whalt" should stipulate that any messages at the
>> given level should result in error (so the existing "-Werror" would need
>> to be disambiguated to "-Whalt").  However, it should do so /after/
>> processing the rest of the manual.  This way we get the whole message
>> list; the system would halt prior to formatting output.
> 
> Hm, i see, what you propose is not more complicated than what
> i proposed, but a bit more flexible.
> 
> My proposal needs to document:
> 
>  1. -Wall
>  2. fatal is always printed
>  3. fatal always causes a non-zero exit status
>  4. warning and error are printed with -Wall
>  5. error causes a non-zero exit status with -Wall
>  6. warning never causes a non-zero exit status
> 
> Your proposal just needs:
> 
>  1. -Wlevel, level can be warning (with alias all), error, fatal
>  2. everything >= level is printed
>  3. everything >= level causes a non-zero exit status
>  4. -Wfatal is the default
>  5. -Whalt (or i think i would prefer -Wstop)
> 
> So, that is even simpler.
> Of course, changing -Werror badly breaks backward compatibility.
> But i think mandoc is still young enough to do that, i can't
> imagine anybody already relying on -Werror as it is.
> 
> Building the base system would then use -Wfatal (the default),
> ports would use -Werror to catch incompatibilities,
> and fall back to a groff build dependency,
> and when hunting for issues, you would use -Wall as before.
> Fine.
> 
> It costs us nothing to steer the exit status by the same mechanism:
> 
> #define EXIT_SUCCESS	 0
> #define EXIT_WARNING	 2
> #define EXIT_ERROR	 3
> #define EXIT_FATAL	 4
> #define EXIT_BADARG	 5
> #define EXIT_SYSERR	 6
> 
> I have a fuzzy feeling that it might come in handy to reserve
> the exit status 1 for future use; probably, i'm wrong, but
> again, it costs us nothing.
> 
> So, i'm going back to the drawing board.
> Expect a new diff.

Ingo, I look forward to it!

My suggestion is only because my own makefiles always use [what would
be] "-Twarn,stop", as I like to have everything linted and fully-warned
just in case.  I can see "-Terror,stop" being useful for "important"
manuals, like those in the base system, and "-Tfatal,stop" for any old
manual.

I vaguely remember talking about this at supper during m210k...

I'm not concerned with the "-Werror" backward compatibility in this
regard.  All downstream people are on this list.

Thanks,

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

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

* Re: more error handling cleanup
  2010-08-18 17:43 ` Kristaps Dzonsons
  2010-08-18 18:42   ` Ingo Schwarze
@ 2010-08-18 19:00   ` Joerg Sonnenberger
  1 sibling, 0 replies; 13+ messages in thread
From: Joerg Sonnenberger @ 2010-08-18 19:00 UTC (permalink / raw)
  To: discuss

On Wed, Aug 18, 2010 at 07:43:52PM +0200, Kristaps Dzonsons wrote:
> I think "-W" should accept the minimum message level, such as "-Wwarn"
> or "-Wfatal".  A "-Whalt" should stipulate that any messages at the
> given level should result in error (so the existing "-Werror" would need
> to be disambiguated to "-Whalt").  However, it should do so /after/
> processing the rest of the manual.  This way we get the whole message
> list; the system would halt prior to formatting output.

I think that's the conclusion we reached originally. Always do the
return value computation at the end, have an option for what is
considered an error.

Joerg
--
 To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

* Re: more error handling cleanup
  2010-08-18 16:12 more error handling cleanup Ingo Schwarze
  2010-08-18 17:43 ` Kristaps Dzonsons
@ 2010-08-19  9:38 ` Sascha Wildner
  2010-08-19 13:19   ` Kristaps Dzonsons
  2010-08-19 21:55   ` Ingo Schwarze
  1 sibling, 2 replies; 13+ messages in thread
From: Sascha Wildner @ 2010-08-19  9:38 UTC (permalink / raw)
  To: discuss

On 8/18/2010 18:12, Ingo Schwarze wrote:
> Hi,
>
> here is more error handling cleanup:
>
>   * Retire -Werror.

My general thought on this is that I definitely have the future plan to 
integrate running mandoc on manpages during _buildworld_ and have the 
build break if mandoc encounters issues.

My reasoning is that only if the build breaks, you'll get the 
developer's attention in case they introduced issues in manual pages. :)

If that is still possible with the later proposals from this discussion 
(just scanned it quickly since I'm at work), I'm fine.

Sascha
--
 To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

* Re: more error handling cleanup
  2010-08-19  9:38 ` Sascha Wildner
@ 2010-08-19 13:19   ` Kristaps Dzonsons
  2010-08-20 12:18     ` Sascha Wildner
  2010-08-19 21:55   ` Ingo Schwarze
  1 sibling, 1 reply; 13+ messages in thread
From: Kristaps Dzonsons @ 2010-08-19 13:19 UTC (permalink / raw)
  To: discuss

> My general thought on this is that I definitely have the future plan to
> integrate running mandoc on manpages during _buildworld_ and have the
> build break if mandoc encounters issues.
> 
> My reasoning is that only if the build breaks, you'll get the
> developer's attention in case they introduced issues in manual pages. :)
> 
> If that is still possible with the later proposals from this discussion
> (just scanned it quickly since I'm at work), I'm fine.

Sascha,

(To answer your question: it is.)

Out of curiosity, what's your time-frame on this endeavour (if you have
one at all)?

Note, by the way, that FreeBSD's GSoC regarding mandoc appears to have
failed:

http://lists.freebsd.org/pipermail/soc-status/2010-August/000282.html

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

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

* Re: more error handling cleanup
  2010-08-18 18:52     ` Kristaps Dzonsons
@ 2010-08-19 17:57       ` Ingo Schwarze
  2010-08-19 22:47         ` Kristaps Dzonsons
  0 siblings, 1 reply; 13+ messages in thread
From: Ingo Schwarze @ 2010-08-19 17:57 UTC (permalink / raw)
  To: discuss

Hi,

i'm sending this to discuss@ again because it has implications for how
mandoc can be used, even though this posting now contains a large diff.
Feel free to ignore the diff and just look at the interface changes.

This implements what was already discussed:
- 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
- remove the old -Werror because calling warnings "fatal" is silly
- always finish parsing each file, unless fatal errors prevent that

In addition to that, it turned out this needs one other interface change:
The -fno-ign-escape and -fno-ign-macro options caused fatal error exit
without throwing a fatal message.  This was ugly with the old code;
it breaks with the new code.  As they do not really look useful in
the first place - in case you are searching for these errors, just
run -Tlint -Werror and look at the output - i removed them instead
of fixing them.

As -fign-scope had no effect anyway, no more -f options were left,
so i removed the whole -f infrastructure.  As i said, i strongly
believe we should not introduce new warning options, we want one
standard, easy way to handle all issues.  I hope the user interface
resulting from this cleanup will last for a long time, maybe for good.

This entails various technical changes behind the scenes:
 - We can now also zap the pflags from struct mdoc and struct man
   and the pflags argument from the *_alloc() functions, and thus
   even drop the now empty *_init() functions.
   While here, i adjusted the order of arguments in roff_alloc()
   to match mdoc_alloc() and man_alloc().
 - I have replaced the WARN_W* #defines by a nice enum mandoclevel
   in mandoc.h, in analogy to the existing enum mandocerr.
   In main.c, in parallel to static mandocerrs[], we now have
   static mandoclevels[] for printing and static mandoclimits[]
   to link both enums together.
 - Now that we have meaningful exit status codes, scattering
   exit(3) across lots of files becomes a problem.  Instead,
   use the mandoc_*alloc() functions we already have everywhere.
 - While doing this, i noticed that MANDOCERR_MEM had a
   misleading text:  It is not "memory exhausted" but
   "static buffer exhausted" (and should eventually go away).
 - Another consequence of using mandoc_*alloc everywhere is that
   no function needs to report errors on malloc failures any more,
   everybody just exits.  So, from the point of view of the callers,
   some cannot fail at all any more and can become void:
   pset(), resize_buf() in main.c; roffnode_push() in roff.c.
 - Yet another consequence is that *_alloc do not return any more
   when they fail, so assert()ing their result is sufficient, no
   error handling is needed any more in the caller.
 - While i kept the return value of mmsg(), telling whether the error
   is fatal, i regard it as mostly useless.  The caller always knows
   whether the error is fatal or non-fatal, so testing the return
   value is almost always useless and just making the code harder to
   read and more error-prone.  I did not scour the code to remove such
   tests, but left them out at those places i needed to touch anyway.
 - The couple of
     assert(MANDOCLEVEL_FATAL <= exit_status);
   in fdesc() maybe need an explanation.
   The parsers might contain programming errors of the following type:
   Throw a non-fatal error or a warning, then return failure.
   In that case, the main program would not produce output for the
   file, but not show a fatal message either.  These asserts are
   intended to catch such programming errors.
 - The mmsg() function has once again been rewritten and is once
   again simpler and more powerful than before.
 - The patch is shortening (+186 -347) and simplifying the code
   while adding functionality and documentation (+145 -75).

Comments, OKs?

Yours,
  Ingo


Index: chars.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/chars.c,v
retrieving revision 1.11
diff -u -p -r1.11 chars.c
--- chars.c	18 Aug 2010 02:46:37 -0000	1.11
+++ chars.c	19 Aug 2010 17:09:15 -0000
@@ -21,6 +21,7 @@
 
 #include "mandoc.h"
 #include "chars.h"
+#include "libmandoc.h"
 
 #define	PRINT_HI	 126
 #define	PRINT_LO	 32
@@ -87,17 +88,8 @@ chars_init(enum chars type)
 	 * (they're in-line re-ordered during lookup).
 	 */
 
-	tab = malloc(sizeof(struct tbl));
-	if (NULL == tab) {
-		perror(NULL);
-		exit(EXIT_FAILURE);
-	}
-
-	htab = calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
-	if (NULL == htab) {
-		perror(NULL);
-		exit(EXIT_FAILURE);
-	}
+	tab = mandoc_malloc(sizeof(struct tbl));
+	htab = mandoc_calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
 
 	for (i = 0; i < LINES_MAX; i++) {
 		hash = (int)lines[i].code[0] - PRINT_LO;
Index: html.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/html.c,v
retrieving revision 1.14
diff -u -p -r1.14 html.c
--- html.c	31 Jul 2010 21:43:07 -0000	1.14
+++ html.c	19 Aug 2010 17:09:15 -0000
@@ -30,6 +30,7 @@
 #include "chars.h"
 #include "html.h"
 #include "main.h"
+#include "libmandoc.h"
 
 struct	htmldata {
 	const char	 *name;
@@ -109,12 +110,7 @@ ml_alloc(char *outopts, enum htmltype ty
 	toks[2] = "includes";
 	toks[3] = NULL;
 
-	h = calloc(1, sizeof(struct html));
-	if (NULL == h) {
-		perror(NULL);
-		exit(EXIT_FAILURE);
-	}
-
+	h = mandoc_calloc(1, sizeof(struct html));
 	h->type = type;
 	h->tags.head = NULL;
 	h->ords.head = NULL;
@@ -391,11 +387,7 @@ print_otag(struct html *h, enum htmltag 
 	/* Push this tags onto the stack of open scopes. */
 
 	if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
-		t = malloc(sizeof(struct tag));
-		if (NULL == t) {
-			perror(NULL);
-			exit(EXIT_FAILURE);
-		}
+		t = mandoc_malloc(sizeof(struct tag));
 		t->tag = tag;
 		t->next = h->tags.head;
 		h->tags.head = t;
Index: libman.h
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/libman.h,v
retrieving revision 1.24
diff -u -p -r1.24 libman.h
--- libman.h	13 Jul 2010 01:09:12 -0000	1.24
+++ libman.h	19 Aug 2010 17:09:15 -0000
@@ -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: libmdoc.h
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/libmdoc.h,v
retrieving revision 1.41
diff -u -p -r1.41 libmdoc.h
--- libmdoc.h	13 Jul 2010 01:09:12 -0000	1.41
+++ libmdoc.h	19 Aug 2010 17:09:15 -0000
@@ -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: main.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/main.c,v
retrieving revision 1.44
diff -u -p -r1.44 main.c
--- main.c	18 Aug 2010 01:03:22 -0000	1.44
+++ main.c	19 Aug 2010 17:09:15 -0000
@@ -33,6 +33,7 @@
 #include "mdoc.h"
 #include "man.h"
 #include "roff.h"
+#include "libmandoc.h"
 
 #define	UNCONST(a)	((void *)(uintptr_t)(const void *)(a))
 
@@ -64,17 +65,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 */
@@ -88,6 +80,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",
 
@@ -164,28 +176,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[])
@@ -203,17 +210,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);
@@ -221,11 +225,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();
@@ -247,8 +251,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;
 	}
@@ -262,8 +265,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);
 }
 
 
@@ -272,7 +274,7 @@ version(void)
 {
 
 	(void)printf("%s %s\n", progname, VERSION);
-	exit(EXIT_SUCCESS);
+	exit(MANDOCLEVEL_OK);
 }
 
 
@@ -283,53 +285,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);
 }
 
 
@@ -340,7 +296,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;
 	}
 
@@ -351,24 +307,12 @@ 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) {
-		perror(NULL);
-		return(0);
-	}
-	buf->buf = tmp;
-	buf->sz = sz;
-	return(1);
+	buf->sz = buf->sz ? 2 * buf->sz : initial;
+	buf->buf = mandoc_realloc(buf->buf, buf->sz);
 }
 
 
@@ -381,7 +325,6 @@ read_whole_file(struct curparse *curp, s
 
 	if (-1 == fstat(curp->fd, &st)) {
 		perror(curp->file);
-		with_fatal = 1;
 		return(0);
 	}
 
@@ -396,7 +339,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;
@@ -423,8 +365,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) {
@@ -440,7 +381,6 @@ read_whole_file(struct curparse *curp, s
 
 	free(fb->buf);
 	fb->buf = NULL;
-	with_fatal = 1;
 	return(0);
 }
 
@@ -467,13 +407,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;
@@ -496,10 +438,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;
 			}
@@ -507,8 +447,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;
 			}
@@ -540,16 +479,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';
 
 		/*
@@ -567,10 +504,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.
@@ -580,32 +519,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). */
 
@@ -681,14 +638,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)
 {
@@ -707,39 +660,38 @@ pset(const char *buf, int pos, struct cu
 			if (' ' != buf[i] && '\t' != buf[i])
 				break;
 		if (0 == buf[i])
-			return(1);
+			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;
 }
 
 
@@ -770,8 +722,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;
@@ -793,71 +744,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;
-			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;
+			curp->wlevel = MANDOCLEVEL_FATAL;
 			break;
 		default:
-			fprintf(stderr, "%s: Bad argument\n", o);
+			fprintf(stderr, "-W%s: Bad argument\n", o);
 			return(0);
 		}
 	}
@@ -870,37 +787,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: man.3
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/man.3,v
retrieving revision 1.14
diff -u -p -r1.14 man.3
--- man.3	18 Aug 2010 02:46:37 -0000	1.14
+++ man.3	19 Aug 2010 17:09:16 -0000
@@ -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: man.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/man.c,v
retrieving revision 1.39
diff -u -p -r1.39 man.c
--- man.c	18 Aug 2010 01:17:44 -0000	1.39
+++ man.c	19 Aug 2010 17:09:16 -0000
@@ -52,7 +52,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 *
@@ -90,8 +89,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;
 
@@ -99,7 +97,6 @@ man_alloc(struct regset *regs, void *dat
 
 	man_hash_init();
 	p->data = data;
-	p->pflags = pflags;
 	p->msg = msg;
 	p->regs = regs;
 
@@ -431,19 +428,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)
 {
@@ -486,15 +470,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: man.h
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/man.h,v
retrieving revision 1.25
diff -u -p -r1.25 man.h
--- man.h	25 Jul 2010 18:05:54 -0000	1.25
+++ man.h	19 Aug 2010 17:09:16 -0000
@@ -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: man_validate.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/man_validate.c,v
retrieving revision 1.28
diff -u -p -r1.28 man_validate.c
--- man_validate.c	25 Jul 2010 18:05:54 -0000	1.28
+++ man_validate.c	19 Aug 2010 17:09:16 -0000
@@ -229,12 +229,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: mandoc.1
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mandoc.1,v
retrieving revision 1.37
diff -u -p -r1.37 mandoc.1
--- mandoc.1	18 Aug 2010 01:35:01 -0000	1.37
+++ mandoc.1	19 Aug 2010 17:09:16 -0000
@@ -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: mandoc.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mandoc.c,v
retrieving revision 1.17
diff -u -p -r1.17 mandoc.c
--- mandoc.c	18 Aug 2010 02:38:40 -0000	1.17
+++ mandoc.c	19 Aug 2010 17:09:16 -0000
@@ -195,7 +195,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);
@@ -210,7 +210,7 @@ mandoc_malloc(size_t size)
 	ptr = malloc(size);
 	if (NULL == ptr) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	return(ptr);
@@ -224,7 +224,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);
@@ -239,7 +239,7 @@ mandoc_strdup(const char *ptr)
 	p = strdup(ptr);
 	if (NULL == p) {
 		perror(NULL);
-		exit(EXIT_FAILURE);
+		exit(MANDOCLEVEL_SYSERR);
 	}
 
 	return(p);
Index: mandoc.h
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mandoc.h,v
retrieving revision 1.11
diff -u -p -r1.11 mandoc.h
--- mandoc.h	25 Jul 2010 18:05:54 -0000	1.11
+++ mandoc.h	19 Aug 2010 17:09:16 -0000
@@ -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: mdoc.3
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mdoc.3,v
retrieving revision 1.11
diff -u -p -r1.11 mdoc.3
--- mdoc.3	13 Jul 2010 01:09:13 -0000	1.11
+++ mdoc.3	19 Aug 2010 17:09:16 -0000
@@ -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;
 
Index: mdoc.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mdoc.c,v
retrieving revision 1.64
diff -u -p -r1.64 mdoc.c
--- mdoc.c	18 Aug 2010 01:17:44 -0000	1.64
+++ mdoc.c	19 Aug 2010 17:09:16 -0000
@@ -94,8 +94,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 *
@@ -187,8 +185,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;
 
@@ -196,7 +193,6 @@ mdoc_alloc(struct regset *regs, void *da
 
 	p->msg = msg;
 	p->data = data;
-	p->pflags = pflags;
 	p->regs = regs;
 
 	mdoc_hash_init();
@@ -723,21 +719,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.
@@ -781,15 +762,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: mdoc.h
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mdoc.h,v
retrieving revision 1.32
diff -u -p -r1.32 mdoc.h
--- mdoc.h	25 Jul 2010 18:05:54 -0000	1.32
+++ mdoc.h	19 Aug 2010 17:09:16 -0000
@@ -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: mdoc_html.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mdoc_html.c,v
retrieving revision 1.28
diff -u -p -r1.28 mdoc_html.c
--- mdoc_html.c	7 Aug 2010 17:26:11 -0000	1.28
+++ mdoc_html.c	19 Aug 2010 17:09:17 -0000
@@ -28,6 +28,7 @@
 #include "html.h"
 #include "mdoc.h"
 #include "main.h"
+#include "libmandoc.h"
 
 #define	INDENT		 5
 #define	HALFINDENT	 3
@@ -1176,11 +1177,7 @@ mdoc_bl_pre(MDOC_ARGS)
 	if (LIST_enum != n->data.Bl->type)
 		return(1);
 
-	ord = malloc(sizeof(struct ord));
-	if (NULL == ord) {
-		perror(NULL);
-		exit(EXIT_FAILURE);
-	}
+	ord = mandoc_malloc(sizeof(struct ord));
 	ord->cookie = n;
 	ord->pos = 1;
 	ord->next = h->ords.head;
Index: mdoc_validate.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/mdoc_validate.c,v
retrieving revision 1.67
diff -u -p -r1.67 mdoc_validate.c
--- mdoc_validate.c	31 Jul 2010 21:43:07 -0000	1.67
+++ mdoc_validate.c	19 Aug 2010 17:09:17 -0000
@@ -477,12 +477,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: roff.3
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/roff.3,v
retrieving revision 1.4
diff -u -p -r1.4 roff.3
--- roff.3	13 Jul 2010 01:09:13 -0000	1.4
+++ roff.3	19 Aug 2010 17:09:18 -0000
@@ -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: roff.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/roff.c,v
retrieving revision 1.10
diff -u -p -r1.10 roff.c
--- roff.c	31 Jul 2010 21:43:07 -0000	1.10
+++ roff.c	19 Aug 2010 17:09:18 -0000
@@ -160,7 +160,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 *);
@@ -250,16 +250,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;
@@ -267,7 +263,6 @@ roffnode_push(struct roff *r, enum rofft
 	p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
 
 	r->last = p;
-	return(1);
 }
 
 
@@ -299,15 +294,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;
@@ -634,8 +625,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);
@@ -657,12 +647,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';
@@ -889,8 +874,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: roff.h
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/roff.h,v
retrieving revision 1.3
diff -u -p -r1.3 roff.h
--- roff.h	27 Jun 2010 21:54:42 -0000	1.3
+++ roff.h	19 Aug 2010 17:09:18 -0000
@@ -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: term.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/term.c,v
retrieving revision 1.46
diff -u -p -r1.46 term.c
--- term.c	31 Jul 2010 21:43:07 -0000	1.46
+++ term.c	19 Aug 2010 17:09:18 -0000
@@ -29,6 +29,7 @@
 #include "out.h"
 #include "term.h"
 #include "main.h"
+#include "libmandoc.h"
 
 static	void		  spec(struct termp *, enum roffdeco,
 				const char *, size_t);
@@ -76,12 +77,7 @@ term_alloc(enum termenc enc)
 {
 	struct termp	*p;
 
-	p = calloc(1, sizeof(struct termp));
-	if (NULL == p) {
-		perror(NULL);
-		exit(EXIT_FAILURE);
-	}
-
+	p = mandoc_calloc(1, sizeof(struct termp));
 	p->enc = enc;
 	return(p);
 }
@@ -570,11 +566,7 @@ adjbuf(struct termp *p, size_t sz)
 	while (sz >= p->maxcols)
 		p->maxcols <<= 2;
 
-	p->buf = realloc(p->buf, p->maxcols);
-	if (NULL == p->buf) {
-		perror(NULL);
-		exit(EXIT_FAILURE);
-	}
+	p->buf = mandoc_realloc(p->buf, p->maxcols);
 }
 
 
Index: term_ps.c
===================================================================
RCS file: /cvs/src/usr.bin/mandoc/term_ps.c,v
retrieving revision 1.9
diff -u -p -r1.9 term_ps.c
--- term_ps.c	18 Aug 2010 01:30:16 -0000	1.9
+++ term_ps.c	19 Aug 2010 17:09:18 -0000
@@ -32,6 +32,7 @@
 #include "out.h"
 #include "main.h"
 #include "term.h"
+#include "libmandoc.h"
 
 /* Convert PostScript point "x" to an AFM unit. */
 #define	PNT2AFM(p, x) /* LINTED */ \
@@ -365,14 +366,9 @@ ps_growbuf(struct termp *p, size_t sz)
 
 	p->engine.ps.psmargsz += sz;
 
-	p->engine.ps.psmarg = realloc
+	p->engine.ps.psmarg = mandoc_realloc
 		(p->engine.ps.psmarg,
 		 p->engine.ps.psmargsz);
-	
-	if (NULL == p->engine.ps.psmarg) {
-		perror(NULL);
-		exit(EXIT_FAILURE);
-	}
 }
 
 static	double		  ps_hspan(const struct termp *,
@@ -603,13 +599,9 @@ pdf_obj(struct termp *p, size_t obj)
 
 	if ((obj - 1) >= p->engine.ps.pdfobjsz) {
 		p->engine.ps.pdfobjsz = obj + 128;
-		p->engine.ps.pdfobjs = realloc
+		p->engine.ps.pdfobjs = mandoc_realloc
 			(p->engine.ps.pdfobjs, 
 			 p->engine.ps.pdfobjsz * sizeof(size_t));
-		if (NULL == p->engine.ps.pdfobjs) {
-			perror(NULL);
-			exit(EXIT_FAILURE);
-		}
 	}
 
 	p->engine.ps.pdfobjs[(int)obj - 1] = p->engine.ps.pdfbytes;
--
 To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

* Re: more error handling cleanup
  2010-08-19  9:38 ` Sascha Wildner
  2010-08-19 13:19   ` Kristaps Dzonsons
@ 2010-08-19 21:55   ` Ingo Schwarze
  2010-08-20 12:31     ` Sascha Wildner
  1 sibling, 1 reply; 13+ messages in thread
From: Ingo Schwarze @ 2010-08-19 21:55 UTC (permalink / raw)
  To: discuss

Hi Sascha,

Sascha Wildner wrote on Thu, Aug 19, 2010 at 11:38:52AM +0200:
> On 8/18/2010 18:12, Ingo Schwarze wrote:

>> here is more error handling cleanup:
>>  * Retire -Werror.

In my newest patch, -Werror has been replaced by -Wstop,
which finishes parsing the file, possibly reporting more
issues, then exits mandoc before starting the formatting
frontend.

> My general thought on this is that I definitely have the future
> plan to integrate running mandoc on manpages during _buildworld_

Sounds nice; be confident that it will work, we have done it in
OpenBSD.  The 4.8 release ISOs have just been built like that and
will soon go to the CD factory.

> and have the build break if mandoc encounters issues.

You probably don't even need -Wstop for that:
I guess you are calling mandoc once for each individual manual,
are you?  Otherwise, how do you get separate output files for
the manuals?

We are using the following make(1) rule in bsd.man.mk:

  MANDOC?= mandoc

  .9.cat9 .8.cat8 .7.cat7 .6.cat6 .5.cat5 .4.cat4 .3p.cat3p .3.cat3 \
  .2.cat2 .1.cat1:
	@echo "${MANDOC} ${.IMPSRC} > ${.TARGET}"
	@${MANDOC} ${.IMPSRC} > ${.TARGET} || (rm -f ${.TARGET}; false)

To break the build in case of errors, you might put

  MANDOC?= mandoc -Werror

or to even break it in case of warnings,

  MANDOC?= mandoc -Wwarning

Right now, this won't work that well, because mandoc is still
reporting some bogus errors.  But that we be worked on.

> My reasoning is that only if the build breaks, you'll get the
> developer's attention in case they introduced issues in manual
> pages. :)

Really?  Oops.
Of course, you are welcome to use that tool if it works for you.  :-)

In OpenBSD, we are taking great care to *not* break the tree, ever.
The problem is that several people are often running builds on
slow architectures.  If you break the build, these people typically
lose days of their time, sometimes weeks.  They won't be amused.

On the other hand, Jason is scrutinizing each manual commit,
and i'm also running most manual commits through mandoc -Tlint.
So, bad style or content in manuals will rarely go undetected,
and when contacted, developers *will* listen to us.

So, were are using -Wfatal without -Wstop for building the tree,
and that won't change.  I guess ports will use -Werror to decide
whether mandoc can be used for their manuals, and fall back to
groff in case t can't.  That is not yet implemented, i plan to
work on it this autumn, in particular during the p2k10 ports
hackathon in Budapest in November.

> If that is still possible with the later proposals from this
> discussion (just scanned it quickly since I'm at work), I'm fine.

As Kristaps already said, yes, you are.

Yours,
  Ingo
--
 To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

* Re: more error handling cleanup
  2010-08-19 17:57       ` Ingo Schwarze
@ 2010-08-19 22:47         ` Kristaps Dzonsons
  2010-08-19 22:49           ` Kristaps Dzonsons
  0 siblings, 1 reply; 13+ messages in thread
From: Kristaps Dzonsons @ 2010-08-19 22:47 UTC (permalink / raw)
  To: discuss

Ingo,

I like this patch and I like what it brings us.  I like getting rid of 
-f and I like having -W levelised.  This is nice work and the code will 
be leaner and more readable when it's checked in: thank you!

I in-line-commented the nits I picked.

The only thing that bothered me was the inclusion of libmandoc.h's 
allocation functions.  I mention this in-line so I won't so it again 
here (it's the first comment).  For now, leave this part out.  We'll fix 
it in later commits.

Thanks again,

Kristaps

> Index: chars.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/chars.c,v
> retrieving revision 1.11
> diff -u -p -r1.11 chars.c
> --- chars.c     18 Aug 2010 02:46:37 -0000      1.11
> +++ chars.c     19 Aug 2010 17:09:15 -0000
> @@ -21,6 +21,7 @@
> 
>  #include "mandoc.h"
>  #include "chars.h"
> +#include "libmandoc.h"
> 
>  #define        PRINT_HI         126
>  #define        PRINT_LO         32
> @@ -87,17 +88,8 @@ chars_init(enum chars type)
>          * (they're in-line re-ordered during lookup).
>          */
> 
> -       tab = malloc(sizeof(struct tbl));
> -       if (NULL == tab) {
> -               perror(NULL);
> -               exit(EXIT_FAILURE);
> -       }
> -
> -       htab = calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
> -       if (NULL == htab) {
> -               perror(NULL);
> -               exit(EXIT_FAILURE);
> -       }
> +       tab = mandoc_malloc(sizeof(struct tbl));
> +       htab = mandoc_calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **));
> 
>         for (i = 0; i < LINES_MAX; i++) {
>                 hash = (int)lines[i].code[0] - PRINT_LO;

This is not correct.  libmandoc is only for use by libroff, libmdoc, and 
libman.  chars.o is linked directly into the mandoc binary.

In order to do this, the libmandoc mandoc_*alloc() functions would need 
to be put into mandoc.h, and another file to implement this.  For now, 
let's leave it as is.


> Index: html.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/html.c,v
> retrieving revision 1.14
> diff -u -p -r1.14 html.c
> --- html.c      31 Jul 2010 21:43:07 -0000      1.14
> +++ html.c      19 Aug 2010 17:09:15 -0000
> @@ -30,6 +30,7 @@
>  #include "chars.h"
>  #include "html.h"
>  #include "main.h"
> +#include "libmandoc.h"
> 
>  struct htmldata {
>         const char       *name;
> @@ -109,12 +110,7 @@ ml_alloc(char *outopts, enum htmltype ty
>         toks[2] = "includes";
>         toks[3] = NULL;
> 
> -       h = calloc(1, sizeof(struct html));
> -       if (NULL == h) {
> -               perror(NULL);
> -               exit(EXIT_FAILURE);
> -       }
> -
> +       h = mandoc_calloc(1, sizeof(struct html));
>         h->type = type;
>         h->tags.head = NULL;
>         h->ords.head = NULL;
> @@ -391,11 +387,7 @@ print_otag(struct html *h, enum htmltag
>         /* Push this tags onto the stack of open scopes. */
> 
>         if ( ! (HTML_NOSTACK & htmltags[tag].flags)) {
> -               t = malloc(sizeof(struct tag));
> -               if (NULL == t) {
> -                       perror(NULL);
> -                       exit(EXIT_FAILURE);
> -               }
> +               t = mandoc_malloc(sizeof(struct tag));
>                 t->tag = tag;
>                 t->next = h->tags.head;
>                 h->tags.head = t;

Same here as above.

> Index: libman.h
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/libman.h,v
> retrieving revision 1.24
> diff -u -p -r1.24 libman.h
> --- libman.h    13 Jul 2010 01:09:12 -0000      1.24
> +++ libman.h    19 Aug 2010 17:09:15 -0000
> @@ -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: libmdoc.h
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/libmdoc.h,v
> retrieving revision 1.41
> diff -u -p -r1.41 libmdoc.h
> --- libmdoc.h   13 Jul 2010 01:09:12 -0000      1.41
> +++ libmdoc.h   19 Aug 2010 17:09:15 -0000
> @@ -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: main.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/main.c,v
> retrieving revision 1.44
> diff -u -p -r1.44 main.c
> --- main.c      18 Aug 2010 01:03:22 -0000      1.44
> +++ main.c      19 Aug 2010 17:09:15 -0000
> @@ -33,6 +33,7 @@
>  #include "mdoc.h"
>  #include "man.h"
>  #include "roff.h"
> +#include "libmandoc.h"

...and same here again...

> 
>  #define        UNCONST(a)      ((void *)(uintptr_t)(const void *)(a))
> 
> @@ -64,17 +65,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 */
> @@ -88,6 +80,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",
> 
> @@ -164,28 +176,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[])
> @@ -203,17 +210,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);
> @@ -221,11 +225,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();
> @@ -247,8 +251,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;
>         }
> @@ -262,8 +265,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);
>  }
> 
> 
> @@ -272,7 +274,7 @@ version(void)
>  {
> 
>         (void)printf("%s %s\n", progname, VERSION);
> -       exit(EXIT_SUCCESS);
> +       exit(MANDOCLEVEL_OK);
>  }
> 
> 
> @@ -283,53 +285,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);
>  }
> 
> 
> @@ -340,7 +296,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;
>         }
> 
> @@ -351,24 +307,12 @@ 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) {
> -               perror(NULL);
> -               return(0);
> -       }
> -       buf->buf = tmp;
> -       buf->sz = sz;
> -       return(1);
> +       buf->sz = buf->sz ? 2 * buf->sz : initial;
> +       buf->buf = mandoc_realloc(buf->buf, buf->sz);
>  }

Note that I'm ok with this function returning void, although the same 
again goes with mandoc_realloc...

> 
> 
> @@ -381,7 +325,6 @@ read_whole_file(struct curparse *curp, s
> 
>         if (-1 == fstat(curp->fd, &st)) {
>                 perror(curp->file);
> -               with_fatal = 1;
>                 return(0);
>         }
> 
> @@ -396,7 +339,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;
> @@ -423,8 +365,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) {
> @@ -440,7 +381,6 @@ read_whole_file(struct curparse *curp, s
> 
>         free(fb->buf);
>         fb->buf = NULL;
> -       with_fatal = 1;
>         return(0);
>  }
> 
> @@ -467,13 +407,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;
> @@ -496,10 +438,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;
>                         }
> @@ -507,8 +447,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;
>                         }
> @@ -540,16 +479,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';
> 
>                 /*
> @@ -567,10 +504,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.
> @@ -580,32 +519,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). */
> 
> @@ -681,14 +638,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)
>  {
> @@ -707,39 +660,38 @@ pset(const char *buf, int pos, struct cu
>                         if (' ' != buf[i] && '\t' != buf[i])
>                                 break;
>                 if (0 == buf[i])

Addendum: this should be '\0' == buf[i]...

> -                       return(1);
> +                       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;
>  }
> 
> 
> @@ -770,8 +722,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;
> @@ -793,71 +744,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;
> -                       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;
> +                       curp->wlevel = MANDOCLEVEL_FATAL;
>                         break;
>                 default:
> -                       fprintf(stderr, "%s: Bad argument\n", o);
> +                       fprintf(stderr, "-W%s: Bad argument\n", o);
>                         return(0);
>                 }
>         }
> @@ -870,37 +787,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: man.3
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/man.3,v
> retrieving revision 1.14
> diff -u -p -r1.14 man.3
> --- man.3       18 Aug 2010 02:46:37 -0000      1.14
> +++ man.3       19 Aug 2010 17:09:16 -0000
> @@ -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: man.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/man.c,v
> retrieving revision 1.39
> diff -u -p -r1.39 man.c
> --- man.c       18 Aug 2010 01:17:44 -0000      1.39
> +++ man.c       19 Aug 2010 17:09:16 -0000
> @@ -52,7 +52,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 *
> @@ -90,8 +89,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;
> 
> @@ -99,7 +97,6 @@ man_alloc(struct regset *regs, void *dat
> 
>         man_hash_init();
>         p->data = data;
> -       p->pflags = pflags;
>         p->msg = msg;
>         p->regs = regs;
> 
> @@ -431,19 +428,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)
>  {
> @@ -486,15 +470,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 ? "..." : "");

Ooh, I like this.

>                 return(1);
>         }
> 
> Index: man.h
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/man.h,v
> retrieving revision 1.25
> diff -u -p -r1.25 man.h
> --- man.h       25 Jul 2010 18:05:54 -0000      1.25
> +++ man.h       19 Aug 2010 17:09:16 -0000
> @@ -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: man_validate.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/man_validate.c,v
> retrieving revision 1.28
> diff -u -p -r1.28 man_validate.c
> --- man_validate.c      25 Jul 2010 18:05:54 -0000      1.28
> +++ man_validate.c      19 Aug 2010 17:09:16 -0000
> @@ -229,12 +229,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: mandoc.1
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/mandoc.1,v
> retrieving revision 1.37
> diff -u -p -r1.37 mandoc.1
> --- mandoc.1    18 Aug 2010 01:35:01 -0000      1.37
> +++ mandoc.1    19 Aug 2010 17:09:16 -0000
> @@ -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: mandoc.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/mandoc.c,v
> retrieving revision 1.17
> diff -u -p -r1.17 mandoc.c
> --- mandoc.c    18 Aug 2010 02:38:40 -0000      1.17
> +++ mandoc.c    19 Aug 2010 17:09:16 -0000
> @@ -195,7 +195,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);
> @@ -210,7 +210,7 @@ mandoc_malloc(size_t size)
>         ptr = malloc(size);
>         if (NULL == ptr) {
>                 perror(NULL);
> -               exit(EXIT_FAILURE);
> +               exit(MANDOCLEVEL_SYSERR);
>         }
> 
>         return(ptr);
> @@ -224,7 +224,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);
> @@ -239,7 +239,7 @@ mandoc_strdup(const char *ptr)
>         p = strdup(ptr);
>         if (NULL == p) {
>                 perror(NULL);
> -               exit(EXIT_FAILURE);
> +               exit(MANDOCLEVEL_SYSERR);
>         }
> 
>         return(p);
> Index: mandoc.h
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/mandoc.h,v
> retrieving revision 1.11
> diff -u -p -r1.11 mandoc.h
> --- mandoc.h    25 Jul 2010 18:05:54 -0000      1.11
> +++ mandoc.h    19 Aug 2010 17:09:16 -0000
> @@ -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: mdoc.3
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/mdoc.3,v
> retrieving revision 1.11
> diff -u -p -r1.11 mdoc.3
> --- mdoc.3      13 Jul 2010 01:09:13 -0000      1.11
> +++ mdoc.3      19 Aug 2010 17:09:16 -0000
> @@ -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;
> 
> Index: mdoc.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/mdoc.c,v
> retrieving revision 1.64
> diff -u -p -r1.64 mdoc.c
> --- mdoc.c      18 Aug 2010 01:17:44 -0000      1.64
> +++ mdoc.c      19 Aug 2010 17:09:16 -0000
> @@ -94,8 +94,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 *
> @@ -187,8 +185,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;
> 
> @@ -196,7 +193,6 @@ mdoc_alloc(struct regset *regs, void *da
> 
>         p->msg = msg;
>         p->data = data;
> -       p->pflags = pflags;
>         p->regs = regs;
> 
>         mdoc_hash_init();
> @@ -723,21 +719,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.
> @@ -781,15 +762,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 ? "..." : "");

I'll say it again: I like this.

>                 return(1);
>         }
> 
> Index: mdoc.h
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/mdoc.h,v
> retrieving revision 1.32
> diff -u -p -r1.32 mdoc.h
> --- mdoc.h      25 Jul 2010 18:05:54 -0000      1.32
> +++ mdoc.h      19 Aug 2010 17:09:16 -0000
> @@ -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: mdoc_html.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/mdoc_html.c,v
> retrieving revision 1.28
> diff -u -p -r1.28 mdoc_html.c
> --- mdoc_html.c 7 Aug 2010 17:26:11 -0000       1.28
> +++ mdoc_html.c 19 Aug 2010 17:09:17 -0000
> @@ -28,6 +28,7 @@
>  #include "html.h"
>  #include "mdoc.h"
>  #include "main.h"
> +#include "libmandoc.h"
> 
>  #define        INDENT           5
>  #define        HALFINDENT       3
> @@ -1176,11 +1177,7 @@ mdoc_bl_pre(MDOC_ARGS)
>         if (LIST_enum != n->data.Bl->type)
>                 return(1);
> 
> -       ord = malloc(sizeof(struct ord));
> -       if (NULL == ord) {
> -               perror(NULL);
> -               exit(EXIT_FAILURE);
> -       }
> +       ord = mandoc_malloc(sizeof(struct ord));

Same again... mandoc_malloc is in libmandoc.h, not mandoc.h, and thus 
only in the parsers (as it's implemented now).

>         ord->cookie = n;
>         ord->pos = 1;
>         ord->next = h->ords.head;
> Index: mdoc_validate.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/mdoc_validate.c,v
> retrieving revision 1.67
> diff -u -p -r1.67 mdoc_validate.c
> --- mdoc_validate.c     31 Jul 2010 21:43:07 -0000      1.67
> +++ mdoc_validate.c     19 Aug 2010 17:09:17 -0000
> @@ -477,12 +477,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: roff.3
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/roff.3,v
> retrieving revision 1.4
> diff -u -p -r1.4 roff.3
> --- roff.3      13 Jul 2010 01:09:13 -0000      1.4
> +++ roff.3      19 Aug 2010 17:09:18 -0000
> @@ -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: roff.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/roff.c,v
> retrieving revision 1.10
> diff -u -p -r1.10 roff.c
> --- roff.c      31 Jul 2010 21:43:07 -0000      1.10
> +++ roff.c      19 Aug 2010 17:09:18 -0000
> @@ -160,7 +160,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 *);
> @@ -250,16 +250,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));

Counterexample: this is fine cause it's in libroff.

>         p->tok = tok;
>         p->parent = r->last;
>         p->line = line;
> @@ -267,7 +263,6 @@ roffnode_push(struct roff *r, enum rofft
>         p->rule = p->parent ? p->parent->rule : ROFFRULE_DENY;
> 
>         r->last = p;
> -       return(1);
>  }
> 
> 
> @@ -299,15 +294,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;
> @@ -634,8 +625,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);
> @@ -657,12 +647,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';
> @@ -889,8 +874,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: roff.h
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/roff.h,v
> retrieving revision 1.3
> diff -u -p -r1.3 roff.h
> --- roff.h      27 Jun 2010 21:54:42 -0000      1.3
> +++ roff.h      19 Aug 2010 17:09:18 -0000
> @@ -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: term.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/term.c,v
> retrieving revision 1.46
> diff -u -p -r1.46 term.c
> --- term.c      31 Jul 2010 21:43:07 -0000      1.46
> +++ term.c      19 Aug 2010 17:09:18 -0000
> @@ -29,6 +29,7 @@
>  #include "out.h"
>  #include "term.h"
>  #include "main.h"
> +#include "libmandoc.h"

Nope: libmandoc/mandoc.h.

> 
>  static void              spec(struct termp *, enum roffdeco,
>                                 const char *, size_t);
> @@ -76,12 +77,7 @@ term_alloc(enum termenc enc)
>  {
>         struct termp    *p;
> 
> -       p = calloc(1, sizeof(struct termp));
> -       if (NULL == p) {
> -               perror(NULL);
> -               exit(EXIT_FAILURE);
> -       }
> -
> +       p = mandoc_calloc(1, sizeof(struct termp));
>         p->enc = enc;
>         return(p);
>  }
> @@ -570,11 +566,7 @@ adjbuf(struct termp *p, size_t sz)
>         while (sz >= p->maxcols)
>                 p->maxcols <<= 2;
> 
> -       p->buf = realloc(p->buf, p->maxcols);
> -       if (NULL == p->buf) {
> -               perror(NULL);
> -               exit(EXIT_FAILURE);
> -       }
> +       p->buf = mandoc_realloc(p->buf, p->maxcols);
>  }
> 
> 
> Index: term_ps.c
> ===================================================================
> RCS file: /cvs/src/usr.bin/mandoc/term_ps.c,v
> retrieving revision 1.9
> diff -u -p -r1.9 term_ps.c
> --- term_ps.c   18 Aug 2010 01:30:16 -0000      1.9
> +++ term_ps.c   19 Aug 2010 17:09:18 -0000
> @@ -32,6 +32,7 @@
>  #include "out.h"
>  #include "main.h"
>  #include "term.h"
> +#include "libmandoc.h"
> 
>  /* Convert PostScript point "x" to an AFM unit. */
>  #define        PNT2AFM(p, x) /* LINTED */ \
> @@ -365,14 +366,9 @@ ps_growbuf(struct termp *p, size_t sz)
> 
>         p->engine.ps.psmargsz += sz;
> 
> -       p->engine.ps.psmarg = realloc
> +       p->engine.ps.psmarg = mandoc_realloc
>                 (p->engine.ps.psmarg,
>                  p->engine.ps.psmargsz);

Nope...

> -
> -       if (NULL == p->engine.ps.psmarg) {
> -               perror(NULL);
> -               exit(EXIT_FAILURE);
> -       }
>  }
> 
>  static double            ps_hspan(const struct termp *,
> @@ -603,13 +599,9 @@ pdf_obj(struct termp *p, size_t obj)
> 
>         if ((obj - 1) >= p->engine.ps.pdfobjsz) {
>                 p->engine.ps.pdfobjsz = obj + 128;
> -               p->engine.ps.pdfobjs = realloc
> +               p->engine.ps.pdfobjs = mandoc_realloc
>                         (p->engine.ps.pdfobjs,
>                          p->engine.ps.pdfobjsz * sizeof(size_t));
> -               if (NULL == p->engine.ps.pdfobjs) {
> -                       perror(NULL);
> -                       exit(EXIT_FAILURE);
> -               }
>         }
> 
>         p->engine.ps.pdfobjs[(int)obj - 1] = p->engine.ps.pdfbytes;
> --
>  To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

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

* Re: more error handling cleanup
  2010-08-19 22:47         ` Kristaps Dzonsons
@ 2010-08-19 22:49           ` Kristaps Dzonsons
  0 siblings, 0 replies; 13+ messages in thread
From: Kristaps Dzonsons @ 2010-08-19 22:49 UTC (permalink / raw)
  To: discuss

...Note that I didn't look over the *.[1-9] changes, just the code...
--
 To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

* Re: more error handling cleanup
  2010-08-19 13:19   ` Kristaps Dzonsons
@ 2010-08-20 12:18     ` Sascha Wildner
  0 siblings, 0 replies; 13+ messages in thread
From: Sascha Wildner @ 2010-08-20 12:18 UTC (permalink / raw)
  To: discuss; +Cc: Kristaps Dzonsons

On 8/19/2010 15:19, Kristaps Dzonsons wrote:
> Out of curiosity, what's your time-frame on this endeavour (if you have
> one at all)?

I really don't know. Time is usually tight and our team is very small.

Sascha
--
 To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

* Re: more error handling cleanup
  2010-08-19 21:55   ` Ingo Schwarze
@ 2010-08-20 12:31     ` Sascha Wildner
  0 siblings, 0 replies; 13+ messages in thread
From: Sascha Wildner @ 2010-08-20 12:31 UTC (permalink / raw)
  To: discuss; +Cc: Ingo Schwarze

On 8/19/2010 23:55, Ingo Schwarze wrote:
> You probably don't even need -Wstop for that:
> I guess you are calling mandoc once for each individual manual,
> are you?  Otherwise, how do you get separate output files for
> the manuals?

Yeah but my idea was not necessarily to format all pages to files that 
would get installed. Only unformatted pages get installed and they get 
formatted when man is run and the formatted page is older than the 
unformatted one or if a formatted version doesn't exist.

My idea was more about running mandoc during build-/quickworld for 
checking the pages only and whining if a regression was introduced.

>> My reasoning is that only if the build breaks, you'll get the
>> developer's attention in case they introduced issues in manual
>> pages. :)
>
> Really?  Oops.
> Of course, you are welcome to use that tool if it works for you.  :-)
>
> In OpenBSD, we are taking great care to *not* break the tree, ever.
> The problem is that several people are often running builds on
> slow architectures.  If you break the build, these people typically
> lose days of their time, sometimes weeks.  They won't be amused.

Well if the build breaks on some manpage that previously formatted 
without problems, obviously someone didn't check their changes to it 
with a build- or quickworld before committing. I'd like to have it work 
the same way as the compiler does for the source. Someone commits a 
manpage error -> build breaks.

> On the other hand, Jason is scrutinizing each manual commit,
> and i'm also running most manual commits through mandoc -Tlint.
> So, bad style or content in manuals will rarely go undetected,
> and when contacted, developers *will* listen to us.

Sure, I do the same in DragonFly at the moment and by having buildworld 
break if some mandoc problem was introduced in a commit that wasn't 
properly tested, I'd like to offload that work to those who committed 
the problem. It just eats too much of my time to review manual page work 
and also I'd like to counter a certain "Sascha will fix it anyways" 
attitude. :-)

Sascha
--
 To unsubscribe send an email to discuss+unsubscribe@mdocml.bsd.lv

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

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

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-08-18 16:12 more error handling cleanup Ingo Schwarze
2010-08-18 17:43 ` Kristaps Dzonsons
2010-08-18 18:42   ` Ingo Schwarze
2010-08-18 18:52     ` Kristaps Dzonsons
2010-08-19 17:57       ` Ingo Schwarze
2010-08-19 22:47         ` Kristaps Dzonsons
2010-08-19 22:49           ` Kristaps Dzonsons
2010-08-18 19:00   ` Joerg Sonnenberger
2010-08-19  9:38 ` Sascha Wildner
2010-08-19 13:19   ` Kristaps Dzonsons
2010-08-20 12:18     ` Sascha Wildner
2010-08-19 21:55   ` Ingo Schwarze
2010-08-20 12:31     ` Sascha Wildner

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).