]>
git.cameronkatri.com Git - mandoc.git/blob - main.c
1 /* $Id: main.c,v 1.97 2010/07/04 22:04:04 schwarze Exp $ */
3 * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
40 #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
42 /* FIXME: Intel's compiler? LLVM? pcc? */
44 #if !defined(__GNUC__) || (__GNUC__ < 2)
46 # define __attribute__(x)
48 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
50 typedef void (*out_mdoc
)(void *, const struct mdoc
*);
51 typedef void (*out_man
)(void *, const struct man
*);
52 typedef void (*out_free
)(void *);
75 const char *file
; /* Current parse. */
76 int fd
; /* Current parse. */
78 /* FIXME: set by max error */
79 #define WARN_WALL (1 << 0) /* All-warnings mask. */
80 #define WARN_WERR (1 << 2) /* Warnings->errors. */
82 #define FL_IGN_SCOPE (1 << 0) /* Ignore scope errors. */
83 #define FL_NIGN_ESCAPE (1 << 1) /* Don't ignore bad escapes. */
84 #define FL_NIGN_MACRO (1 << 2) /* Don't ignore bad macros. */
85 #define FL_IGN_ERRORS (1 << 4) /* Ignore failed parse. */
86 #define FL_STRICT FL_NIGN_ESCAPE | \
87 FL_NIGN_MACRO /* ignore nothing */
88 enum intt inttype
; /* which parser to use */
89 struct man
*man
; /* man parser */
90 struct mdoc
*mdoc
; /* mdoc parser */
91 struct roff
*roff
; /* roff parser (!NULL) */
92 struct regset regs
; /* roff registers */
93 enum outt outtype
; /* which output to use */
94 out_mdoc outmdoc
; /* mdoc output ptr */
95 out_man outman
; /* man output ptr */
96 out_free outfree
; /* free output ptr */
97 void *outdata
; /* data for output */
98 char outopts
[BUFSIZ
]; /* buf of output opts */
101 static const char * const mandocerrs
[MANDOCERR_MAX
] = {
106 "text should be uppercase",
107 "sections out of conventional order",
108 "section name repeats",
109 "out of order prologue",
110 "repeated prologue entry",
111 "list type must come first",
114 "bad escape sequence",
115 "unterminated quoted string",
116 "argument requires the width argument",
117 "superfluous width argument",
120 "bad width argument",
121 "unknown manual section",
122 "section not in conventional manual section",
123 "end of line whitespace",
124 "blocks badly nested",
125 "scope open on exit",
129 "NAME section must come first",
131 "child violates parent syntax",
133 "list type repeated",
134 "display type repeated",
136 "manual name not yet set",
137 "obsolete macro ignored",
138 "empty macro ignored",
139 "macro not allowed in body",
140 "macro not allowed in prologue",
142 "bad NAME section contents",
144 "no text in this context",
146 "unknown macro will be lost",
148 "argument count wrong",
149 "request scope close w/none open",
150 "scope already open",
151 "macro requires line argument(s)",
152 "macro requires body argument(s)",
153 "macro requires argument(s)",
154 "no title in document",
156 "missing display type",
158 "line argument(s) will be lost",
159 "body argument(s) will be lost",
161 "generic fatal error",
163 "column syntax is inconsistent",
164 "displays may not be nested",
165 "unsupported display type",
166 "blocks badly nested",
167 "no such block is open",
168 "scope broken, syntax violated",
169 "line scope broken, syntax violated",
170 "argument count wrong, violates syntax",
171 "child violates parent syntax",
172 "argument count wrong, violates syntax",
174 "no document prologue",
175 "utsname system call failed",
179 static void fdesc(struct curparse
*);
180 static void ffile(const char *, struct curparse
*);
181 static int foptions(int *, char *);
182 static struct man
*man_init(struct curparse
*);
183 static struct mdoc
*mdoc_init(struct curparse
*);
184 static struct roff
*roff_init(struct curparse
*);
185 static int moptions(enum intt
*, char *);
186 static int mmsg(enum mandocerr
, void *,
187 int, int, const char *);
188 static int pset(const char *, int, struct curparse
*,
189 struct man
**, struct mdoc
**);
190 static int toptions(struct curparse
*, char *);
191 static void usage(void) __attribute__((noreturn
));
192 static void version(void) __attribute__((noreturn
));
193 static int woptions(int *, char *);
195 static const char *progname
;
196 static int with_fatal
;
197 static int with_error
;
200 main(int argc
, char *argv
[])
203 struct curparse curp
;
205 progname
= strrchr(argv
[0], '/');
206 if (progname
== NULL
)
211 memset(&curp
, 0, sizeof(struct curparse
));
213 curp
.inttype
= INTT_AUTO
;
214 curp
.outtype
= OUTT_ASCII
;
217 while (-1 != (c
= getopt(argc
, argv
, "f:m:O:T:VW:")))
220 if ( ! foptions(&curp
.fflags
, optarg
))
221 return(EXIT_FAILURE
);
224 if ( ! moptions(&curp
.inttype
, optarg
))
225 return(EXIT_FAILURE
);
228 (void)strlcat(curp
.outopts
, optarg
, BUFSIZ
);
229 (void)strlcat(curp
.outopts
, ",", BUFSIZ
);
232 if ( ! toptions(&curp
, optarg
))
233 return(EXIT_FAILURE
);
236 if ( ! woptions(&curp
.wflags
, optarg
))
237 return(EXIT_FAILURE
);
251 curp
.file
= "<stdin>";
252 curp
.fd
= STDIN_FILENO
;
260 if (with_fatal
&& !(curp
.fflags
& FL_IGN_ERRORS
))
266 (*curp
.outfree
)(curp
.outdata
);
268 mdoc_free(curp
.mdoc
);
272 roff_free(curp
.roff
);
274 return((with_fatal
|| with_error
) ?
275 EXIT_FAILURE
: EXIT_SUCCESS
);
283 (void)printf("%s %s\n", progname
, VERSION
);
292 (void)fprintf(stderr
, "usage: %s [-V] [-foption] "
293 "[-mformat] [-Ooption] [-Toutput] "
294 "[-Werr] [file...]\n", progname
);
300 man_init(struct curparse
*curp
)
304 /* Defaults from mandoc.1. */
306 pflags
= MAN_IGN_MACRO
| MAN_IGN_ESCAPE
;
308 if (curp
->fflags
& FL_NIGN_MACRO
)
309 pflags
&= ~MAN_IGN_MACRO
;
310 if (curp
->fflags
& FL_NIGN_ESCAPE
)
311 pflags
&= ~MAN_IGN_ESCAPE
;
313 return(man_alloc(&curp
->regs
, curp
, pflags
, mmsg
));
318 roff_init(struct curparse
*curp
)
321 return(roff_alloc(&curp
->regs
, mmsg
, curp
));
326 mdoc_init(struct curparse
*curp
)
330 /* Defaults from mandoc.1. */
332 pflags
= MDOC_IGN_MACRO
| MDOC_IGN_ESCAPE
;
334 if (curp
->fflags
& FL_IGN_SCOPE
)
335 pflags
|= MDOC_IGN_SCOPE
;
336 if (curp
->fflags
& FL_NIGN_ESCAPE
)
337 pflags
&= ~MDOC_IGN_ESCAPE
;
338 if (curp
->fflags
& FL_NIGN_MACRO
)
339 pflags
&= ~MDOC_IGN_MACRO
;
341 return(mdoc_alloc(&curp
->regs
, curp
, pflags
, mmsg
));
346 ffile(const char *file
, struct curparse
*curp
)
350 if (-1 == (curp
->fd
= open(curp
->file
, O_RDONLY
, 0))) {
358 if (-1 == close(curp
->fd
))
364 resize_buf(struct buf
*buf
, size_t initial
)
373 tmp
= realloc(buf
->buf
, sz
);
385 read_whole_file(struct curparse
*curp
, struct buf
*fb
, int *with_mmap
)
391 if (-1 == fstat(curp
->fd
, &st
)) {
398 * If we're a regular file, try just reading in the whole entry
399 * via mmap(). This is faster than reading it into blocks, and
400 * since each file is only a few bytes to begin with, I'm not
401 * concerned that this is going to tank any machines.
404 if (S_ISREG(st
.st_mode
)) {
405 if (st
.st_size
>= (1U << 31)) {
406 fprintf(stderr
, "%s: input too large\n",
412 fb
->sz
= (size_t)st
.st_size
;
413 fb
->buf
= mmap(NULL
, fb
->sz
, PROT_READ
,
414 MAP_FILE
|MAP_SHARED
, curp
->fd
, 0);
415 if (fb
->buf
!= MAP_FAILED
)
420 * If this isn't a regular file (like, say, stdin), then we must
421 * go the old way and just read things in bit by bit.
430 if (fb
->sz
== (1U << 31)) {
431 fprintf(stderr
, "%s: input too large\n",
435 if (! resize_buf(fb
, 65536))
438 ssz
= read(curp
->fd
, fb
->buf
+ (int)off
, fb
->sz
- off
);
458 fdesc(struct curparse
*curp
)
461 int i
, pos
, lnn
, lnn_start
, with_mmap
, of
;
471 memset(&ln
, 0, sizeof(struct buf
));
474 * Two buffers: ln and buf. buf is the input file and may be
475 * memory mapped. ln is a line buffer and grows on-demand.
478 if ( ! read_whole_file(curp
, &blk
, &with_mmap
))
481 if (NULL
== curp
->roff
)
482 curp
->roff
= roff_init(curp
);
483 if (NULL
== (roff
= curp
->roff
))
486 for (i
= 0, lnn
= 1; i
< (int)blk
.sz
;) {
489 while (i
< (int)blk
.sz
) {
490 if ('\n' == blk
.buf
[i
]) {
495 /* Trailing backslash is like a plain character. */
496 if ('\\' != blk
.buf
[i
] || i
+ 1 == (int)blk
.sz
) {
497 if (pos
>= (int)ln
.sz
)
498 if (! resize_buf(&ln
, 256))
500 ln
.buf
[pos
++] = blk
.buf
[i
++];
503 /* Found an escape and at least one other character. */
504 if ('\n' == blk
.buf
[i
+ 1]) {
505 /* Escaped newlines are skipped over */
510 if ('"' == blk
.buf
[i
+ 1]) {
512 /* Comment, skip to end of line */
513 for (; i
< (int)blk
.sz
; ++i
) {
514 if ('\n' == blk
.buf
[i
]) {
520 /* Backout trailing whitespaces */
521 for (; pos
> 0; --pos
) {
522 if (ln
.buf
[pos
- 1] != ' ')
524 if (pos
> 2 && ln
.buf
[pos
- 2] == '\\')
529 /* Some other escape sequence, copy and continue. */
530 if (pos
+ 1 >= (int)ln
.sz
)
531 if (! resize_buf(&ln
, 256))
534 ln
.buf
[pos
++] = blk
.buf
[i
++];
535 ln
.buf
[pos
++] = blk
.buf
[i
++];
538 if (pos
>= (int)ln
.sz
)
539 if (! resize_buf(&ln
, 256))
544 * A significant amount of complexity is contained by
545 * the roff preprocessor. It's line-oriented but can be
546 * expressed on one line, so we need at times to
547 * readjust our starting point and re-run it. The roff
548 * preprocessor can also readjust the buffers with new
549 * data, so we pass them in wholesale.
554 re
= roff_parseln(roff
, lnn_start
,
555 &ln
.buf
, &ln
.sz
, of
, &of
);
556 } while (ROFF_RERUN
== re
);
560 else if (ROFF_ERR
== re
)
564 * If input parsers have not been allocated, do so now.
565 * We keep these instanced betwen parsers, but set them
566 * locally per parse routine since we can use different
567 * parsers with each one.
570 if ( ! (man
|| mdoc
))
571 if ( ! pset(ln
.buf
+ of
, pos
- of
, curp
, &man
, &mdoc
))
574 /* Lastly, push down into the parsers themselves. */
576 if (man
&& ! man_parseln(man
, lnn_start
, ln
.buf
, of
))
578 if (mdoc
&& ! mdoc_parseln(mdoc
, lnn_start
, ln
.buf
, of
))
582 /* NOTE a parser may not have been assigned, yet. */
584 if ( ! (man
|| mdoc
)) {
585 fprintf(stderr
, "%s: Not a manual\n", curp
->file
);
589 /* Clean up the parse routine ASTs. */
591 if (mdoc
&& ! mdoc_endparse(mdoc
))
593 if (man
&& ! man_endparse(man
))
595 if (roff
&& ! roff_endparse(roff
))
598 /* If unset, allocate output dev now (if applicable). */
600 if ( ! (curp
->outman
&& curp
->outmdoc
)) {
601 switch (curp
->outtype
) {
603 curp
->outdata
= xhtml_alloc(curp
->outopts
);
606 curp
->outdata
= html_alloc(curp
->outopts
);
609 curp
->outdata
= ascii_alloc(curp
->outopts
);
610 curp
->outfree
= ascii_free
;
613 curp
->outdata
= ps_alloc(curp
->outopts
);
614 curp
->outfree
= ps_free
;
620 switch (curp
->outtype
) {
624 curp
->outman
= html_man
;
625 curp
->outmdoc
= html_mdoc
;
626 curp
->outfree
= html_free
;
629 curp
->outman
= tree_man
;
630 curp
->outmdoc
= tree_mdoc
;
635 curp
->outman
= terminal_man
;
636 curp
->outmdoc
= terminal_mdoc
;
643 /* Execute the out device, if it exists. */
645 if (man
&& curp
->outman
)
646 (*curp
->outman
)(curp
->outdata
, man
);
647 if (mdoc
&& curp
->outmdoc
)
648 (*curp
->outmdoc
)(curp
->outdata
, mdoc
);
651 memset(&curp
->regs
, 0, sizeof(struct regset
));
661 munmap(blk
.buf
, blk
.sz
);
674 pset(const char *buf
, int pos
, struct curparse
*curp
,
675 struct man
**man
, struct mdoc
**mdoc
)
680 * Try to intuit which kind of manual parser should be used. If
681 * passed in by command-line (-man, -mdoc), then use that
682 * explicitly. If passed as -mandoc, then try to guess from the
683 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
684 * default to -man, which is more lenient.
687 if ('.' == buf
[0] || '\'' == buf
[0]) {
688 for (i
= 1; buf
[i
]; i
++)
689 if (' ' != buf
[i
] && '\t' != buf
[i
])
695 switch (curp
->inttype
) {
697 if (NULL
== curp
->mdoc
)
698 curp
->mdoc
= mdoc_init(curp
);
699 if (NULL
== (*mdoc
= curp
->mdoc
))
703 if (NULL
== curp
->man
)
704 curp
->man
= man_init(curp
);
705 if (NULL
== (*man
= curp
->man
))
712 if (pos
>= 3 && 0 == memcmp(buf
, ".Dd", 3)) {
713 if (NULL
== curp
->mdoc
)
714 curp
->mdoc
= mdoc_init(curp
);
715 if (NULL
== (*mdoc
= curp
->mdoc
))
720 if (NULL
== curp
->man
)
721 curp
->man
= man_init(curp
);
722 if (NULL
== (*man
= curp
->man
))
729 moptions(enum intt
*tflags
, char *arg
)
732 if (0 == strcmp(arg
, "doc"))
734 else if (0 == strcmp(arg
, "andoc"))
736 else if (0 == strcmp(arg
, "an"))
739 fprintf(stderr
, "%s: Bad argument\n", arg
);
748 toptions(struct curparse
*curp
, char *arg
)
751 if (0 == strcmp(arg
, "ascii"))
752 curp
->outtype
= OUTT_ASCII
;
753 else if (0 == strcmp(arg
, "lint")) {
754 curp
->outtype
= OUTT_LINT
;
755 curp
->wflags
|= WARN_WALL
;
756 curp
->fflags
|= FL_STRICT
;
758 else if (0 == strcmp(arg
, "tree"))
759 curp
->outtype
= OUTT_TREE
;
760 else if (0 == strcmp(arg
, "html"))
761 curp
->outtype
= OUTT_HTML
;
762 else if (0 == strcmp(arg
, "xhtml"))
763 curp
->outtype
= OUTT_XHTML
;
764 else if (0 == strcmp(arg
, "ps"))
765 curp
->outtype
= OUTT_PS
;
767 fprintf(stderr
, "%s: Bad argument\n", arg
);
776 foptions(int *fflags
, char *arg
)
781 toks
[0] = "ign-scope";
782 toks
[1] = "no-ign-escape";
783 toks
[2] = "no-ign-macro";
784 toks
[3] = "ign-errors";
786 toks
[5] = "ign-escape";
791 switch (getsubopt(&arg
, UNCONST(toks
), &v
)) {
793 *fflags
|= FL_IGN_SCOPE
;
796 *fflags
|= FL_NIGN_ESCAPE
;
799 *fflags
|= FL_NIGN_MACRO
;
802 *fflags
|= FL_IGN_ERRORS
;
805 *fflags
|= FL_STRICT
;
808 *fflags
&= ~FL_NIGN_ESCAPE
;
811 fprintf(stderr
, "%s: Bad argument\n", o
);
821 woptions(int *wflags
, char *arg
)
832 switch (getsubopt(&arg
, UNCONST(toks
), &v
)) {
834 *wflags
|= WARN_WALL
;
837 *wflags
|= WARN_WERR
;
840 fprintf(stderr
, "%s: Bad argument\n", o
);
850 mmsg(enum mandocerr t
, void *arg
, int ln
, int col
, const char *msg
)
856 cp
= (struct curparse
*)arg
;
860 if (t
>= MANDOCERR_FATAL
) {
865 if ( ! (WARN_WALL
& cp
->wflags
))
867 if (t
>= MANDOCERR_ERROR
) {
871 if (WARN_WERR
& cp
->wflags
) {
877 fprintf(stderr
, "%s:%d:%d:", cp
->file
, ln
, col
+ 1);
879 fprintf(stderr
, " %s:", level
);
880 fprintf(stderr
, " %s", mandocerrs
[t
]);
882 fprintf(stderr
, ": %s", msg
);