xref: /dragonfly/contrib/mdocml/main.c (revision fb151170)
1 /*	$Id: main.c,v 1.163 2011/05/20 15:51:18 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_LOCALE,	/* -Tlocale */
47 	OUTT_UTF8,	/* -Tutf8 */
48 	OUTT_TREE,	/* -Ttree */
49 	OUTT_HTML,	/* -Thtml */
50 	OUTT_XHTML,	/* -Txhtml */
51 	OUTT_LINT,	/* -Tlint */
52 	OUTT_PS,	/* -Tps */
53 	OUTT_PDF	/* -Tpdf */
54 };
55 
56 struct	curparse {
57 	struct mparse	 *mp;
58 	enum mandoclevel  wlevel;	/* ignore messages below this */
59 	int		  wstop;	/* stop after a file with a warning */
60 	enum outt	  outtype; 	/* which output to use */
61 	out_mdoc	  outmdoc;	/* mdoc output ptr */
62 	out_man	  	  outman;	/* man output ptr */
63 	out_free	  outfree;	/* free output ptr */
64 	void		 *outdata;	/* data for output */
65 	char		  outopts[BUFSIZ]; /* buf of output opts */
66 };
67 
68 static	int		  moptions(enum mparset *, char *);
69 static	void		  mmsg(enum mandocerr, enum mandoclevel,
70 				const char *, int, int, const char *);
71 static	void		  parse(struct curparse *, int,
72 				const char *, enum mandoclevel *);
73 static	int		  toptions(struct curparse *, char *);
74 static	void		  usage(void) __attribute__((noreturn));
75 static	void		  version(void) __attribute__((noreturn));
76 static	int		  woptions(struct curparse *, char *);
77 
78 static	const char	 *progname;
79 
80 int
81 main(int argc, char *argv[])
82 {
83 	int		 c;
84 	struct curparse	 curp;
85 	enum mparset	 type;
86 	enum mandoclevel rc;
87 
88 	progname = strrchr(argv[0], '/');
89 	if (progname == NULL)
90 		progname = argv[0];
91 	else
92 		++progname;
93 
94 	memset(&curp, 0, sizeof(struct curparse));
95 
96 	type = MPARSE_AUTO;
97 	curp.outtype = OUTT_ASCII;
98 	curp.wlevel  = MANDOCLEVEL_FATAL;
99 
100 	/* LINTED */
101 	while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
102 		switch (c) {
103 		case ('m'):
104 			if ( ! moptions(&type, optarg))
105 				return((int)MANDOCLEVEL_BADARG);
106 			break;
107 		case ('O'):
108 			(void)strlcat(curp.outopts, optarg, BUFSIZ);
109 			(void)strlcat(curp.outopts, ",", BUFSIZ);
110 			break;
111 		case ('T'):
112 			if ( ! toptions(&curp, optarg))
113 				return((int)MANDOCLEVEL_BADARG);
114 			break;
115 		case ('W'):
116 			if ( ! woptions(&curp, optarg))
117 				return((int)MANDOCLEVEL_BADARG);
118 			break;
119 		case ('V'):
120 			version();
121 			/* NOTREACHED */
122 		default:
123 			usage();
124 			/* NOTREACHED */
125 		}
126 
127 	curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp);
128 
129 	argc -= optind;
130 	argv += optind;
131 
132 	rc = MANDOCLEVEL_OK;
133 
134 	if (NULL == *argv)
135 		parse(&curp, STDIN_FILENO, "<stdin>", &rc);
136 
137 	while (*argv) {
138 		parse(&curp, -1, *argv, &rc);
139 		if (MANDOCLEVEL_OK != rc && curp.wstop)
140 			break;
141 		++argv;
142 	}
143 
144 	if (curp.outfree)
145 		(*curp.outfree)(curp.outdata);
146 	if (curp.mp)
147 		mparse_free(curp.mp);
148 
149 	return((int)rc);
150 }
151 
152 static void
153 version(void)
154 {
155 
156 	printf("%s %s\n", progname, VERSION);
157 	exit((int)MANDOCLEVEL_OK);
158 }
159 
160 static void
161 usage(void)
162 {
163 
164 	fprintf(stderr, "usage: %s "
165 			"[-V] "
166 			"[-foption] "
167 			"[-mformat] "
168 			"[-Ooption] "
169 			"[-Toutput] "
170 			"[-Wlevel] "
171 			"[file...]\n",
172 			progname);
173 
174 	exit((int)MANDOCLEVEL_BADARG);
175 }
176 
177 static void
178 parse(struct curparse *curp, int fd,
179 		const char *file, enum mandoclevel *level)
180 {
181 	enum mandoclevel  rc;
182 	struct mdoc	 *mdoc;
183 	struct man	 *man;
184 
185 	/* Begin by parsing the file itself. */
186 
187 	assert(file);
188 	assert(fd >= -1);
189 
190 	rc = mparse_readfd(curp->mp, fd, file);
191 
192 	/* Stop immediately if the parse has failed. */
193 
194 	if (MANDOCLEVEL_FATAL <= rc)
195 		goto cleanup;
196 
197 	/*
198 	 * With -Wstop and warnings or errors of at least the requested
199 	 * level, do not produce output.
200 	 */
201 
202 	if (MANDOCLEVEL_OK != rc && curp->wstop)
203 		goto cleanup;
204 
205 	/* If unset, allocate output dev now (if applicable). */
206 
207 	if ( ! (curp->outman && curp->outmdoc)) {
208 		switch (curp->outtype) {
209 		case (OUTT_XHTML):
210 			curp->outdata = xhtml_alloc(curp->outopts);
211 			curp->outfree = html_free;
212 			break;
213 		case (OUTT_HTML):
214 			curp->outdata = html_alloc(curp->outopts);
215 			curp->outfree = html_free;
216 			break;
217 		case (OUTT_UTF8):
218 			curp->outdata = utf8_alloc(curp->outopts);
219 			curp->outfree = ascii_free;
220 			break;
221 		case (OUTT_LOCALE):
222 			curp->outdata = locale_alloc(curp->outopts);
223 			curp->outfree = ascii_free;
224 			break;
225 		case (OUTT_ASCII):
226 			curp->outdata = ascii_alloc(curp->outopts);
227 			curp->outfree = ascii_free;
228 			break;
229 		case (OUTT_PDF):
230 			curp->outdata = pdf_alloc(curp->outopts);
231 			curp->outfree = pspdf_free;
232 			break;
233 		case (OUTT_PS):
234 			curp->outdata = ps_alloc(curp->outopts);
235 			curp->outfree = pspdf_free;
236 			break;
237 		default:
238 			break;
239 		}
240 
241 		switch (curp->outtype) {
242 		case (OUTT_HTML):
243 			/* FALLTHROUGH */
244 		case (OUTT_XHTML):
245 			curp->outman = html_man;
246 			curp->outmdoc = html_mdoc;
247 			break;
248 		case (OUTT_TREE):
249 			curp->outman = tree_man;
250 			curp->outmdoc = tree_mdoc;
251 			break;
252 		case (OUTT_PDF):
253 			/* FALLTHROUGH */
254 		case (OUTT_ASCII):
255 			/* FALLTHROUGH */
256 		case (OUTT_UTF8):
257 			/* FALLTHROUGH */
258 		case (OUTT_LOCALE):
259 			/* FALLTHROUGH */
260 		case (OUTT_PS):
261 			curp->outman = terminal_man;
262 			curp->outmdoc = terminal_mdoc;
263 			break;
264 		default:
265 			break;
266 		}
267 	}
268 
269 	mparse_result(curp->mp, &mdoc, &man);
270 
271 	/* Execute the out device, if it exists. */
272 
273 	if (man && curp->outman)
274 		(*curp->outman)(curp->outdata, man);
275 	if (mdoc && curp->outmdoc)
276 		(*curp->outmdoc)(curp->outdata, mdoc);
277 
278  cleanup:
279 
280 	mparse_reset(curp->mp);
281 
282 	if (*level < rc)
283 		*level = rc;
284 }
285 
286 static int
287 moptions(enum mparset *tflags, char *arg)
288 {
289 
290 	if (0 == strcmp(arg, "doc"))
291 		*tflags = MPARSE_MDOC;
292 	else if (0 == strcmp(arg, "andoc"))
293 		*tflags = MPARSE_AUTO;
294 	else if (0 == strcmp(arg, "an"))
295 		*tflags = MPARSE_MAN;
296 	else {
297 		fprintf(stderr, "%s: Bad argument\n", arg);
298 		return(0);
299 	}
300 
301 	return(1);
302 }
303 
304 static int
305 toptions(struct curparse *curp, char *arg)
306 {
307 
308 	if (0 == strcmp(arg, "ascii"))
309 		curp->outtype = OUTT_ASCII;
310 	else if (0 == strcmp(arg, "lint")) {
311 		curp->outtype = OUTT_LINT;
312 		curp->wlevel  = MANDOCLEVEL_WARNING;
313 	} else if (0 == strcmp(arg, "tree"))
314 		curp->outtype = OUTT_TREE;
315 	else if (0 == strcmp(arg, "html"))
316 		curp->outtype = OUTT_HTML;
317 	else if (0 == strcmp(arg, "utf8"))
318 		curp->outtype = OUTT_UTF8;
319 	else if (0 == strcmp(arg, "locale"))
320 		curp->outtype = OUTT_LOCALE;
321 	else if (0 == strcmp(arg, "xhtml"))
322 		curp->outtype = OUTT_XHTML;
323 	else if (0 == strcmp(arg, "ps"))
324 		curp->outtype = OUTT_PS;
325 	else if (0 == strcmp(arg, "pdf"))
326 		curp->outtype = OUTT_PDF;
327 	else {
328 		fprintf(stderr, "%s: Bad argument\n", arg);
329 		return(0);
330 	}
331 
332 	return(1);
333 }
334 
335 static int
336 woptions(struct curparse *curp, char *arg)
337 {
338 	char		*v, *o;
339 	const char	*toks[6];
340 
341 	toks[0] = "stop";
342 	toks[1] = "all";
343 	toks[2] = "warning";
344 	toks[3] = "error";
345 	toks[4] = "fatal";
346 	toks[5] = NULL;
347 
348 	while (*arg) {
349 		o = arg;
350 		switch (getsubopt(&arg, UNCONST(toks), &v)) {
351 		case (0):
352 			curp->wstop = 1;
353 			break;
354 		case (1):
355 			/* FALLTHROUGH */
356 		case (2):
357 			curp->wlevel = MANDOCLEVEL_WARNING;
358 			break;
359 		case (3):
360 			curp->wlevel = MANDOCLEVEL_ERROR;
361 			break;
362 		case (4):
363 			curp->wlevel = MANDOCLEVEL_FATAL;
364 			break;
365 		default:
366 			fprintf(stderr, "-W%s: Bad argument\n", o);
367 			return(0);
368 		}
369 	}
370 
371 	return(1);
372 }
373 
374 static void
375 mmsg(enum mandocerr t, enum mandoclevel lvl,
376 		const char *file, int line, int col, const char *msg)
377 {
378 
379 	fprintf(stderr, "%s:%d:%d: %s: %s",
380 			file, line, col + 1,
381 			mparse_strlevel(lvl),
382 			mparse_strerror(t));
383 
384 	if (msg)
385 		fprintf(stderr, ": %s", msg);
386 
387 	fputc('\n', stderr);
388 }
389