xref: /dragonfly/contrib/mdocml/mandoc_msg.c (revision 99db7d0e)
1*99db7d0eSSascha Wildner /* $OpenBSD: mandoc_msg.c,v 1.8 2020/01/19 17:59:01 schwarze Exp $ */
254ba9607SSascha Wildner /*
3*99db7d0eSSascha Wildner  * Copyright (c) 2014-2021 Ingo Schwarze <schwarze@openbsd.org>
454ba9607SSascha Wildner  * Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
554ba9607SSascha Wildner  *
654ba9607SSascha Wildner  * Permission to use, copy, modify, and distribute this software for any
754ba9607SSascha Wildner  * purpose with or without fee is hereby granted, provided that the above
854ba9607SSascha Wildner  * copyright notice and this permission notice appear in all copies.
954ba9607SSascha Wildner  *
1054ba9607SSascha Wildner  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1154ba9607SSascha Wildner  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1254ba9607SSascha Wildner  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1354ba9607SSascha Wildner  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1454ba9607SSascha Wildner  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1554ba9607SSascha Wildner  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1654ba9607SSascha Wildner  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*99db7d0eSSascha Wildner  *
18*99db7d0eSSascha Wildner  * Implementation of warning and error messages for mandoc(1).
1954ba9607SSascha Wildner  */
2054ba9607SSascha Wildner #include "config.h"
2154ba9607SSascha Wildner 
2254ba9607SSascha Wildner #include <stdarg.h>
2354ba9607SSascha Wildner #include <stdio.h>
2454ba9607SSascha Wildner #include <stdlib.h>
2554ba9607SSascha Wildner 
2654ba9607SSascha Wildner #include "mandoc.h"
2754ba9607SSascha Wildner 
2854ba9607SSascha Wildner static	const enum mandocerr lowest_type[MANDOCLEVEL_MAX] = {
2954ba9607SSascha Wildner 	MANDOCERR_OK,
3054ba9607SSascha Wildner 	MANDOCERR_OK,
3154ba9607SSascha Wildner 	MANDOCERR_WARNING,
3254ba9607SSascha Wildner 	MANDOCERR_ERROR,
3354ba9607SSascha Wildner 	MANDOCERR_UNSUPP,
34*99db7d0eSSascha Wildner 	MANDOCERR_BADARG,
35*99db7d0eSSascha Wildner 	MANDOCERR_SYSERR
3654ba9607SSascha Wildner };
3754ba9607SSascha Wildner 
3854ba9607SSascha Wildner static	const char *const level_name[MANDOCLEVEL_MAX] = {
3954ba9607SSascha Wildner 	"SUCCESS",
4054ba9607SSascha Wildner 	"STYLE",
4154ba9607SSascha Wildner 	"WARNING",
4254ba9607SSascha Wildner 	"ERROR",
4354ba9607SSascha Wildner 	"UNSUPP",
4454ba9607SSascha Wildner 	"BADARG",
4554ba9607SSascha Wildner 	"SYSERR"
4654ba9607SSascha Wildner };
4754ba9607SSascha Wildner 
4854ba9607SSascha Wildner static	const char *const type_message[MANDOCERR_MAX] = {
4954ba9607SSascha Wildner 	"ok",
5054ba9607SSascha Wildner 
5154ba9607SSascha Wildner 	"base system convention",
5254ba9607SSascha Wildner 
5354ba9607SSascha Wildner 	"Mdocdate found",
5454ba9607SSascha Wildner 	"Mdocdate missing",
5554ba9607SSascha Wildner 	"unknown architecture",
5654ba9607SSascha Wildner 	"operating system explicitly specified",
5754ba9607SSascha Wildner 	"RCS id missing",
5854ba9607SSascha Wildner 
5954ba9607SSascha Wildner 	"generic style suggestion",
6054ba9607SSascha Wildner 
6154ba9607SSascha Wildner 	"legacy man(7) date format",
6254ba9607SSascha Wildner 	"normalizing date format to",
6354ba9607SSascha Wildner 	"lower case character in document title",
6454ba9607SSascha Wildner 	"duplicate RCS id",
6554ba9607SSascha Wildner 	"possible typo in section name",
6654ba9607SSascha Wildner 	"unterminated quoted argument",
6754ba9607SSascha Wildner 	"useless macro",
6854ba9607SSascha Wildner 	"consider using OS macro",
6954ba9607SSascha Wildner 	"errnos out of order",
7054ba9607SSascha Wildner 	"duplicate errno",
71*99db7d0eSSascha Wildner 	"referenced manual not found",
7254ba9607SSascha Wildner 	"trailing delimiter",
7354ba9607SSascha Wildner 	"no blank before trailing delimiter",
7454ba9607SSascha Wildner 	"fill mode already enabled, skipping",
7554ba9607SSascha Wildner 	"fill mode already disabled, skipping",
76*99db7d0eSSascha Wildner 	"input text line longer than 80 bytes",
7754ba9607SSascha Wildner 	"verbatim \"--\", maybe consider using \\(em",
7854ba9607SSascha Wildner 	"function name without markup",
7954ba9607SSascha Wildner 	"whitespace at end of input line",
8054ba9607SSascha Wildner 	"bad comment style",
8154ba9607SSascha Wildner 
8254ba9607SSascha Wildner 	"generic warning",
8354ba9607SSascha Wildner 
8454ba9607SSascha Wildner 	/* related to the prologue */
8554ba9607SSascha Wildner 	"missing manual title, using UNTITLED",
8654ba9607SSascha Wildner 	"missing manual title, using \"\"",
8754ba9607SSascha Wildner 	"missing manual section, using \"\"",
8854ba9607SSascha Wildner 	"unknown manual section",
89*99db7d0eSSascha Wildner 	"filename/section mismatch",
90*99db7d0eSSascha Wildner 	"missing date, using \"\"",
9154ba9607SSascha Wildner 	"cannot parse date, using it verbatim",
9254ba9607SSascha Wildner 	"date in the future, using it anyway",
9354ba9607SSascha Wildner 	"missing Os macro, using \"\"",
9454ba9607SSascha Wildner 	"late prologue macro",
9554ba9607SSascha Wildner 	"prologue macros out of order",
9654ba9607SSascha Wildner 
9754ba9607SSascha Wildner 	/* related to document structure */
9854ba9607SSascha Wildner 	".so is fragile, better use ln(1)",
9954ba9607SSascha Wildner 	"no document body",
10054ba9607SSascha Wildner 	"content before first section header",
10154ba9607SSascha Wildner 	"first section is not \"NAME\"",
10254ba9607SSascha Wildner 	"NAME section without Nm before Nd",
10354ba9607SSascha Wildner 	"NAME section without description",
10454ba9607SSascha Wildner 	"description not at the end of NAME",
10554ba9607SSascha Wildner 	"bad NAME section content",
10654ba9607SSascha Wildner 	"missing comma before name",
10754ba9607SSascha Wildner 	"missing description line, using \"\"",
10854ba9607SSascha Wildner 	"description line outside NAME section",
10954ba9607SSascha Wildner 	"sections out of conventional order",
11054ba9607SSascha Wildner 	"duplicate section title",
11154ba9607SSascha Wildner 	"unexpected section",
11254ba9607SSascha Wildner 	"cross reference to self",
11354ba9607SSascha Wildner 	"unusual Xr order",
11454ba9607SSascha Wildner 	"unusual Xr punctuation",
11554ba9607SSascha Wildner 	"AUTHORS section without An macro",
11654ba9607SSascha Wildner 
11754ba9607SSascha Wildner 	/* related to macros and nesting */
11854ba9607SSascha Wildner 	"obsolete macro",
11954ba9607SSascha Wildner 	"macro neither callable nor escaped",
12054ba9607SSascha Wildner 	"skipping paragraph macro",
12154ba9607SSascha Wildner 	"moving paragraph macro out of list",
12254ba9607SSascha Wildner 	"skipping no-space macro",
12354ba9607SSascha Wildner 	"blocks badly nested",
12454ba9607SSascha Wildner 	"nested displays are not portable",
12554ba9607SSascha Wildner 	"moving content out of list",
12654ba9607SSascha Wildner 	"first macro on line",
12754ba9607SSascha Wildner 	"line scope broken",
12854ba9607SSascha Wildner 	"skipping blank line in line scope",
12954ba9607SSascha Wildner 
13054ba9607SSascha Wildner 	/* related to missing macro arguments */
13154ba9607SSascha Wildner 	"skipping empty request",
13254ba9607SSascha Wildner 	"conditional request controls empty scope",
13354ba9607SSascha Wildner 	"skipping empty macro",
13454ba9607SSascha Wildner 	"empty block",
13554ba9607SSascha Wildner 	"empty argument, using 0n",
13654ba9607SSascha Wildner 	"missing display type, using -ragged",
13754ba9607SSascha Wildner 	"list type is not the first argument",
13854ba9607SSascha Wildner 	"missing -width in -tag list, using 6n",
13954ba9607SSascha Wildner 	"missing utility name, using \"\"",
14054ba9607SSascha Wildner 	"missing function name, using \"\"",
14154ba9607SSascha Wildner 	"empty head in list item",
14254ba9607SSascha Wildner 	"empty list item",
14354ba9607SSascha Wildner 	"missing argument, using next line",
14454ba9607SSascha Wildner 	"missing font type, using \\fR",
14554ba9607SSascha Wildner 	"unknown font type, using \\fR",
14654ba9607SSascha Wildner 	"nothing follows prefix",
14754ba9607SSascha Wildner 	"empty reference block",
14854ba9607SSascha Wildner 	"missing section argument",
14954ba9607SSascha Wildner 	"missing -std argument, adding it",
15054ba9607SSascha Wildner 	"missing option string, using \"\"",
15154ba9607SSascha Wildner 	"missing resource identifier, using \"\"",
15254ba9607SSascha Wildner 	"missing eqn box, using \"\"",
15354ba9607SSascha Wildner 
15454ba9607SSascha Wildner 	/* related to bad macro arguments */
15554ba9607SSascha Wildner 	"duplicate argument",
15654ba9607SSascha Wildner 	"skipping duplicate argument",
15754ba9607SSascha Wildner 	"skipping duplicate display type",
15854ba9607SSascha Wildner 	"skipping duplicate list type",
15954ba9607SSascha Wildner 	"skipping -width argument",
16054ba9607SSascha Wildner 	"wrong number of cells",
16154ba9607SSascha Wildner 	"unknown AT&T UNIX version",
16254ba9607SSascha Wildner 	"comma in function argument",
16354ba9607SSascha Wildner 	"parenthesis in function name",
16454ba9607SSascha Wildner 	"unknown library name",
16554ba9607SSascha Wildner 	"invalid content in Rs block",
16654ba9607SSascha Wildner 	"invalid Boolean argument",
16754ba9607SSascha Wildner 	"argument contains two font escapes",
16854ba9607SSascha Wildner 	"unknown font, skipping request",
16954ba9607SSascha Wildner 	"odd number of characters in request",
17054ba9607SSascha Wildner 
17154ba9607SSascha Wildner 	/* related to plain text */
17254ba9607SSascha Wildner 	"blank line in fill mode, using .sp",
17354ba9607SSascha Wildner 	"tab in filled text",
17454ba9607SSascha Wildner 	"new sentence, new line",
17554ba9607SSascha Wildner 	"invalid escape sequence",
17654ba9607SSascha Wildner 	"undefined escape, printing literally",
17754ba9607SSascha Wildner 	"undefined string, using \"\"",
17854ba9607SSascha Wildner 
17954ba9607SSascha Wildner 	/* related to tables */
18054ba9607SSascha Wildner 	"tbl line starts with span",
18154ba9607SSascha Wildner 	"tbl column starts with span",
18254ba9607SSascha Wildner 	"skipping vertical bar in tbl layout",
18354ba9607SSascha Wildner 
18454ba9607SSascha Wildner 	"generic error",
18554ba9607SSascha Wildner 
18654ba9607SSascha Wildner 	/* related to tables */
18754ba9607SSascha Wildner 	"non-alphabetic character in tbl options",
18854ba9607SSascha Wildner 	"skipping unknown tbl option",
18954ba9607SSascha Wildner 	"missing tbl option argument",
19054ba9607SSascha Wildner 	"wrong tbl option argument size",
19154ba9607SSascha Wildner 	"empty tbl layout",
19254ba9607SSascha Wildner 	"invalid character in tbl layout",
19354ba9607SSascha Wildner 	"unmatched parenthesis in tbl layout",
194*99db7d0eSSascha Wildner 	"ignoring excessive spacing in tbl layout",
19554ba9607SSascha Wildner 	"tbl without any data cells",
19654ba9607SSascha Wildner 	"ignoring data in spanned tbl cell",
19754ba9607SSascha Wildner 	"ignoring extra tbl data cells",
19854ba9607SSascha Wildner 	"data block open at end of tbl",
19954ba9607SSascha Wildner 
20054ba9607SSascha Wildner 	/* related to document structure and macros */
20154ba9607SSascha Wildner 	"duplicate prologue macro",
20254ba9607SSascha Wildner 	"skipping late title macro",
20354ba9607SSascha Wildner 	"input stack limit exceeded, infinite loop?",
20454ba9607SSascha Wildner 	"skipping bad character",
20554ba9607SSascha Wildner 	"skipping unknown macro",
20654ba9607SSascha Wildner 	"ignoring request outside macro",
20754ba9607SSascha Wildner 	"skipping insecure request",
20854ba9607SSascha Wildner 	"skipping item outside list",
20954ba9607SSascha Wildner 	"skipping column outside column list",
21054ba9607SSascha Wildner 	"skipping end of block that is not open",
21154ba9607SSascha Wildner 	"fewer RS blocks open, skipping",
21254ba9607SSascha Wildner 	"inserting missing end of block",
21354ba9607SSascha Wildner 	"appending missing end of block",
21454ba9607SSascha Wildner 
21554ba9607SSascha Wildner 	/* related to request and macro arguments */
21654ba9607SSascha Wildner 	"escaped character not allowed in a name",
21754ba9607SSascha Wildner 	"using macro argument outside macro",
21854ba9607SSascha Wildner 	"argument number is not numeric",
21954ba9607SSascha Wildner 	"NOT IMPLEMENTED: Bd -file",
22054ba9607SSascha Wildner 	"skipping display without arguments",
22154ba9607SSascha Wildner 	"missing list type, using -item",
22254ba9607SSascha Wildner 	"argument is not numeric, using 1",
22354ba9607SSascha Wildner 	"argument is not a character",
22454ba9607SSascha Wildner 	"missing manual name, using \"\"",
22554ba9607SSascha Wildner 	"uname(3) system call failed, using UNKNOWN",
22654ba9607SSascha Wildner 	"unknown standard specifier",
22754ba9607SSascha Wildner 	"skipping request without numeric argument",
22854ba9607SSascha Wildner 	"excessive shift",
22954ba9607SSascha Wildner 	"NOT IMPLEMENTED: .so with absolute path or \"..\"",
23054ba9607SSascha Wildner 	".so request failed",
231*99db7d0eSSascha Wildner 	"skipping tag containing whitespace",
23254ba9607SSascha Wildner 	"skipping all arguments",
23354ba9607SSascha Wildner 	"skipping excess arguments",
23454ba9607SSascha Wildner 	"divide by zero",
23554ba9607SSascha Wildner 
23654ba9607SSascha Wildner 	"unsupported feature",
23754ba9607SSascha Wildner 	"input too large",
23854ba9607SSascha Wildner 	"unsupported control character",
23954ba9607SSascha Wildner 	"unsupported escape sequence",
24054ba9607SSascha Wildner 	"unsupported roff request",
24154ba9607SSascha Wildner 	"nested .while loops",
24254ba9607SSascha Wildner 	"end of scope with open .while loop",
24354ba9607SSascha Wildner 	"end of .while loop in inner scope",
24454ba9607SSascha Wildner 	"cannot continue this .while loop",
24554ba9607SSascha Wildner 	"eqn delim option in tbl",
24654ba9607SSascha Wildner 	"unsupported tbl layout modifier",
24754ba9607SSascha Wildner 	"ignoring macro in table",
248*99db7d0eSSascha Wildner 	"skipping tbl in -Tman mode",
249*99db7d0eSSascha Wildner 	"skipping eqn in -Tman mode",
250*99db7d0eSSascha Wildner 
251*99db7d0eSSascha Wildner 	/* bad command line arguments */
252*99db7d0eSSascha Wildner 	NULL,
253*99db7d0eSSascha Wildner 	"bad command line argument",
254*99db7d0eSSascha Wildner 	"duplicate command line argument",
255*99db7d0eSSascha Wildner 	"option has a superfluous value",
256*99db7d0eSSascha Wildner 	"missing option value",
257*99db7d0eSSascha Wildner 	"bad option value",
258*99db7d0eSSascha Wildner 	"duplicate option value",
259*99db7d0eSSascha Wildner 	"no such tag",
260*99db7d0eSSascha Wildner 	"-Tmarkdown unsupported for man(7) input",
261*99db7d0eSSascha Wildner 
262*99db7d0eSSascha Wildner 	/* system errors */
263*99db7d0eSSascha Wildner 	NULL,
264*99db7d0eSSascha Wildner 	"dup",
265*99db7d0eSSascha Wildner 	"exec",
266*99db7d0eSSascha Wildner 	"fdopen",
267*99db7d0eSSascha Wildner 	"fflush",
268*99db7d0eSSascha Wildner 	"fork",
269*99db7d0eSSascha Wildner 	"fstat",
270*99db7d0eSSascha Wildner 	"getline",
271*99db7d0eSSascha Wildner 	"glob",
272*99db7d0eSSascha Wildner 	"gzclose",
273*99db7d0eSSascha Wildner 	"gzdopen",
274*99db7d0eSSascha Wildner 	"mkstemp",
275*99db7d0eSSascha Wildner 	"open",
276*99db7d0eSSascha Wildner 	"pledge",
277*99db7d0eSSascha Wildner 	"read",
278*99db7d0eSSascha Wildner 	"wait",
279*99db7d0eSSascha Wildner 	"write",
28054ba9607SSascha Wildner };
28154ba9607SSascha Wildner 
28254ba9607SSascha Wildner static	FILE		*fileptr = NULL;
28354ba9607SSascha Wildner static	const char	*filename = NULL;
284*99db7d0eSSascha Wildner static	enum mandocerr	 min_type = MANDOCERR_BADARG;
28554ba9607SSascha Wildner static	enum mandoclevel rc = MANDOCLEVEL_OK;
28654ba9607SSascha Wildner 
28754ba9607SSascha Wildner 
28854ba9607SSascha Wildner void
mandoc_msg_setoutfile(FILE * fp)28954ba9607SSascha Wildner mandoc_msg_setoutfile(FILE *fp)
29054ba9607SSascha Wildner {
29154ba9607SSascha Wildner 	fileptr = fp;
29254ba9607SSascha Wildner }
29354ba9607SSascha Wildner 
29454ba9607SSascha Wildner const char *
mandoc_msg_getinfilename(void)29554ba9607SSascha Wildner mandoc_msg_getinfilename(void)
29654ba9607SSascha Wildner {
29754ba9607SSascha Wildner 	return filename;
29854ba9607SSascha Wildner }
29954ba9607SSascha Wildner 
30054ba9607SSascha Wildner void
mandoc_msg_setinfilename(const char * fn)30154ba9607SSascha Wildner mandoc_msg_setinfilename(const char *fn)
30254ba9607SSascha Wildner {
30354ba9607SSascha Wildner 	filename = fn;
30454ba9607SSascha Wildner }
30554ba9607SSascha Wildner 
30654ba9607SSascha Wildner enum mandocerr
mandoc_msg_getmin(void)30754ba9607SSascha Wildner mandoc_msg_getmin(void)
30854ba9607SSascha Wildner {
30954ba9607SSascha Wildner 	return min_type;
31054ba9607SSascha Wildner }
31154ba9607SSascha Wildner 
31254ba9607SSascha Wildner void
mandoc_msg_setmin(enum mandocerr t)31354ba9607SSascha Wildner mandoc_msg_setmin(enum mandocerr t)
31454ba9607SSascha Wildner {
31554ba9607SSascha Wildner 	min_type = t;
31654ba9607SSascha Wildner }
31754ba9607SSascha Wildner 
31854ba9607SSascha Wildner enum mandoclevel
mandoc_msg_getrc(void)31954ba9607SSascha Wildner mandoc_msg_getrc(void)
32054ba9607SSascha Wildner {
32154ba9607SSascha Wildner 	return rc;
32254ba9607SSascha Wildner }
32354ba9607SSascha Wildner 
32454ba9607SSascha Wildner void
mandoc_msg_setrc(enum mandoclevel level)32554ba9607SSascha Wildner mandoc_msg_setrc(enum mandoclevel level)
32654ba9607SSascha Wildner {
32754ba9607SSascha Wildner 	if (rc < level)
32854ba9607SSascha Wildner 		rc = level;
32954ba9607SSascha Wildner }
33054ba9607SSascha Wildner 
33154ba9607SSascha Wildner void
mandoc_msg(enum mandocerr t,int line,int col,const char * fmt,...)33254ba9607SSascha Wildner mandoc_msg(enum mandocerr t, int line, int col, const char *fmt, ...)
33354ba9607SSascha Wildner {
33454ba9607SSascha Wildner 	va_list			 ap;
33554ba9607SSascha Wildner 	enum mandoclevel	 level;
33654ba9607SSascha Wildner 
337*99db7d0eSSascha Wildner 	if (t < min_type)
33854ba9607SSascha Wildner 		return;
33954ba9607SSascha Wildner 
340*99db7d0eSSascha Wildner 	level = MANDOCLEVEL_SYSERR;
34154ba9607SSascha Wildner 	while (t < lowest_type[level])
34254ba9607SSascha Wildner 		level--;
34354ba9607SSascha Wildner 	mandoc_msg_setrc(level);
34454ba9607SSascha Wildner 
34554ba9607SSascha Wildner 	if (fileptr == NULL)
34654ba9607SSascha Wildner 		return;
34754ba9607SSascha Wildner 
34854ba9607SSascha Wildner 	fprintf(fileptr, "%s:", getprogname());
34954ba9607SSascha Wildner 	if (filename != NULL)
35054ba9607SSascha Wildner 		fprintf(fileptr, " %s:", filename);
35154ba9607SSascha Wildner 
35254ba9607SSascha Wildner 	if (line > 0)
35354ba9607SSascha Wildner 		fprintf(fileptr, "%d:%d:", line, col + 1);
35454ba9607SSascha Wildner 
35554ba9607SSascha Wildner 	fprintf(fileptr, " %s", level_name[level]);
35654ba9607SSascha Wildner 	if (type_message[t] != NULL)
35754ba9607SSascha Wildner 		fprintf(fileptr, ": %s", type_message[t]);
35854ba9607SSascha Wildner 
35954ba9607SSascha Wildner 	if (fmt != NULL) {
36054ba9607SSascha Wildner 		fprintf(fileptr, ": ");
36154ba9607SSascha Wildner 		va_start(ap, fmt);
36254ba9607SSascha Wildner 		vfprintf(fileptr, fmt, ap);
36354ba9607SSascha Wildner 		va_end(ap);
36454ba9607SSascha Wildner 	}
36554ba9607SSascha Wildner 	fputc('\n', fileptr);
36654ba9607SSascha Wildner }
367*99db7d0eSSascha Wildner 
368*99db7d0eSSascha Wildner void
mandoc_msg_summary(void)369*99db7d0eSSascha Wildner mandoc_msg_summary(void)
370*99db7d0eSSascha Wildner {
371*99db7d0eSSascha Wildner 	if (fileptr != NULL && rc != MANDOCLEVEL_OK)
372*99db7d0eSSascha Wildner 		fprintf(fileptr,
373*99db7d0eSSascha Wildner 		    "%s: see above the output for %s messages\n",
374*99db7d0eSSascha Wildner 		    getprogname(), level_name[rc]);
375*99db7d0eSSascha Wildner }
376