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