Merge branch 'vendor/MDOCML'
[dragonfly.git] / contrib / mdocml / 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 }