]> git.cameronkatri.com Git - mandoc.git/blob - main.c
Add support for 1/2, 1/4, and 3/4 (needed by eqn).
[mandoc.git] / main.c
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 }