1241675Suqs/*	$Id: main.c,v 1.165 2011/10/06 22:29:12 kristaps Exp $ */
2241675Suqs/*
3241675Suqs * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4241675Suqs * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5241675Suqs *
6241675Suqs * Permission to use, copy, modify, and distribute this software for any
7241675Suqs * purpose with or without fee is hereby granted, provided that the above
8241675Suqs * copyright notice and this permission notice appear in all copies.
9241675Suqs *
10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17241675Suqs */
18241675Suqs#ifdef HAVE_CONFIG_H
19241675Suqs#include "config.h"
20241675Suqs#endif
21241675Suqs
22241675Suqs#include <assert.h>
23241675Suqs#include <stdio.h>
24241675Suqs#include <stdint.h>
25241675Suqs#include <stdlib.h>
26241675Suqs#include <string.h>
27241675Suqs#include <unistd.h>
28241675Suqs
29241675Suqs#include "mandoc.h"
30241675Suqs#include "main.h"
31241675Suqs#include "mdoc.h"
32241675Suqs#include "man.h"
33241675Suqs
34241675Suqs#if !defined(__GNUC__) || (__GNUC__ < 2)
35241675Suqs# if !defined(lint)
36241675Suqs#  define __attribute__(x)
37241675Suqs# endif
38241675Suqs#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
39241675Suqs
40241675Suqstypedef	void		(*out_mdoc)(void *, const struct mdoc *);
41241675Suqstypedef	void		(*out_man)(void *, const struct man *);
42241675Suqstypedef	void		(*out_free)(void *);
43241675Suqs
44241675Suqsenum	outt {
45241675Suqs	OUTT_ASCII = 0,	/* -Tascii */
46241675Suqs	OUTT_LOCALE,	/* -Tlocale */
47241675Suqs	OUTT_UTF8,	/* -Tutf8 */
48241675Suqs	OUTT_TREE,	/* -Ttree */
49241675Suqs	OUTT_MAN,	/* -Tman */
50241675Suqs	OUTT_HTML,	/* -Thtml */
51241675Suqs	OUTT_XHTML,	/* -Txhtml */
52241675Suqs	OUTT_LINT,	/* -Tlint */
53241675Suqs	OUTT_PS,	/* -Tps */
54241675Suqs	OUTT_PDF	/* -Tpdf */
55241675Suqs};
56241675Suqs
57241675Suqsstruct	curparse {
58241675Suqs	struct mparse	 *mp;
59241675Suqs	enum mandoclevel  wlevel;	/* ignore messages below this */
60241675Suqs	int		  wstop;	/* stop after a file with a warning */
61241675Suqs	enum outt	  outtype; 	/* which output to use */
62241675Suqs	out_mdoc	  outmdoc;	/* mdoc output ptr */
63241675Suqs	out_man	  	  outman;	/* man output ptr */
64241675Suqs	out_free	  outfree;	/* free output ptr */
65241675Suqs	void		 *outdata;	/* data for output */
66241675Suqs	char		  outopts[BUFSIZ]; /* buf of output opts */
67241675Suqs};
68241675Suqs
69241675Suqsstatic	int		  moptions(enum mparset *, char *);
70241675Suqsstatic	void		  mmsg(enum mandocerr, enum mandoclevel,
71241675Suqs				const char *, int, int, const char *);
72241675Suqsstatic	void		  parse(struct curparse *, int,
73241675Suqs				const char *, enum mandoclevel *);
74241675Suqsstatic	int		  toptions(struct curparse *, char *);
75241675Suqsstatic	void		  usage(void) __attribute__((noreturn));
76241675Suqsstatic	void		  version(void) __attribute__((noreturn));
77241675Suqsstatic	int		  woptions(struct curparse *, char *);
78241675Suqs
79241675Suqsstatic	const char	 *progname;
80241675Suqs
81241675Suqsint
82241675Suqsmain(int argc, char *argv[])
83241675Suqs{
84241675Suqs	int		 c;
85241675Suqs	struct curparse	 curp;
86241675Suqs	enum mparset	 type;
87241675Suqs	enum mandoclevel rc;
88241675Suqs
89241675Suqs	progname = strrchr(argv[0], '/');
90241675Suqs	if (progname == NULL)
91241675Suqs		progname = argv[0];
92241675Suqs	else
93241675Suqs		++progname;
94241675Suqs
95241675Suqs	memset(&curp, 0, sizeof(struct curparse));
96241675Suqs
97241675Suqs	type = MPARSE_AUTO;
98241675Suqs	curp.outtype = OUTT_ASCII;
99241675Suqs	curp.wlevel  = MANDOCLEVEL_FATAL;
100241675Suqs
101241675Suqs	/* LINTED */
102241675Suqs	while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
103241675Suqs		switch (c) {
104241675Suqs		case ('m'):
105241675Suqs			if ( ! moptions(&type, optarg))
106241675Suqs				return((int)MANDOCLEVEL_BADARG);
107241675Suqs			break;
108241675Suqs		case ('O'):
109241675Suqs			(void)strlcat(curp.outopts, optarg, BUFSIZ);
110241675Suqs			(void)strlcat(curp.outopts, ",", BUFSIZ);
111241675Suqs			break;
112241675Suqs		case ('T'):
113241675Suqs			if ( ! toptions(&curp, optarg))
114241675Suqs				return((int)MANDOCLEVEL_BADARG);
115241675Suqs			break;
116241675Suqs		case ('W'):
117241675Suqs			if ( ! woptions(&curp, optarg))
118241675Suqs				return((int)MANDOCLEVEL_BADARG);
119241675Suqs			break;
120241675Suqs		case ('V'):
121241675Suqs			version();
122241675Suqs			/* NOTREACHED */
123241675Suqs		default:
124241675Suqs			usage();
125241675Suqs			/* NOTREACHED */
126241675Suqs		}
127241675Suqs
128241675Suqs	curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp);
129241675Suqs
130241675Suqs	/*
131241675Suqs	 * Conditionally start up the lookaside buffer before parsing.
132241675Suqs	 */
133241675Suqs	if (OUTT_MAN == curp.outtype)
134241675Suqs		mparse_keep(curp.mp);
135241675Suqs
136241675Suqs	argc -= optind;
137241675Suqs	argv += optind;
138241675Suqs
139241675Suqs	rc = MANDOCLEVEL_OK;
140241675Suqs
141241675Suqs	if (NULL == *argv)
142241675Suqs		parse(&curp, STDIN_FILENO, "<stdin>", &rc);
143241675Suqs
144241675Suqs	while (*argv) {
145241675Suqs		parse(&curp, -1, *argv, &rc);
146241675Suqs		if (MANDOCLEVEL_OK != rc && curp.wstop)
147241675Suqs			break;
148241675Suqs		++argv;
149241675Suqs	}
150241675Suqs
151241675Suqs	if (curp.outfree)
152241675Suqs		(*curp.outfree)(curp.outdata);
153241675Suqs	if (curp.mp)
154241675Suqs		mparse_free(curp.mp);
155241675Suqs
156241675Suqs	return((int)rc);
157241675Suqs}
158241675Suqs
159241675Suqsstatic void
160241675Suqsversion(void)
161241675Suqs{
162241675Suqs
163241675Suqs	printf("%s %s\n", progname, VERSION);
164241675Suqs	exit((int)MANDOCLEVEL_OK);
165241675Suqs}
166241675Suqs
167241675Suqsstatic void
168241675Suqsusage(void)
169241675Suqs{
170241675Suqs
171241675Suqs	fprintf(stderr, "usage: %s "
172241675Suqs			"[-V] "
173241675Suqs			"[-foption] "
174241675Suqs			"[-mformat] "
175241675Suqs			"[-Ooption] "
176241675Suqs			"[-Toutput] "
177241675Suqs			"[-Wlevel] "
178241675Suqs			"[file...]\n",
179241675Suqs			progname);
180241675Suqs
181241675Suqs	exit((int)MANDOCLEVEL_BADARG);
182241675Suqs}
183241675Suqs
184241675Suqsstatic void
185241675Suqsparse(struct curparse *curp, int fd,
186241675Suqs		const char *file, enum mandoclevel *level)
187241675Suqs{
188241675Suqs	enum mandoclevel  rc;
189241675Suqs	struct mdoc	 *mdoc;
190241675Suqs	struct man	 *man;
191241675Suqs
192241675Suqs	/* Begin by parsing the file itself. */
193241675Suqs
194241675Suqs	assert(file);
195241675Suqs	assert(fd >= -1);
196241675Suqs
197241675Suqs	rc = mparse_readfd(curp->mp, fd, file);
198241675Suqs
199241675Suqs	/* Stop immediately if the parse has failed. */
200241675Suqs
201241675Suqs	if (MANDOCLEVEL_FATAL <= rc)
202241675Suqs		goto cleanup;
203241675Suqs
204241675Suqs	/*
205241675Suqs	 * With -Wstop and warnings or errors of at least the requested
206241675Suqs	 * level, do not produce output.
207241675Suqs	 */
208241675Suqs
209241675Suqs	if (MANDOCLEVEL_OK != rc && curp->wstop)
210241675Suqs		goto cleanup;
211241675Suqs
212241675Suqs	/* If unset, allocate output dev now (if applicable). */
213241675Suqs
214241675Suqs	if ( ! (curp->outman && curp->outmdoc)) {
215241675Suqs		switch (curp->outtype) {
216241675Suqs		case (OUTT_XHTML):
217241675Suqs			curp->outdata = xhtml_alloc(curp->outopts);
218241675Suqs			curp->outfree = html_free;
219241675Suqs			break;
220241675Suqs		case (OUTT_HTML):
221241675Suqs			curp->outdata = html_alloc(curp->outopts);
222241675Suqs			curp->outfree = html_free;
223241675Suqs			break;
224241675Suqs		case (OUTT_UTF8):
225241675Suqs			curp->outdata = utf8_alloc(curp->outopts);
226241675Suqs			curp->outfree = ascii_free;
227241675Suqs			break;
228241675Suqs		case (OUTT_LOCALE):
229241675Suqs			curp->outdata = locale_alloc(curp->outopts);
230241675Suqs			curp->outfree = ascii_free;
231241675Suqs			break;
232241675Suqs		case (OUTT_ASCII):
233241675Suqs			curp->outdata = ascii_alloc(curp->outopts);
234241675Suqs			curp->outfree = ascii_free;
235241675Suqs			break;
236241675Suqs		case (OUTT_PDF):
237241675Suqs			curp->outdata = pdf_alloc(curp->outopts);
238241675Suqs			curp->outfree = pspdf_free;
239241675Suqs			break;
240241675Suqs		case (OUTT_PS):
241241675Suqs			curp->outdata = ps_alloc(curp->outopts);
242241675Suqs			curp->outfree = pspdf_free;
243241675Suqs			break;
244241675Suqs		default:
245241675Suqs			break;
246241675Suqs		}
247241675Suqs
248241675Suqs		switch (curp->outtype) {
249241675Suqs		case (OUTT_HTML):
250241675Suqs			/* FALLTHROUGH */
251241675Suqs		case (OUTT_XHTML):
252241675Suqs			curp->outman = html_man;
253241675Suqs			curp->outmdoc = html_mdoc;
254241675Suqs			break;
255241675Suqs		case (OUTT_TREE):
256241675Suqs			curp->outman = tree_man;
257241675Suqs			curp->outmdoc = tree_mdoc;
258241675Suqs			break;
259241675Suqs		case (OUTT_MAN):
260241675Suqs			curp->outmdoc = man_mdoc;
261241675Suqs			curp->outman = man_man;
262241675Suqs			break;
263241675Suqs		case (OUTT_PDF):
264241675Suqs			/* FALLTHROUGH */
265241675Suqs		case (OUTT_ASCII):
266241675Suqs			/* FALLTHROUGH */
267241675Suqs		case (OUTT_UTF8):
268241675Suqs			/* FALLTHROUGH */
269241675Suqs		case (OUTT_LOCALE):
270241675Suqs			/* FALLTHROUGH */
271241675Suqs		case (OUTT_PS):
272241675Suqs			curp->outman = terminal_man;
273241675Suqs			curp->outmdoc = terminal_mdoc;
274241675Suqs			break;
275241675Suqs		default:
276241675Suqs			break;
277241675Suqs		}
278241675Suqs	}
279241675Suqs
280241675Suqs	mparse_result(curp->mp, &mdoc, &man);
281241675Suqs
282241675Suqs	/* Execute the out device, if it exists. */
283241675Suqs
284241675Suqs	if (man && curp->outman)
285241675Suqs		(*curp->outman)(curp->outdata, man);
286241675Suqs	if (mdoc && curp->outmdoc)
287241675Suqs		(*curp->outmdoc)(curp->outdata, mdoc);
288241675Suqs
289241675Suqs cleanup:
290241675Suqs
291241675Suqs	mparse_reset(curp->mp);
292241675Suqs
293241675Suqs	if (*level < rc)
294241675Suqs		*level = rc;
295241675Suqs}
296241675Suqs
297241675Suqsstatic int
298241675Suqsmoptions(enum mparset *tflags, char *arg)
299241675Suqs{
300241675Suqs
301241675Suqs	if (0 == strcmp(arg, "doc"))
302241675Suqs		*tflags = MPARSE_MDOC;
303241675Suqs	else if (0 == strcmp(arg, "andoc"))
304241675Suqs		*tflags = MPARSE_AUTO;
305241675Suqs	else if (0 == strcmp(arg, "an"))
306241675Suqs		*tflags = MPARSE_MAN;
307241675Suqs	else {
308241675Suqs		fprintf(stderr, "%s: Bad argument\n", arg);
309241675Suqs		return(0);
310241675Suqs	}
311241675Suqs
312241675Suqs	return(1);
313241675Suqs}
314241675Suqs
315241675Suqsstatic int
316241675Suqstoptions(struct curparse *curp, char *arg)
317241675Suqs{
318241675Suqs
319241675Suqs	if (0 == strcmp(arg, "ascii"))
320241675Suqs		curp->outtype = OUTT_ASCII;
321241675Suqs	else if (0 == strcmp(arg, "lint")) {
322241675Suqs		curp->outtype = OUTT_LINT;
323241675Suqs		curp->wlevel  = MANDOCLEVEL_WARNING;
324241675Suqs	} else if (0 == strcmp(arg, "tree"))
325241675Suqs		curp->outtype = OUTT_TREE;
326241675Suqs	else if (0 == strcmp(arg, "man"))
327241675Suqs		curp->outtype = OUTT_MAN;
328241675Suqs	else if (0 == strcmp(arg, "html"))
329241675Suqs		curp->outtype = OUTT_HTML;
330241675Suqs	else if (0 == strcmp(arg, "utf8"))
331241675Suqs		curp->outtype = OUTT_UTF8;
332241675Suqs	else if (0 == strcmp(arg, "locale"))
333241675Suqs		curp->outtype = OUTT_LOCALE;
334241675Suqs	else if (0 == strcmp(arg, "xhtml"))
335241675Suqs		curp->outtype = OUTT_XHTML;
336241675Suqs	else if (0 == strcmp(arg, "ps"))
337241675Suqs		curp->outtype = OUTT_PS;
338241675Suqs	else if (0 == strcmp(arg, "pdf"))
339241675Suqs		curp->outtype = OUTT_PDF;
340241675Suqs	else {
341241675Suqs		fprintf(stderr, "%s: Bad argument\n", arg);
342241675Suqs		return(0);
343241675Suqs	}
344241675Suqs
345241675Suqs	return(1);
346241675Suqs}
347241675Suqs
348241675Suqsstatic int
349241675Suqswoptions(struct curparse *curp, char *arg)
350241675Suqs{
351241675Suqs	char		*v, *o;
352241675Suqs	const char	*toks[6];
353241675Suqs
354241675Suqs	toks[0] = "stop";
355241675Suqs	toks[1] = "all";
356241675Suqs	toks[2] = "warning";
357241675Suqs	toks[3] = "error";
358241675Suqs	toks[4] = "fatal";
359241675Suqs	toks[5] = NULL;
360241675Suqs
361241675Suqs	while (*arg) {
362241675Suqs		o = arg;
363241675Suqs		switch (getsubopt(&arg, UNCONST(toks), &v)) {
364241675Suqs		case (0):
365241675Suqs			curp->wstop = 1;
366241675Suqs			break;
367241675Suqs		case (1):
368241675Suqs			/* FALLTHROUGH */
369241675Suqs		case (2):
370241675Suqs			curp->wlevel = MANDOCLEVEL_WARNING;
371241675Suqs			break;
372241675Suqs		case (3):
373241675Suqs			curp->wlevel = MANDOCLEVEL_ERROR;
374241675Suqs			break;
375241675Suqs		case (4):
376241675Suqs			curp->wlevel = MANDOCLEVEL_FATAL;
377241675Suqs			break;
378241675Suqs		default:
379241675Suqs			fprintf(stderr, "-W%s: Bad argument\n", o);
380241675Suqs			return(0);
381241675Suqs		}
382241675Suqs	}
383241675Suqs
384241675Suqs	return(1);
385241675Suqs}
386241675Suqs
387241675Suqsstatic void
388241675Suqsmmsg(enum mandocerr t, enum mandoclevel lvl,
389241675Suqs		const char *file, int line, int col, const char *msg)
390241675Suqs{
391241675Suqs
392241675Suqs	fprintf(stderr, "%s:%d:%d: %s: %s",
393241675Suqs			file, line, col + 1,
394241675Suqs			mparse_strlevel(lvl),
395241675Suqs			mparse_strerror(t));
396241675Suqs
397241675Suqs	if (msg)
398241675Suqs		fprintf(stderr, ": %s", msg);
399241675Suqs
400241675Suqs	fputc('\n', stderr);
401241675Suqs}
402