]>
git.cameronkatri.com Git - mandoc.git/blob - main.c
1 /* $Id: main.c,v 1.91 2010/06/26 15:36:37 kristaps Exp $ */
3 * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
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.
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.
39 #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
41 /* FIXME: Intel's compiler? LLVM? pcc? */
43 #if !defined(__GNUC__) || (__GNUC__ < 2)
45 # define __attribute__(x)
47 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
49 typedef void (*out_mdoc
)(void *, const struct mdoc
*);
50 typedef void (*out_man
)(void *, const struct man
*);
51 typedef void (*out_free
)(void *);
74 const char *file
; /* Current parse. */
75 int fd
; /* Current parse. */
77 /* FIXME: set by max error */
78 #define WARN_WALL (1 << 0) /* All-warnings mask. */
79 #define WARN_WERR (1 << 2) /* Warnings->errors. */
81 #define FL_IGN_SCOPE (1 << 0) /* Ignore scope errors. */
82 #define FL_NIGN_ESCAPE (1 << 1) /* Don't ignore bad escapes. */
83 #define FL_NIGN_MACRO (1 << 2) /* Don't ignore bad macros. */
84 #define FL_IGN_ERRORS (1 << 4) /* Ignore failed parse. */
85 #define FL_STRICT FL_NIGN_ESCAPE | \
86 FL_NIGN_MACRO /* ignore nothing */
87 enum intt inttype
; /* which parser to use */
88 struct man
*man
; /* man parser */
89 struct mdoc
*mdoc
; /* mdoc parser */
90 struct roff
*roff
; /* roff parser (!NULL) */
91 enum outt outtype
; /* which output to use */
92 out_mdoc outmdoc
; /* mdoc output ptr */
93 out_man outman
; /* man output ptr */
94 out_free outfree
; /* free output ptr */
95 void *outdata
; /* data for output */
96 char outopts
[BUFSIZ
]; /* buf of output opts */
99 static const char * const mandocerrs
[MANDOCERR_MAX
] = {
101 "text should be uppercase",
102 "sections out of conventional order",
103 "section name repeats",
104 "out of order prologue",
105 "repeated prologue entry",
106 "list type must come first",
109 "bad escape sequence",
110 "unterminated quoted string",
111 "argument requires the width argument",
112 "superfluous width argument",
115 "bad width argument",
116 "unknown manual section",
117 "section not in conventional manual section",
118 "end of line whitespace",
119 "scope open on exit",
120 "NAME section must come first",
122 "child violates parent syntax",
124 "list type repeated",
125 "display type repeated",
127 "manual name not yet set",
128 "obsolete macro ignored",
129 "empty macro ignored",
130 "macro not allowed in body",
131 "macro not allowed in prologue",
133 "bad NAME section contents",
135 "no text in this context",
137 "unknown macro will be lost",
140 "argument count wrong",
141 "request scope close w/none open",
142 "scope already open",
143 "macro requires line argument(s)",
144 "macro requires body argument(s)",
145 "macro requires argument(s)",
146 "no title in document",
148 "missing display type",
149 "line argument(s) will be lost",
150 "body argument(s) will be lost",
151 "column syntax is inconsistent",
153 "displays may not be nested",
154 "unsupported display type",
155 "no scope to rewind: syntax violated",
156 "scope broken, syntax violated",
157 "line scope broken, syntax violated",
158 "argument count wrong, violates syntax",
159 "child violates parent syntax",
160 "argument count wrong, violates syntax",
162 "no document prologue",
163 "utsname system call failed",
167 static void fdesc(struct curparse
*);
168 static void ffile(const char *, struct curparse
*);
169 static int foptions(int *, char *);
170 static struct man
*man_init(struct curparse
*);
171 static struct mdoc
*mdoc_init(struct curparse
*);
172 static struct roff
*roff_init(struct curparse
*);
173 static int moptions(enum intt
*, char *);
174 static int mmsg(enum mandocerr
, void *,
175 int, int, const char *);
176 static int pset(const char *, int, struct curparse
*,
177 struct man
**, struct mdoc
**);
178 static int toptions(struct curparse
*, char *);
179 static void usage(void) __attribute__((noreturn
));
180 static void version(void) __attribute__((noreturn
));
181 static int woptions(int *, char *);
183 static const char *progname
;
184 static int with_error
;
185 static int with_warning
;
188 main(int argc
, char *argv
[])
191 struct curparse curp
;
193 progname
= strrchr(argv
[0], '/');
194 if (progname
== NULL
)
199 memset(&curp
, 0, sizeof(struct curparse
));
201 curp
.inttype
= INTT_AUTO
;
202 curp
.outtype
= OUTT_ASCII
;
205 while (-1 != (c
= getopt(argc
, argv
, "f:m:O:T:VW:")))
208 if ( ! foptions(&curp
.fflags
, optarg
))
209 return(EXIT_FAILURE
);
212 if ( ! moptions(&curp
.inttype
, optarg
))
213 return(EXIT_FAILURE
);
216 (void)strlcat(curp
.outopts
, optarg
, BUFSIZ
);
217 (void)strlcat(curp
.outopts
, ",", BUFSIZ
);
220 if ( ! toptions(&curp
, optarg
))
221 return(EXIT_FAILURE
);
224 if ( ! woptions(&curp
.wflags
, optarg
))
225 return(EXIT_FAILURE
);
239 curp
.file
= "<stdin>";
240 curp
.fd
= STDIN_FILENO
;
248 if (with_error
&& !(curp
.fflags
& FL_IGN_ERRORS
))
254 (*curp
.outfree
)(curp
.outdata
);
256 mdoc_free(curp
.mdoc
);
260 roff_free(curp
.roff
);
262 return((with_warning
|| with_error
) ?
263 EXIT_FAILURE
: EXIT_SUCCESS
);
271 (void)printf("%s %s\n", progname
, VERSION
);
280 (void)fprintf(stderr
, "usage: %s [-V] [-foption] "
281 "[-mformat] [-Ooption] [-Toutput] "
282 "[-Werr] [file...]\n", progname
);
288 man_init(struct curparse
*curp
)
292 /* Defaults from mandoc.1. */
294 pflags
= MAN_IGN_MACRO
| MAN_IGN_ESCAPE
;
296 if (curp
->fflags
& FL_NIGN_MACRO
)
297 pflags
&= ~MAN_IGN_MACRO
;
298 if (curp
->fflags
& FL_NIGN_ESCAPE
)
299 pflags
&= ~MAN_IGN_ESCAPE
;
301 return(man_alloc(curp
, pflags
, mmsg
));
306 roff_init(struct curparse
*curp
)
309 return(roff_alloc(mmsg
, curp
));
314 mdoc_init(struct curparse
*curp
)
318 /* Defaults from mandoc.1. */
320 pflags
= MDOC_IGN_MACRO
| MDOC_IGN_ESCAPE
;
322 if (curp
->fflags
& FL_IGN_SCOPE
)
323 pflags
|= MDOC_IGN_SCOPE
;
324 if (curp
->fflags
& FL_NIGN_ESCAPE
)
325 pflags
&= ~MDOC_IGN_ESCAPE
;
326 if (curp
->fflags
& FL_NIGN_MACRO
)
327 pflags
&= ~MDOC_IGN_MACRO
;
329 return(mdoc_alloc(curp
, pflags
, mmsg
));
334 ffile(const char *file
, struct curparse
*curp
)
338 if (-1 == (curp
->fd
= open(curp
->file
, O_RDONLY
, 0))) {
346 if (-1 == close(curp
->fd
))
352 resize_buf(struct buf
*buf
, size_t initial
)
361 tmp
= realloc(buf
->buf
, sz
);
373 read_whole_file(struct curparse
*curp
, struct buf
*fb
, int *with_mmap
)
379 if (-1 == fstat(curp
->fd
, &st
)) {
386 * If we're a regular file, try just reading in the whole entry
387 * via mmap(). This is faster than reading it into blocks, and
388 * since each file is only a few bytes to begin with, I'm not
389 * concerned that this is going to tank any machines.
392 if (S_ISREG(st
.st_mode
)) {
393 if (st
.st_size
>= (1U << 31)) {
394 fprintf(stderr
, "%s: input too large\n",
400 fb
->sz
= (size_t)st
.st_size
;
401 fb
->buf
= mmap(NULL
, fb
->sz
, PROT_READ
,
402 MAP_FILE
|MAP_SHARED
, curp
->fd
, 0);
403 if (fb
->buf
!= MAP_FAILED
)
408 * If this isn't a regular file (like, say, stdin), then we must
409 * go the old way and just read things in bit by bit.
418 if (fb
->sz
== (1U << 31)) {
419 fprintf(stderr
, "%s: input too large\n",
423 if (! resize_buf(fb
, 65536))
426 ssz
= read(curp
->fd
, fb
->buf
+ (int)off
, fb
->sz
- off
);
446 fdesc(struct curparse
*curp
)
449 int i
, pos
, lnn
, lnn_start
, with_mmap
, of
;
459 memset(&ln
, 0, sizeof(struct buf
));
460 memset(®s
, 0, sizeof(struct regset
));
463 * Two buffers: ln and buf. buf is the input file and may be
464 * memory mapped. ln is a line buffer and grows on-demand.
467 if ( ! read_whole_file(curp
, &blk
, &with_mmap
))
470 if (NULL
== curp
->roff
)
471 curp
->roff
= roff_init(curp
);
472 if (NULL
== (roff
= curp
->roff
))
475 for (i
= 0, lnn
= 1; i
< (int)blk
.sz
;) {
478 while (i
< (int)blk
.sz
) {
479 if ('\n' == blk
.buf
[i
]) {
484 /* Trailing backslash is like a plain character. */
485 if ('\\' != blk
.buf
[i
] || i
+ 1 == (int)blk
.sz
) {
486 if (pos
>= (int)ln
.sz
)
487 if (! resize_buf(&ln
, 256))
489 ln
.buf
[pos
++] = blk
.buf
[i
++];
492 /* Found an escape and at least one other character. */
493 if ('\n' == blk
.buf
[i
+ 1]) {
494 /* Escaped newlines are skipped over */
499 if ('"' == blk
.buf
[i
+ 1]) {
501 /* Comment, skip to end of line */
502 for (; i
< (int)blk
.sz
; ++i
) {
503 if ('\n' == blk
.buf
[i
]) {
509 /* Backout trailing whitespaces */
510 for (; pos
> 0; --pos
) {
511 if (ln
.buf
[pos
- 1] != ' ')
513 if (pos
> 2 && ln
.buf
[pos
- 2] == '\\')
518 /* Some other escape sequence, copy and continue. */
519 if (pos
+ 1 >= (int)ln
.sz
)
520 if (! resize_buf(&ln
, 256))
523 ln
.buf
[pos
++] = blk
.buf
[i
++];
524 ln
.buf
[pos
++] = blk
.buf
[i
++];
527 if (pos
>= (int)ln
.sz
)
528 if (! resize_buf(&ln
, 256))
533 * A significant amount of complexity is contained by
534 * the roff preprocessor. It's line-oriented but can be
535 * expressed on one line, so we need at times to
536 * readjust our starting point and re-run it. The roff
537 * preprocessor can also readjust the buffers with new
538 * data, so we pass them in wholesale.
543 re
= roff_parseln(roff
, ®s
, lnn_start
,
544 &ln
.buf
, &ln
.sz
, of
, &of
);
545 } while (ROFF_RERUN
== re
);
549 else if (ROFF_ERR
== re
)
553 * If input parsers have not been allocated, do so now.
554 * We keep these instanced betwen parsers, but set them
555 * locally per parse routine since we can use different
556 * parsers with each one.
559 if ( ! (man
|| mdoc
))
560 if ( ! pset(ln
.buf
+ of
, pos
- of
, curp
, &man
, &mdoc
))
563 /* Lastly, push down into the parsers themselves. */
565 if (man
&& ! man_parseln(man
, ®s
, lnn_start
, ln
.buf
, of
))
567 if (mdoc
&& ! mdoc_parseln(mdoc
, ®s
, lnn_start
, ln
.buf
, of
))
571 /* NOTE a parser may not have been assigned, yet. */
573 if ( ! (man
|| mdoc
)) {
574 fprintf(stderr
, "%s: Not a manual\n", curp
->file
);
578 /* Clean up the parse routine ASTs. */
580 if (mdoc
&& ! mdoc_endparse(mdoc
))
582 if (man
&& ! man_endparse(man
))
584 if (roff
&& ! roff_endparse(roff
))
587 /* If unset, allocate output dev now (if applicable). */
589 if ( ! (curp
->outman
&& curp
->outmdoc
)) {
590 switch (curp
->outtype
) {
592 curp
->outdata
= xhtml_alloc(curp
->outopts
);
595 curp
->outdata
= html_alloc(curp
->outopts
);
598 curp
->outdata
= ascii_alloc(curp
->outopts
);
599 curp
->outfree
= ascii_free
;
602 curp
->outdata
= ps_alloc();
603 curp
->outfree
= ps_free
;
609 switch (curp
->outtype
) {
613 curp
->outman
= html_man
;
614 curp
->outmdoc
= html_mdoc
;
615 curp
->outfree
= html_free
;
618 curp
->outman
= tree_man
;
619 curp
->outmdoc
= tree_mdoc
;
624 curp
->outman
= terminal_man
;
625 curp
->outmdoc
= terminal_mdoc
;
632 /* Execute the out device, if it exists. */
634 if (man
&& curp
->outman
)
635 (*curp
->outman
)(curp
->outdata
, man
);
636 if (mdoc
&& curp
->outmdoc
)
637 (*curp
->outmdoc
)(curp
->outdata
, mdoc
);
649 munmap(blk
.buf
, blk
.sz
);
662 pset(const char *buf
, int pos
, struct curparse
*curp
,
663 struct man
**man
, struct mdoc
**mdoc
)
668 * Try to intuit which kind of manual parser should be used. If
669 * passed in by command-line (-man, -mdoc), then use that
670 * explicitly. If passed as -mandoc, then try to guess from the
671 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
672 * default to -man, which is more lenient.
675 if ('.' == buf
[0] || '\'' == buf
[0]) {
676 for (i
= 1; buf
[i
]; i
++)
677 if (' ' != buf
[i
] && '\t' != buf
[i
])
683 switch (curp
->inttype
) {
685 if (NULL
== curp
->mdoc
)
686 curp
->mdoc
= mdoc_init(curp
);
687 if (NULL
== (*mdoc
= curp
->mdoc
))
691 if (NULL
== curp
->man
)
692 curp
->man
= man_init(curp
);
693 if (NULL
== (*man
= curp
->man
))
700 if (pos
>= 3 && 0 == memcmp(buf
, ".Dd", 3)) {
701 if (NULL
== curp
->mdoc
)
702 curp
->mdoc
= mdoc_init(curp
);
703 if (NULL
== (*mdoc
= curp
->mdoc
))
708 if (NULL
== curp
->man
)
709 curp
->man
= man_init(curp
);
710 if (NULL
== (*man
= curp
->man
))
717 moptions(enum intt
*tflags
, char *arg
)
720 if (0 == strcmp(arg
, "doc"))
722 else if (0 == strcmp(arg
, "andoc"))
724 else if (0 == strcmp(arg
, "an"))
727 fprintf(stderr
, "%s: Bad argument\n", arg
);
736 toptions(struct curparse
*curp
, char *arg
)
739 if (0 == strcmp(arg
, "ascii"))
740 curp
->outtype
= OUTT_ASCII
;
741 else if (0 == strcmp(arg
, "lint")) {
742 curp
->outtype
= OUTT_LINT
;
743 curp
->wflags
|= WARN_WALL
;
744 curp
->fflags
|= FL_STRICT
;
746 else if (0 == strcmp(arg
, "tree"))
747 curp
->outtype
= OUTT_TREE
;
748 else if (0 == strcmp(arg
, "html"))
749 curp
->outtype
= OUTT_HTML
;
750 else if (0 == strcmp(arg
, "xhtml"))
751 curp
->outtype
= OUTT_XHTML
;
752 else if (0 == strcmp(arg
, "ps"))
753 curp
->outtype
= OUTT_PS
;
755 fprintf(stderr
, "%s: Bad argument\n", arg
);
764 foptions(int *fflags
, char *arg
)
769 toks
[0] = "ign-scope";
770 toks
[1] = "no-ign-escape";
771 toks
[2] = "no-ign-macro";
772 toks
[3] = "ign-errors";
774 toks
[5] = "ign-escape";
779 switch (getsubopt(&arg
, UNCONST(toks
), &v
)) {
781 *fflags
|= FL_IGN_SCOPE
;
784 *fflags
|= FL_NIGN_ESCAPE
;
787 *fflags
|= FL_NIGN_MACRO
;
790 *fflags
|= FL_IGN_ERRORS
;
793 *fflags
|= FL_STRICT
;
796 *fflags
&= ~FL_NIGN_ESCAPE
;
799 fprintf(stderr
, "%s: Bad argument\n", o
);
809 woptions(int *wflags
, char *arg
)
820 switch (getsubopt(&arg
, UNCONST(toks
), &v
)) {
822 *wflags
|= WARN_WALL
;
825 *wflags
|= WARN_WERR
;
828 fprintf(stderr
, "%s: Bad argument\n", o
);
838 mmsg(enum mandocerr t
, void *arg
, int ln
, int col
, const char *msg
)
842 cp
= (struct curparse
*)arg
;
844 if (t
<= MANDOCERR_ERROR
) {
845 if ( ! (cp
->wflags
& WARN_WALL
))
851 fprintf(stderr
, "%s:%d:%d: %s", cp
->file
,
852 ln
, col
+ 1, mandocerrs
[t
]);
855 fprintf(stderr
, ": %s", msg
);
859 /* This is superfluous, but whatever. */
860 if (t
> MANDOCERR_ERROR
)
862 if (cp
->wflags
& WARN_WERR
) {