xref: /dflybsd-src/contrib/mdocml/main.c (revision 0eb2eccd5a86ef7dd7492d2651de55c3589f23d7)
1 /*	$Id: main.c,v 1.161 2011/03/31 10:53:43 kristaps Exp $ */
2 /*
3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
21 
22 #include <assert.h>
23 #include <stdio.h>
24 #include <stdint.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 
29 #include "mandoc.h"
30 #include "main.h"
31 #include "mdoc.h"
32 #include "man.h"
33 
34 #if !defined(__GNUC__) || (__GNUC__ < 2)
35 # if !defined(lint)
36 #  define __attribute__(x)
37 # endif
38 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
39 
40 typedef	void		(*out_mdoc)(void *, const struct mdoc *);
41 typedef	void		(*out_man)(void *, const struct man *);
42 typedef	void		(*out_free)(void *);
43 
44 enum	outt {
45 	OUTT_ASCII = 0,	/* -Tascii */
46 	OUTT_TREE,	/* -Ttree */
47 	OUTT_HTML,	/* -Thtml */
48 	OUTT_XHTML,	/* -Txhtml */
49 	OUTT_LINT,	/* -Tlint */
50 	OUTT_PS,	/* -Tps */
51 	OUTT_PDF	/* -Tpdf */
52 };
53 
54 struct	curparse {
55 	struct mparse	 *mp;
56 	enum mandoclevel  wlevel;	/* ignore messages below this */
57 	int		  wstop;	/* stop after a file with a warning */
58 	enum outt	  outtype; 	/* which output to use */
59 	out_mdoc	  outmdoc;	/* mdoc output ptr */
60 	out_man	  	  outman;	/* man output ptr */
61 	out_free	  outfree;	/* free output ptr */
62 	void		 *outdata;	/* data for output */
63 	char		  outopts[BUFSIZ]; /* buf of output opts */
64 };
65 
66 static	int		  moptions(enum mparset *, char *);
67 static	void		  mmsg(enum mandocerr, enum mandoclevel,
68 				const char *, int, int, const char *);
69 static	void		  parse(struct curparse *, int,
70 				const char *, enum mandoclevel *);
71 static	int		  toptions(struct curparse *, char *);
72 static	void		  usage(void) __attribute__((noreturn));
73 static	void		  version(void) __attribute__((noreturn));
74 static	int		  woptions(struct curparse *, char *);
75 
76 static	const char	 *progname;
77 
78 int
79 main(int argc, char *argv[])
80 {
81 	int		 c;
82 	struct curparse	 curp;
83 	enum mparset	 type;
84 	enum mandoclevel rc;
85 
86 	progname = strrchr(argv[0], '/');
87 	if (progname == NULL)
88 		progname = argv[0];
89 	else
90 		++progname;
91 
92 	memset(&curp, 0, sizeof(struct curparse));
93 
94 	type = MPARSE_AUTO;
95 	curp.outtype = OUTT_ASCII;
96 	curp.wlevel  = MANDOCLEVEL_FATAL;
97 
98 	/* LINTED */
99 	while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
100 		switch (c) {
101 		case ('m'):
102 			if ( ! moptions(&type, optarg))
103 				return((int)MANDOCLEVEL_BADARG);
104 			break;
105 		case ('O'):
106 			(void)strlcat(curp.outopts, optarg, BUFSIZ);
107 			(void)strlcat(curp.outopts, ",", BUFSIZ);
108 			break;
109 		case ('T'):
110 			if ( ! toptions(&curp, optarg))
111 				return((int)MANDOCLEVEL_BADARG);
112 			break;
113 		case ('W'):
114 			if ( ! woptions(&curp, optarg))
115 				return((int)MANDOCLEVEL_BADARG);
116 			break;
117 		case ('V'):
118 			version();
119 			/* NOTREACHED */
120 		default:
121 			usage();
122 			/* NOTREACHED */
123 		}
124 
125 	curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp);
126 
127 	argc -= optind;
128 	argv += optind;
129 
130 	rc = MANDOCLEVEL_OK;
131 
132 	if (NULL == *argv)
133 		parse(&curp, STDIN_FILENO, "<stdin>", &rc);
134 
135 	while (*argv) {
136 		parse(&curp, -1, *argv, &rc);
137 		if (MANDOCLEVEL_OK != rc && curp.wstop)
138 			break;
139 		++argv;
140 	}
141 
142 	if (curp.outfree)
143 		(*curp.outfree)(curp.outdata);
144 	if (curp.mp)
145 		mparse_free(curp.mp);
146 
147 	return((int)rc);
148 }
149 
150 static void
151 version(void)
152 {
153 
154 	printf("%s %s\n", progname, VERSION);
155 	exit((int)MANDOCLEVEL_OK);
156 }
157 
158 static void
159 usage(void)
160 {
161 
162 	fprintf(stderr, "usage: %s "
163 			"[-V] "
164 			"[-foption] "
165 			"[-mformat] "
166 			"[-Ooption] "
167 			"[-Toutput] "
168 			"[-Wlevel] "
169 			"[file...]\n",
170 			progname);
171 
172 	exit((int)MANDOCLEVEL_BADARG);
173 }
174 
175 static void
176 parse(struct curparse *curp, int fd,
177 		const char *file, enum mandoclevel *level)
178 {
179 	enum mandoclevel  rc;
180 	struct mdoc	 *mdoc;
181 	struct man	 *man;
182 
183 	/* Begin by parsing the file itself. */
184 
185 	assert(file);
186 	assert(fd >= -1);
187 
188 	rc = mparse_readfd(curp->mp, fd, file);
189 
190 	/* Stop immediately if the parse has failed. */
191 
192 	if (MANDOCLEVEL_FATAL <= rc)
193 		goto cleanup;
194 
195 	/*
196 	 * With -Wstop and warnings or errors of at least the requested
197 	 * level, do not produce output.
198 	 */
199 
200 	if (MANDOCLEVEL_OK != rc && curp->wstop)
201 		goto cleanup;
202 
203 	/* If unset, allocate output dev now (if applicable). */
204 
205 	if ( ! (curp->outman && curp->outmdoc)) {
206 		switch (curp->outtype) {
207 		case (OUTT_XHTML):
208 			curp->outdata = xhtml_alloc(curp->outopts);
209 			break;
210 		case (OUTT_HTML):
211 			curp->outdata = html_alloc(curp->outopts);
212 			break;
213 		case (OUTT_ASCII):
214 			curp->outdata = ascii_alloc(curp->outopts);
215 			curp->outfree = ascii_free;
216 			break;
217 		case (OUTT_PDF):
218 			curp->outdata = pdf_alloc(curp->outopts);
219 			curp->outfree = pspdf_free;
220 			break;
221 		case (OUTT_PS):
222 			curp->outdata = ps_alloc(curp->outopts);
223 			curp->outfree = pspdf_free;
224 			break;
225 		default:
226 			break;
227 		}
228 
229 		switch (curp->outtype) {
230 		case (OUTT_HTML):
231 			/* FALLTHROUGH */
232 		case (OUTT_XHTML):
233 			curp->outman = html_man;
234 			curp->outmdoc = html_mdoc;
235 			curp->outfree = html_free;
236 			break;
237 		case (OUTT_TREE):
238 			curp->outman = tree_man;
239 			curp->outmdoc = tree_mdoc;
240 			break;
241 		case (OUTT_PDF):
242 			/* FALLTHROUGH */
243 		case (OUTT_ASCII):
244 			/* FALLTHROUGH */
245 		case (OUTT_PS):
246 			curp->outman = terminal_man;
247 			curp->outmdoc = terminal_mdoc;
248 			break;
249 		default:
250 			break;
251 		}
252 	}
253 
254 	mparse_result(curp->mp, &mdoc, &man);
255 
256 	/* Execute the out device, if it exists. */
257 
258 	if (man && curp->outman)
259 		(*curp->outman)(curp->outdata, man);
260 	if (mdoc && curp->outmdoc)
261 		(*curp->outmdoc)(curp->outdata, mdoc);
262 
263  cleanup:
264 
265 	mparse_reset(curp->mp);
266 
267 	if (*level < rc)
268 		*level = rc;
269 }
270 
271 static int
272 moptions(enum mparset *tflags, char *arg)
273 {
274 
275 	if (0 == strcmp(arg, "doc"))
276 		*tflags = MPARSE_MDOC;
277 	else if (0 == strcmp(arg, "andoc"))
278 		*tflags = MPARSE_AUTO;
279 	else if (0 == strcmp(arg, "an"))
280 		*tflags = MPARSE_MAN;
281 	else {
282 		fprintf(stderr, "%s: Bad argument\n", arg);
283 		return(0);
284 	}
285 
286 	return(1);
287 }
288 
289 static int
290 toptions(struct curparse *curp, char *arg)
291 {
292 
293 	if (0 == strcmp(arg, "ascii"))
294 		curp->outtype = OUTT_ASCII;
295 	else if (0 == strcmp(arg, "lint")) {
296 		curp->outtype = OUTT_LINT;
297 		curp->wlevel  = MANDOCLEVEL_WARNING;
298 	} else if (0 == strcmp(arg, "tree"))
299 		curp->outtype = OUTT_TREE;
300 	else if (0 == strcmp(arg, "html"))
301 		curp->outtype = OUTT_HTML;
302 	else if (0 == strcmp(arg, "xhtml"))
303 		curp->outtype = OUTT_XHTML;
304 	else if (0 == strcmp(arg, "ps"))
305 		curp->outtype = OUTT_PS;
306 	else if (0 == strcmp(arg, "pdf"))
307 		curp->outtype = OUTT_PDF;
308 	else {
309 		fprintf(stderr, "%s: Bad argument\n", arg);
310 		return(0);
311 	}
312 
313 	return(1);
314 }
315 
316 static int
317 woptions(struct curparse *curp, char *arg)
318 {
319 	char		*v, *o;
320 	const char	*toks[6];
321 
322 	toks[0] = "stop";
323 	toks[1] = "all";
324 	toks[2] = "warning";
325 	toks[3] = "error";
326 	toks[4] = "fatal";
327 	toks[5] = NULL;
328 
329 	while (*arg) {
330 		o = arg;
331 		switch (getsubopt(&arg, UNCONST(toks), &v)) {
332 		case (0):
333 			curp->wstop = 1;
334 			break;
335 		case (1):
336 			/* FALLTHROUGH */
337 		case (2):
338 			curp->wlevel = MANDOCLEVEL_WARNING;
339 			break;
340 		case (3):
341 			curp->wlevel = MANDOCLEVEL_ERROR;
342 			break;
343 		case (4):
344 			curp->wlevel = MANDOCLEVEL_FATAL;
345 			break;
346 		default:
347 			fprintf(stderr, "-W%s: Bad argument\n", o);
348 			return(0);
349 		}
350 	}
351 
352 	return(1);
353 }
354 
355 static void
356 mmsg(enum mandocerr t, enum mandoclevel lvl,
357 		const char *file, int line, int col, const char *msg)
358 {
359 
360 	fprintf(stderr, "%s:%d:%d: %s: %s",
361 			file, line, col + 1,
362 			mparse_strlevel(lvl),
363 			mparse_strerror(t));
364 
365 	if (msg)
366 		fprintf(stderr, ": %s", msg);
367 
368 	fputc('\n', stderr);
369 }
370