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