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