]> git.cameronkatri.com Git - mandoc.git/blob - main.c
Using proper license template (const).
[mandoc.git] / main.c
1 /* $Id: main.c,v 1.25 2009/04/12 19:45:26 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #include <sys/stat.h>
18
19 #include <assert.h>
20 #include <err.h>
21 #include <fcntl.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "mdoc.h"
28 #include "man.h"
29
30 /* Account for FreeBSD and Linux in our declarations. */
31
32 #ifdef __linux__
33 extern int getsubopt(char **, char * const *, char **);
34 # ifndef __dead
35 # define __dead __attribute__((__noreturn__))
36 # endif
37 #elif defined(__dead2)
38 # ifndef __dead
39 # define __dead __dead2
40 # endif
41 #endif
42
43 typedef int (*out_mdoc)(void *, const struct mdoc *);
44 typedef int (*out_man)(void *, const struct man *);
45 typedef void (*out_free)(void *);
46
47 struct buf {
48 char *buf;
49 size_t sz;
50 };
51
52 enum intt {
53 INTT_AUTO,
54 INTT_MDOC,
55 INTT_MAN
56 };
57
58 enum outt {
59 OUTT_ASCII = 0,
60 OUTT_TREE,
61 OUTT_LINT
62 };
63
64 struct curparse {
65 const char *file; /* Current parse. */
66 int fd; /* Current parse. */
67 int wflags;
68 #define WARN_WALL 0x03 /* All-warnings mask. */
69 #define WARN_WCOMPAT (1 << 0) /* Compatibility warnings. */
70 #define WARN_WSYNTAX (1 << 1) /* Syntax warnings. */
71 #define WARN_WERR (1 << 2) /* Warnings->errors. */
72 int fflags;
73 #define IGN_SCOPE (1 << 0) /* Ignore scope errors. */
74 #define NO_IGN_ESCAPE (1 << 1) /* Don't ignore bad escapes. */
75 #define NO_IGN_MACRO (1 << 2) /* Don't ignore bad macros. */
76 #define NO_IGN_CHARS (1 << 3) /* Don't ignore bad chars. */
77 enum intt inttype; /* Input parsers. */
78 struct man *man;
79 struct man *lastman;
80 struct mdoc *mdoc;
81 struct mdoc *lastmdoc;
82 enum outt outtype; /* Output devices. */
83 out_mdoc outmdoc;
84 out_man outman;
85 out_free outfree;
86 void *outdata;
87 };
88
89 extern void *ascii_alloc(void);
90 extern int tree_mdoc(void *, const struct mdoc *);
91 extern int tree_man(void *, const struct man *);
92 extern int terminal_mdoc(void *, const struct mdoc *);
93 extern int terminal_man(void *, const struct man *);
94 extern void terminal_free(void *);
95
96 static int foptions(int *, char *);
97 static int toptions(enum outt *, char *);
98 static int moptions(enum intt *, char *);
99 static int woptions(int *, char *);
100 static int merr(void *, int, int, const char *);
101 static int manwarn(void *, int, int, const char *);
102 static int mdocwarn(void *, int, int,
103 enum mdoc_warn, const char *);
104 static int fstdin(struct buf *, struct buf *,
105 struct curparse *);
106 static int ffile(struct buf *, struct buf *,
107 const char *, struct curparse *);
108 static int fdesc(struct buf *, struct buf *,
109 struct curparse *);
110 static int pset(const char *, int, struct curparse *,
111 struct man **, struct mdoc **);
112 static struct man *man_init(struct curparse *);
113 static struct mdoc *mdoc_init(struct curparse *);
114 __dead static void version(void);
115 __dead static void usage(void);
116
117 extern char *__progname;
118
119
120 int
121 main(int argc, char *argv[])
122 {
123 int c, rc;
124 struct buf ln, blk;
125 struct curparse curp;
126
127 bzero(&curp, sizeof(struct curparse));
128
129 curp.inttype = INTT_AUTO;
130 curp.outtype = OUTT_ASCII;
131
132 /* LINTED */
133 while (-1 != (c = getopt(argc, argv, "f:m:VW:T:")))
134 switch (c) {
135 case ('f'):
136 if ( ! foptions(&curp.fflags, optarg))
137 return(0);
138 break;
139 case ('m'):
140 if ( ! moptions(&curp.inttype, optarg))
141 return(0);
142 break;
143 case ('T'):
144 if ( ! toptions(&curp.outtype, optarg))
145 return(0);
146 break;
147 case ('W'):
148 if ( ! woptions(&curp.wflags, optarg))
149 return(0);
150 break;
151 case ('V'):
152 version();
153 /* NOTREACHED */
154 default:
155 usage();
156 /* NOTREACHED */
157 }
158
159 argc -= optind;
160 argv += optind;
161
162 /* Configure buffers. */
163
164 bzero(&ln, sizeof(struct buf));
165 bzero(&blk, sizeof(struct buf));
166
167 rc = 1;
168
169 if (NULL == *argv)
170 if ( ! fstdin(&blk, &ln, &curp))
171 rc = 0;
172
173 while (rc && *argv) {
174 if ( ! ffile(&blk, &ln, *argv, &curp))
175 rc = 0;
176 argv++;
177 if (*argv && rc) {
178 if (curp.lastman)
179 if ( ! man_reset(curp.lastman))
180 rc = 0;
181 if (curp.lastmdoc)
182 if ( ! mdoc_reset(curp.lastmdoc))
183 rc = 0;
184 curp.lastman = NULL;
185 curp.lastmdoc = NULL;
186 }
187 }
188
189 if (blk.buf)
190 free(blk.buf);
191 if (ln.buf)
192 free(ln.buf);
193 if (curp.outfree)
194 (*curp.outfree)(curp.outdata);
195 if (curp.mdoc)
196 mdoc_free(curp.mdoc);
197 if (curp.man)
198 man_free(curp.man);
199
200 return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
201 }
202
203
204 __dead static void
205 version(void)
206 {
207
208 (void)printf("%s %s\n", __progname, VERSION);
209 exit(EXIT_SUCCESS);
210 }
211
212
213 __dead static void
214 usage(void)
215 {
216
217 (void)fprintf(stderr, "usage: %s [-V] [-foption...] "
218 "[-mformat] [-Toutput] [-Werr...]\n",
219 __progname);
220 exit(EXIT_FAILURE);
221 }
222
223
224 static struct man *
225 man_init(struct curparse *curp)
226 {
227 int pflags;
228 struct man *man;
229 struct man_cb mancb;
230
231 mancb.man_err = merr;
232 mancb.man_warn = manwarn;
233
234 pflags = MAN_IGN_MACRO; /* XXX */
235
236 if (curp->fflags & NO_IGN_MACRO)
237 pflags &= ~MAN_IGN_MACRO;
238
239 if (NULL == (man = man_alloc(curp, pflags, &mancb)))
240 warnx("memory exhausted");
241
242 return(man);
243 }
244
245
246 static struct mdoc *
247 mdoc_init(struct curparse *curp)
248 {
249 int pflags;
250 struct mdoc *mdoc;
251 struct mdoc_cb mdoccb;
252
253 mdoccb.mdoc_msg = NULL;
254 mdoccb.mdoc_err = merr;
255 mdoccb.mdoc_warn = mdocwarn;
256
257 pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE | MDOC_IGN_CHARS;
258
259 if (curp->fflags & IGN_SCOPE)
260 pflags |= MDOC_IGN_SCOPE;
261 if (curp->fflags & NO_IGN_ESCAPE)
262 pflags &= ~MDOC_IGN_ESCAPE;
263 if (curp->fflags & NO_IGN_MACRO)
264 pflags &= ~MDOC_IGN_MACRO;
265 if (curp->fflags & NO_IGN_CHARS)
266 pflags &= ~MDOC_IGN_CHARS;
267
268 if (NULL == (mdoc = mdoc_alloc(curp, pflags, &mdoccb)))
269 warnx("memory exhausted");
270
271 return(mdoc);
272 }
273
274
275 static int
276 fstdin(struct buf *blk, struct buf *ln, struct curparse *curp)
277 {
278
279 curp->file = "<stdin>";
280 curp->fd = STDIN_FILENO;
281 return(fdesc(blk, ln, curp));
282 }
283
284
285 static int
286 ffile(struct buf *blk, struct buf *ln,
287 const char *file, struct curparse *curp)
288 {
289 int c;
290
291 curp->file = file;
292 if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
293 warn("%s", curp->file);
294 return(0);
295 }
296
297 c = fdesc(blk, ln, curp);
298
299 if (-1 == close(curp->fd))
300 warn("%s", curp->file);
301
302 return(c);
303 }
304
305
306 static int
307 fdesc(struct buf *blk, struct buf *ln, struct curparse *curp)
308 {
309 size_t sz;
310 ssize_t ssz;
311 struct stat st;
312 int j, i, pos, lnn;
313 struct man *man;
314 struct mdoc *mdoc;
315
316 sz = BUFSIZ;
317 man = NULL;
318 mdoc = NULL;
319
320 /*
321 * Two buffers: ln and buf. buf is the input buffer optimised
322 * here for each file's block size. ln is a line buffer. Both
323 * growable, hence passed in by ptr-ptr.
324 */
325
326 if (-1 == fstat(curp->fd, &st))
327 warnx("%s", curp->file);
328 else if ((size_t)st.st_blksize > sz)
329 sz = st.st_blksize;
330
331 if (sz > blk->sz) {
332 blk->buf = realloc(blk->buf, sz);
333 if (NULL == blk->buf) {
334 warn("realloc");
335 return(0);
336 }
337 blk->sz = sz;
338 }
339
340 /* Fill buf with file blocksize. */
341
342 for (lnn = 0, pos = 0; ; ) {
343 if (-1 == (ssz = read(curp->fd, blk->buf, sz))) {
344 warn("%s", curp->file);
345 return(0);
346 } else if (0 == ssz)
347 break;
348
349 /* Parse the read block into partial or full lines. */
350
351 for (i = 0; i < (int)ssz; i++) {
352 if (pos >= (int)ln->sz) {
353 ln->sz += 256; /* Step-size. */
354 ln->buf = realloc(ln->buf, ln->sz);
355 if (NULL == ln->buf) {
356 warn("realloc");
357 return(0);
358 }
359 }
360
361 if ('\n' != blk->buf[i]) {
362 ln->buf[pos++] = blk->buf[i];
363 continue;
364 }
365
366 /* Check for CPP-escaped newline. */
367
368 if (pos > 0 && '\\' == ln->buf[pos - 1]) {
369 for (j = pos - 1; j >= 0; j--)
370 if ('\\' != ln->buf[j])
371 break;
372
373 if ( ! ((pos - j) % 2)) {
374 pos--;
375 lnn++;
376 continue;
377 }
378 }
379
380 ln->buf[pos] = 0;
381 lnn++;
382
383 /*
384 * If no manual parser has been assigned, then
385 * try to assign one in pset(), which may do
386 * nothing at all. After this, parse the manual
387 * line accordingly.
388 */
389
390 if ( ! (man || mdoc) && ! pset(ln->buf,
391 pos, curp, &man, &mdoc))
392 return(0);
393
394 pos = 0;
395
396 if (man && ! man_parseln(man, lnn, ln->buf))
397 return(0);
398 if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf))
399 return(0);
400 }
401 }
402
403 /* Note that a parser may not have been assigned, yet. */
404
405 if ( ! (man || mdoc)) {
406 warnx("%s: not a manual", curp->file);
407 return(0);
408 }
409
410 if (mdoc && ! mdoc_endparse(mdoc))
411 return(0);
412 if (man && ! man_endparse(man))
413 return(0);
414
415 /*
416 * If an output device hasn't been allocated, see if we should
417 * do so now. Note that not all outtypes have functions, so
418 * this switch statement may be superfluous, but it's
419 * low-overhead enough not to matter very much.
420 */
421
422 if ( ! (curp->outman && curp->outmdoc)) {
423 switch (curp->outtype) {
424 case (OUTT_TREE):
425 curp->outman = tree_man;
426 curp->outmdoc = tree_mdoc;
427 break;
428 case (OUTT_LINT):
429 break;
430 default:
431 curp->outdata = ascii_alloc();
432 curp->outman = terminal_man;
433 curp->outmdoc = terminal_mdoc;
434 curp->outfree = terminal_free;
435 break;
436 }
437 }
438
439 /* Execute the out device, if it exists. */
440
441 if (man && curp->outman)
442 if ( ! (*curp->outman)(curp->outdata, man))
443 return(0);
444 if (mdoc && curp->outmdoc)
445 if ( ! (*curp->outmdoc)(curp->outdata, mdoc))
446 return(0);
447
448 return(1);
449 }
450
451
452 static int
453 pset(const char *buf, int pos, struct curparse *curp,
454 struct man **man, struct mdoc **mdoc)
455 {
456
457 /*
458 * Try to intuit which kind of manual parser should be used. If
459 * passed in by command-line (-man, -mdoc), then use that
460 * explicitly. If passed as -mandoc, then try to guess from the
461 * line: either skip comments, use -mdoc when finding `.Dt', or
462 * default to -man, which is more lenient.
463 */
464
465 if (pos >= 3 && 0 == memcmp(buf, ".\\\"", 3))
466 return(1);
467
468 switch (curp->inttype) {
469 case (INTT_MDOC):
470 if (NULL == curp->mdoc)
471 curp->mdoc = mdoc_init(curp);
472 if (NULL == (*mdoc = curp->mdoc))
473 return(0);
474 curp->lastmdoc = *mdoc;
475 return(1);
476 case (INTT_MAN):
477 if (NULL == curp->man)
478 curp->man = man_init(curp);
479 if (NULL == (*man = curp->man))
480 return(0);
481 curp->lastman = *man;
482 return(1);
483 default:
484 break;
485 }
486
487 if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
488 if (NULL == curp->mdoc)
489 curp->mdoc = mdoc_init(curp);
490 if (NULL == (*mdoc = curp->mdoc))
491 return(0);
492 curp->lastmdoc = *mdoc;
493 return(1);
494 }
495
496 if (NULL == curp->man)
497 curp->man = man_init(curp);
498 if (NULL == (*man = curp->man))
499 return(0);
500 curp->lastman = *man;
501 return(1);
502 }
503
504
505 static int
506 moptions(enum intt *tflags, char *arg)
507 {
508
509 if (0 == strcmp(arg, "doc"))
510 *tflags = INTT_MDOC;
511 else if (0 == strcmp(arg, "andoc"))
512 *tflags = INTT_AUTO;
513 else if (0 == strcmp(arg, "an"))
514 *tflags = INTT_MAN;
515 else {
516 warnx("bad argument: -m%s", arg);
517 return(0);
518 }
519
520 return(1);
521 }
522
523
524 static int
525 toptions(enum outt *tflags, char *arg)
526 {
527
528 if (0 == strcmp(arg, "ascii"))
529 *tflags = OUTT_ASCII;
530 else if (0 == strcmp(arg, "lint"))
531 *tflags = OUTT_LINT;
532 else if (0 == strcmp(arg, "tree"))
533 *tflags = OUTT_TREE;
534 else {
535 warnx("bad argument: -T%s", arg);
536 return(0);
537 }
538
539 return(1);
540 }
541
542
543 /*
544 * Parse out the options for [-fopt...] setting compiler options. These
545 * can be comma-delimited or called again.
546 */
547 static int
548 foptions(int *fflags, char *arg)
549 {
550 char *v;
551 char *toks[6];
552
553 toks[0] = "ign-scope";
554 toks[1] = "no-ign-escape";
555 toks[2] = "no-ign-macro";
556 toks[3] = "no-ign-chars";
557 toks[4] = "strict";
558 toks[5] = NULL;
559
560 while (*arg)
561 switch (getsubopt(&arg, toks, &v)) {
562 case (0):
563 *fflags |= IGN_SCOPE;
564 break;
565 case (1):
566 *fflags |= NO_IGN_ESCAPE;
567 break;
568 case (2):
569 *fflags |= NO_IGN_MACRO;
570 break;
571 case (3):
572 *fflags |= NO_IGN_CHARS;
573 break;
574 case (4):
575 *fflags |= NO_IGN_ESCAPE |
576 NO_IGN_MACRO | NO_IGN_CHARS;
577 break;
578 default:
579 warnx("bad argument: -f%s", arg);
580 return(0);
581 }
582
583 return(1);
584 }
585
586
587 /*
588 * Parse out the options for [-Werr...], which sets warning modes.
589 * These can be comma-delimited or called again.
590 */
591 static int
592 woptions(int *wflags, char *arg)
593 {
594 char *v;
595 char *toks[5];
596
597 toks[0] = "all";
598 toks[1] = "compat";
599 toks[2] = "syntax";
600 toks[3] = "error";
601 toks[4] = NULL;
602
603 while (*arg)
604 switch (getsubopt(&arg, toks, &v)) {
605 case (0):
606 *wflags |= WARN_WALL;
607 break;
608 case (1):
609 *wflags |= WARN_WCOMPAT;
610 break;
611 case (2):
612 *wflags |= WARN_WSYNTAX;
613 break;
614 case (3):
615 *wflags |= WARN_WERR;
616 break;
617 default:
618 warnx("bad argument: -W%s", arg);
619 return(0);
620 }
621
622 return(1);
623 }
624
625
626 /* ARGSUSED */
627 static int
628 merr(void *arg, int line, int col, const char *msg)
629 {
630 struct curparse *curp;
631
632 curp = (struct curparse *)arg;
633
634 warnx("%s:%d: error: %s (column %d)",
635 curp->file, line, msg, col);
636 return(0);
637 }
638
639
640 static int
641 mdocwarn(void *arg, int line, int col,
642 enum mdoc_warn type, const char *msg)
643 {
644 struct curparse *curp;
645 char *wtype;
646
647 curp = (struct curparse *)arg;
648 wtype = NULL;
649
650 switch (type) {
651 case (WARN_COMPAT):
652 wtype = "compat";
653 if (curp->wflags & WARN_WCOMPAT)
654 break;
655 return(1);
656 case (WARN_SYNTAX):
657 wtype = "syntax";
658 if (curp->wflags & WARN_WSYNTAX)
659 break;
660 return(1);
661 }
662
663 assert(wtype);
664 warnx("%s:%d: %s warning: %s (column %d)",
665 curp->file, line, wtype, msg, col);
666
667 if ( ! (curp->wflags & WARN_WERR))
668 return(1);
669
670 warnx("%s: considering warnings as errors",
671 __progname);
672 return(0);
673 }
674
675
676 static int
677 manwarn(void *arg, int line, int col, const char *msg)
678 {
679 struct curparse *curp;
680
681 curp = (struct curparse *)arg;
682
683 if ( ! (curp->wflags & WARN_WSYNTAX))
684 return(1);
685
686 warnx("%s:%d: syntax warning: %s (column %d)",
687 curp->file, line, msg, col);
688
689 if ( ! (curp->wflags & WARN_WERR))
690 return(1);
691
692 warnx("%s: considering warnings as errors",
693 __progname);
694 return(0);
695 }