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