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