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