]> git.cameronkatri.com Git - mandoc.git/blob - main.c
Have horizontal spanner not clobber pre-set width.
[mandoc.git] / main.c
1 /* $Id: main.c,v 1.134 2011/01/04 12:06:21 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010 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 <sys/mman.h>
23 #include <sys/stat.h>
24
25 #include <assert.h>
26 #include <ctype.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <stdint.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include "mandoc.h"
35 #include "main.h"
36 #include "mdoc.h"
37 #include "man.h"
38 #include "roff.h"
39
40 #ifndef MAP_FILE
41 #define MAP_FILE 0
42 #endif
43
44 #define REPARSE_LIMIT 1000
45 #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
46
47 /* FIXME: Intel's compiler? LLVM? pcc? */
48
49 #if !defined(__GNUC__) || (__GNUC__ < 2)
50 # if !defined(lint)
51 # define __attribute__(x)
52 # endif
53 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
54
55 typedef void (*out_mdoc)(void *, const struct mdoc *);
56 typedef void (*out_man)(void *, const struct man *);
57 typedef void (*out_free)(void *);
58
59 struct buf {
60 char *buf;
61 size_t sz;
62 };
63
64 enum intt {
65 INTT_AUTO,
66 INTT_MDOC,
67 INTT_MAN
68 };
69
70 enum outt {
71 OUTT_ASCII = 0,
72 OUTT_TREE,
73 OUTT_HTML,
74 OUTT_XHTML,
75 OUTT_LINT,
76 OUTT_PS,
77 OUTT_PDF
78 };
79
80 struct curparse {
81 const char *file; /* Current parse. */
82 int fd; /* Current parse. */
83 int line; /* Line number in the file. */
84 enum mandoclevel wlevel; /* Ignore messages below this. */
85 int wstop; /* Stop after a file with a warning. */
86 enum intt inttype; /* which parser to use */
87 struct man *pman; /* persistent man parser */
88 struct mdoc *pmdoc; /* persistent mdoc parser */
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 int reparse_count; /* finite interpolation stack */
94 enum outt outtype; /* which output to use */
95 out_mdoc outmdoc; /* mdoc output ptr */
96 out_man outman; /* man output ptr */
97 out_free outfree; /* free output ptr */
98 void *outdata; /* data for output */
99 char outopts[BUFSIZ]; /* buf of output opts */
100 };
101
102 static const char * const mandoclevels[MANDOCLEVEL_MAX] = {
103 "SUCCESS",
104 "RESERVED",
105 "WARNING",
106 "ERROR",
107 "FATAL",
108 "BADARG",
109 "SYSERR"
110 };
111
112 static const enum mandocerr mandoclimits[MANDOCLEVEL_MAX] = {
113 MANDOCERR_OK,
114 MANDOCERR_WARNING,
115 MANDOCERR_WARNING,
116 MANDOCERR_ERROR,
117 MANDOCERR_FATAL,
118 MANDOCERR_MAX,
119 MANDOCERR_MAX
120 };
121
122 static const char * const mandocerrs[MANDOCERR_MAX] = {
123 "ok",
124
125 "generic warning",
126
127 /* related to the prologue */
128 "no title in document",
129 "document title should be all caps",
130 "unknown manual section",
131 "cannot parse date argument",
132 "prologue macros out of order",
133 "duplicate prologue macro",
134 "macro not allowed in prologue",
135 "macro not allowed in body",
136
137 /* related to document structure */
138 ".so is fragile, better use ln(1)",
139 "NAME section must come first",
140 "bad NAME section contents",
141 "manual name not yet set",
142 "sections out of conventional order",
143 "duplicate section name",
144 "section not in conventional manual section",
145
146 /* related to macros and nesting */
147 "skipping obsolete macro",
148 "skipping paragraph macro",
149 "blocks badly nested",
150 "child violates parent syntax",
151 "nested displays are not portable",
152 "already in literal mode",
153
154 /* related to missing macro arguments */
155 "skipping empty macro",
156 "argument count wrong",
157 "missing display type",
158 "list type must come first",
159 "tag lists require a width argument",
160 "missing font type",
161
162 /* related to bad macro arguments */
163 "skipping argument",
164 "duplicate argument",
165 "duplicate display type",
166 "duplicate list type",
167 "unknown AT&T UNIX version",
168 "bad Boolean value",
169 "unknown font",
170 "unknown standard specifier",
171 "bad width argument",
172
173 /* related to plain text */
174 "blank line in non-literal context",
175 "tab in non-literal context",
176 "end of line whitespace",
177 "bad comment style",
178 "unknown escape sequence",
179 "unterminated quoted string",
180
181 /* related to tables */
182 "extra data cells",
183
184 "generic error",
185
186 /* related to tables */
187 "bad table syntax",
188 "bad table option",
189 "bad table layout",
190 "no table layout cells specified",
191 "no table data cells specified",
192 "ignore data in cell",
193
194 "input stack limit exceeded, infinite loop?",
195 "skipping bad character",
196 "skipping text before the first section header",
197 "skipping unknown macro",
198 "NOT IMPLEMENTED: skipping request",
199 "line scope broken",
200 "argument count wrong",
201 "skipping end of block that is not open",
202 "missing end of block",
203 "scope open on exit",
204 "uname(3) system call failed",
205 "macro requires line argument(s)",
206 "macro requires body argument(s)",
207 "macro requires argument(s)",
208 "missing list type",
209 "line argument(s) will be lost",
210 "body argument(s) will be lost",
211
212 "generic fatal error",
213
214 "column syntax is inconsistent",
215 "NOT IMPLEMENTED: .Bd -file",
216 "line scope broken, syntax violated",
217 "argument count wrong, violates syntax",
218 "child violates parent syntax",
219 "argument count wrong, violates syntax",
220 "NOT IMPLEMENTED: .so with absolute path or \"..\"",
221 "no document body",
222 "no document prologue",
223 "static buffer exhausted",
224 };
225
226 static void parsebuf(struct curparse *, struct buf, int);
227 static void pdesc(struct curparse *);
228 static void fdesc(struct curparse *);
229 static void ffile(const char *, struct curparse *);
230 static int pfile(const char *, struct curparse *);
231 static int moptions(enum intt *, char *);
232 static int mmsg(enum mandocerr, void *,
233 int, int, const char *);
234 static void pset(const char *, int, struct curparse *);
235 static int toptions(struct curparse *, char *);
236 static void usage(void) __attribute__((noreturn));
237 static void version(void) __attribute__((noreturn));
238 static int woptions(struct curparse *, char *);
239
240 static const char *progname;
241 static enum mandoclevel file_status = MANDOCLEVEL_OK;
242 static enum mandoclevel exit_status = MANDOCLEVEL_OK;
243
244 int
245 main(int argc, char *argv[])
246 {
247 int c;
248 struct curparse curp;
249
250 progname = strrchr(argv[0], '/');
251 if (progname == NULL)
252 progname = argv[0];
253 else
254 ++progname;
255
256 memset(&curp, 0, sizeof(struct curparse));
257
258 curp.inttype = INTT_AUTO;
259 curp.outtype = OUTT_ASCII;
260 curp.wlevel = MANDOCLEVEL_FATAL;
261
262 /* LINTED */
263 while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
264 switch (c) {
265 case ('m'):
266 if ( ! moptions(&curp.inttype, optarg))
267 return((int)MANDOCLEVEL_BADARG);
268 break;
269 case ('O'):
270 (void)strlcat(curp.outopts, optarg, BUFSIZ);
271 (void)strlcat(curp.outopts, ",", BUFSIZ);
272 break;
273 case ('T'):
274 if ( ! toptions(&curp, optarg))
275 return((int)MANDOCLEVEL_BADARG);
276 break;
277 case ('W'):
278 if ( ! woptions(&curp, optarg))
279 return((int)MANDOCLEVEL_BADARG);
280 break;
281 case ('V'):
282 version();
283 /* NOTREACHED */
284 default:
285 usage();
286 /* NOTREACHED */
287 }
288
289 argc -= optind;
290 argv += optind;
291
292 if (NULL == *argv) {
293 curp.file = "<stdin>";
294 curp.fd = STDIN_FILENO;
295
296 fdesc(&curp);
297 }
298
299 while (*argv) {
300 ffile(*argv, &curp);
301 if (MANDOCLEVEL_OK != exit_status && curp.wstop)
302 break;
303 ++argv;
304 }
305
306 if (curp.outfree)
307 (*curp.outfree)(curp.outdata);
308 if (curp.pmdoc)
309 mdoc_free(curp.pmdoc);
310 if (curp.pman)
311 man_free(curp.pman);
312 if (curp.roff)
313 roff_free(curp.roff);
314
315 return((int)exit_status);
316 }
317
318
319 static void
320 version(void)
321 {
322
323 (void)printf("%s %s\n", progname, VERSION);
324 exit((int)MANDOCLEVEL_OK);
325 }
326
327
328 static void
329 usage(void)
330 {
331
332 (void)fprintf(stderr, "usage: %s "
333 "[-V] "
334 "[-foption] "
335 "[-mformat] "
336 "[-Ooption] "
337 "[-Toutput] "
338 "[-Werr] "
339 "[file...]\n",
340 progname);
341
342 exit((int)MANDOCLEVEL_BADARG);
343 }
344
345 static void
346 ffile(const char *file, struct curparse *curp)
347 {
348
349 /*
350 * Called once per input file. Get the file ready for reading,
351 * pass it through to the parser-driver, then close it out.
352 * XXX: don't do anything special as this is only called for
353 * files; stdin goes directly to fdesc().
354 */
355
356 curp->file = file;
357
358 if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
359 perror(curp->file);
360 exit_status = MANDOCLEVEL_SYSERR;
361 return;
362 }
363
364 fdesc(curp);
365
366 if (-1 == close(curp->fd))
367 perror(curp->file);
368 }
369
370 static int
371 pfile(const char *file, struct curparse *curp)
372 {
373 const char *savefile;
374 int fd, savefd;
375
376 if (-1 == (fd = open(file, O_RDONLY, 0))) {
377 perror(file);
378 file_status = MANDOCLEVEL_SYSERR;
379 return(0);
380 }
381
382 savefile = curp->file;
383 savefd = curp->fd;
384
385 curp->file = file;
386 curp->fd = fd;
387
388 pdesc(curp);
389
390 curp->file = savefile;
391 curp->fd = savefd;
392
393 if (-1 == close(fd))
394 perror(file);
395
396 return(MANDOCLEVEL_FATAL > file_status ? 1 : 0);
397 }
398
399
400 static void
401 resize_buf(struct buf *buf, size_t initial)
402 {
403
404 buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
405 buf->buf = realloc(buf->buf, buf->sz);
406 if (NULL == buf->buf) {
407 perror(NULL);
408 exit((int)MANDOCLEVEL_SYSERR);
409 }
410 }
411
412
413 static int
414 read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
415 {
416 struct stat st;
417 size_t off;
418 ssize_t ssz;
419
420 if (-1 == fstat(curp->fd, &st)) {
421 perror(curp->file);
422 return(0);
423 }
424
425 /*
426 * If we're a regular file, try just reading in the whole entry
427 * via mmap(). This is faster than reading it into blocks, and
428 * since each file is only a few bytes to begin with, I'm not
429 * concerned that this is going to tank any machines.
430 */
431
432 if (S_ISREG(st.st_mode)) {
433 if (st.st_size >= (1U << 31)) {
434 fprintf(stderr, "%s: input too large\n",
435 curp->file);
436 return(0);
437 }
438 *with_mmap = 1;
439 fb->sz = (size_t)st.st_size;
440 fb->buf = mmap(NULL, fb->sz, PROT_READ,
441 MAP_FILE|MAP_SHARED, curp->fd, 0);
442 if (fb->buf != MAP_FAILED)
443 return(1);
444 }
445
446 /*
447 * If this isn't a regular file (like, say, stdin), then we must
448 * go the old way and just read things in bit by bit.
449 */
450
451 *with_mmap = 0;
452 off = 0;
453 fb->sz = 0;
454 fb->buf = NULL;
455 for (;;) {
456 if (off == fb->sz) {
457 if (fb->sz == (1U << 31)) {
458 fprintf(stderr, "%s: input too large\n",
459 curp->file);
460 break;
461 }
462 resize_buf(fb, 65536);
463 }
464 ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);
465 if (ssz == 0) {
466 fb->sz = off;
467 return(1);
468 }
469 if (ssz == -1) {
470 perror(curp->file);
471 break;
472 }
473 off += (size_t)ssz;
474 }
475
476 free(fb->buf);
477 fb->buf = NULL;
478 return(0);
479 }
480
481
482 static void
483 fdesc(struct curparse *curp)
484 {
485
486 /*
487 * Called once per file with an opened file descriptor. All
488 * pre-file-parse operations (whether stdin or a file) should go
489 * here.
490 *
491 * This calls down into the nested parser, which drills down and
492 * fully parses a file and all its dependences (i.e., `so'). It
493 * then runs the cleanup validators and pushes to output.
494 */
495
496 /* Zero the parse type. */
497
498 curp->mdoc = NULL;
499 curp->man = NULL;
500 file_status = MANDOCLEVEL_OK;
501
502 /* Make sure the mandotory roff parser is initialised. */
503
504 if (NULL == curp->roff) {
505 curp->roff = roff_alloc(&curp->regs, curp, mmsg);
506 assert(curp->roff);
507 }
508
509 /* Fully parse the file. */
510
511 pdesc(curp);
512
513 if (MANDOCLEVEL_FATAL <= file_status)
514 goto cleanup;
515
516 /* NOTE a parser may not have been assigned, yet. */
517
518 if ( ! (curp->man || curp->mdoc)) {
519 fprintf(stderr, "%s: Not a manual\n", curp->file);
520 file_status = MANDOCLEVEL_FATAL;
521 goto cleanup;
522 }
523
524 /* Clean up the parse routine ASTs. */
525
526 if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
527 assert(MANDOCLEVEL_FATAL <= file_status);
528 goto cleanup;
529 }
530
531 if (curp->man && ! man_endparse(curp->man)) {
532 assert(MANDOCLEVEL_FATAL <= file_status);
533 goto cleanup;
534 }
535
536 assert(curp->roff);
537 roff_endparse(curp->roff);
538
539 /*
540 * With -Wstop and warnings or errors of at least
541 * the requested level, do not produce output.
542 */
543
544 if (MANDOCLEVEL_OK != file_status && curp->wstop)
545 goto cleanup;
546
547 /* If unset, allocate output dev now (if applicable). */
548
549 if ( ! (curp->outman && curp->outmdoc)) {
550 switch (curp->outtype) {
551 case (OUTT_XHTML):
552 curp->outdata = xhtml_alloc(curp->outopts);
553 break;
554 case (OUTT_HTML):
555 curp->outdata = html_alloc(curp->outopts);
556 break;
557 case (OUTT_ASCII):
558 curp->outdata = ascii_alloc(curp->outopts);
559 curp->outfree = ascii_free;
560 break;
561 case (OUTT_PDF):
562 curp->outdata = pdf_alloc(curp->outopts);
563 curp->outfree = pspdf_free;
564 break;
565 case (OUTT_PS):
566 curp->outdata = ps_alloc(curp->outopts);
567 curp->outfree = pspdf_free;
568 break;
569 default:
570 break;
571 }
572
573 switch (curp->outtype) {
574 case (OUTT_HTML):
575 /* FALLTHROUGH */
576 case (OUTT_XHTML):
577 curp->outman = html_man;
578 curp->outmdoc = html_mdoc;
579 curp->outfree = html_free;
580 break;
581 case (OUTT_TREE):
582 curp->outman = tree_man;
583 curp->outmdoc = tree_mdoc;
584 break;
585 case (OUTT_PDF):
586 /* FALLTHROUGH */
587 case (OUTT_ASCII):
588 /* FALLTHROUGH */
589 case (OUTT_PS):
590 curp->outman = terminal_man;
591 curp->outmdoc = terminal_mdoc;
592 break;
593 default:
594 break;
595 }
596 }
597
598 /* Execute the out device, if it exists. */
599
600 if (curp->man && curp->outman)
601 (*curp->outman)(curp->outdata, curp->man);
602 if (curp->mdoc && curp->outmdoc)
603 (*curp->outmdoc)(curp->outdata, curp->mdoc);
604
605 cleanup:
606
607 memset(&curp->regs, 0, sizeof(struct regset));
608
609 /* Reset the current-parse compilers. */
610
611 if (curp->mdoc)
612 mdoc_reset(curp->mdoc);
613 if (curp->man)
614 man_reset(curp->man);
615
616 assert(curp->roff);
617 roff_reset(curp->roff);
618
619 if (exit_status < file_status)
620 exit_status = file_status;
621
622 return;
623 }
624
625 static void
626 pdesc(struct curparse *curp)
627 {
628 struct buf blk;
629 int with_mmap;
630
631 /*
632 * Run for each opened file; may be called more than once for
633 * each full parse sequence if the opened file is nested (i.e.,
634 * from `so'). Simply sucks in the whole file and moves into
635 * the parse phase for the file.
636 */
637
638 if ( ! read_whole_file(curp, &blk, &with_mmap)) {
639 file_status = MANDOCLEVEL_SYSERR;
640 return;
641 }
642
643 /* Line number is per-file. */
644
645 curp->line = 1;
646
647 parsebuf(curp, blk, 1);
648
649 if (with_mmap)
650 munmap(blk.buf, blk.sz);
651 else
652 free(blk.buf);
653 }
654
655 static void
656 parsebuf(struct curparse *curp, struct buf blk, int start)
657 {
658 struct buf ln;
659 enum rofferr rr;
660 int i, of, rc;
661 int pos; /* byte number in the ln buffer */
662 int lnn; /* line number in the real file */
663 unsigned char c;
664
665 /*
666 * Main parse routine for an opened file. This is called for
667 * each opened file and simply loops around the full input file,
668 * possibly nesting (i.e., with `so').
669 */
670
671 memset(&ln, 0, sizeof(struct buf));
672
673 lnn = curp->line;
674 pos = 0;
675
676 for (i = 0; i < (int)blk.sz; ) {
677 if (0 == pos && '\0' == blk.buf[i])
678 break;
679
680 if (start) {
681 curp->line = lnn;
682 curp->reparse_count = 0;
683 }
684
685 while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
686 if ('\n' == blk.buf[i]) {
687 ++i;
688 ++lnn;
689 break;
690 }
691
692 /*
693 * Warn about bogus characters. If you're using
694 * non-ASCII encoding, you're screwing your
695 * readers. Since I'd rather this not happen,
696 * I'll be helpful and drop these characters so
697 * we don't display gibberish. Note to manual
698 * writers: use special characters.
699 */
700
701 c = (unsigned char) blk.buf[i];
702
703 if ( ! (isascii(c) &&
704 (isgraph(c) || isblank(c)))) {
705 mmsg(MANDOCERR_BADCHAR, curp,
706 curp->line, pos, "ignoring byte");
707 i++;
708 continue;
709 }
710
711 /* Trailing backslash = a plain char. */
712
713 if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
714 if (pos >= (int)ln.sz)
715 resize_buf(&ln, 256);
716 ln.buf[pos++] = blk.buf[i++];
717 continue;
718 }
719
720 /* Found escape & at least one other char. */
721
722 if ('\n' == blk.buf[i + 1]) {
723 i += 2;
724 /* Escaped newlines are skipped over */
725 ++lnn;
726 continue;
727 }
728
729 if ('"' == blk.buf[i + 1]) {
730 i += 2;
731 /* Comment, skip to end of line */
732 for (; i < (int)blk.sz; ++i) {
733 if ('\n' == blk.buf[i]) {
734 ++i;
735 ++lnn;
736 break;
737 }
738 }
739
740 /* Backout trailing whitespaces */
741 for (; pos > 0; --pos) {
742 if (ln.buf[pos - 1] != ' ')
743 break;
744 if (pos > 2 && ln.buf[pos - 2] == '\\')
745 break;
746 }
747 break;
748 }
749
750 /* Some other escape sequence, copy & cont. */
751
752 if (pos + 1 >= (int)ln.sz)
753 resize_buf(&ln, 256);
754
755 ln.buf[pos++] = blk.buf[i++];
756 ln.buf[pos++] = blk.buf[i++];
757 }
758
759 if (pos >= (int)ln.sz)
760 resize_buf(&ln, 256);
761
762 ln.buf[pos] = '\0';
763
764 /*
765 * A significant amount of complexity is contained by
766 * the roff preprocessor. It's line-oriented but can be
767 * expressed on one line, so we need at times to
768 * readjust our starting point and re-run it. The roff
769 * preprocessor can also readjust the buffers with new
770 * data, so we pass them in wholesale.
771 */
772
773 of = 0;
774
775 rerun:
776 rr = roff_parseln
777 (curp->roff, curp->line,
778 &ln.buf, &ln.sz, of, &of);
779
780 switch (rr) {
781 case (ROFF_REPARSE):
782 if (REPARSE_LIMIT >= ++curp->reparse_count)
783 parsebuf(curp, ln, 0);
784 else
785 mmsg(MANDOCERR_ROFFLOOP, curp,
786 curp->line, pos, NULL);
787 pos = 0;
788 continue;
789 case (ROFF_APPEND):
790 pos = strlen(ln.buf);
791 continue;
792 case (ROFF_RERUN):
793 goto rerun;
794 case (ROFF_IGN):
795 pos = 0;
796 continue;
797 case (ROFF_ERR):
798 assert(MANDOCLEVEL_FATAL <= file_status);
799 break;
800 case (ROFF_SO):
801 if (pfile(ln.buf + of, curp)) {
802 pos = 0;
803 continue;
804 } else
805 break;
806 default:
807 break;
808 }
809
810 /*
811 * If we encounter errors in the recursive parsebuf()
812 * call, make sure we don't continue parsing.
813 */
814
815 if (MANDOCLEVEL_FATAL <= file_status)
816 break;
817
818 /*
819 * If input parsers have not been allocated, do so now.
820 * We keep these instanced betwen parsers, but set them
821 * locally per parse routine since we can use different
822 * parsers with each one.
823 */
824
825 if ( ! (curp->man || curp->mdoc))
826 pset(ln.buf + of, pos - of, curp);
827
828 /*
829 * Lastly, push down into the parsers themselves. One
830 * of these will have already been set in the pset()
831 * routine.
832 * If libroff returns ROFF_TBL, then add it to the
833 * currently open parse. Since we only get here if
834 * there does exist data (see tbl_data.c), we're
835 * guaranteed that something's been allocated.
836 */
837
838 if (ROFF_TBL == rr) {
839 assert(curp->man || curp->mdoc);
840 if (curp->man)
841 man_addspan(curp->man, roff_span(curp->roff));
842 else
843 mdoc_addspan(curp->mdoc, roff_span(curp->roff));
844
845 } else if (curp->man || curp->mdoc) {
846 rc = curp->man ?
847 man_parseln(curp->man,
848 curp->line, ln.buf, of) :
849 mdoc_parseln(curp->mdoc,
850 curp->line, ln.buf, of);
851
852 if ( ! rc) {
853 assert(MANDOCLEVEL_FATAL <= file_status);
854 break;
855 }
856 }
857
858 /* Temporary buffers typically are not full. */
859
860 if (0 == start && '\0' == blk.buf[i])
861 break;
862
863 /* Start the next input line. */
864
865 pos = 0;
866 }
867
868 free(ln.buf);
869 }
870
871 static void
872 pset(const char *buf, int pos, struct curparse *curp)
873 {
874 int i;
875
876 /*
877 * Try to intuit which kind of manual parser should be used. If
878 * passed in by command-line (-man, -mdoc), then use that
879 * explicitly. If passed as -mandoc, then try to guess from the
880 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
881 * default to -man, which is more lenient.
882 *
883 * Separate out pmdoc/pman from mdoc/man: the first persists
884 * through all parsers, while the latter is used per-parse.
885 */
886
887 if ('.' == buf[0] || '\'' == buf[0]) {
888 for (i = 1; buf[i]; i++)
889 if (' ' != buf[i] && '\t' != buf[i])
890 break;
891 if ('\0' == buf[i])
892 return;
893 }
894
895 switch (curp->inttype) {
896 case (INTT_MDOC):
897 if (NULL == curp->pmdoc)
898 curp->pmdoc = mdoc_alloc
899 (&curp->regs, curp, mmsg);
900 assert(curp->pmdoc);
901 curp->mdoc = curp->pmdoc;
902 return;
903 case (INTT_MAN):
904 if (NULL == curp->pman)
905 curp->pman = man_alloc
906 (&curp->regs, curp, mmsg);
907 assert(curp->pman);
908 curp->man = curp->pman;
909 return;
910 default:
911 break;
912 }
913
914 if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
915 if (NULL == curp->pmdoc)
916 curp->pmdoc = mdoc_alloc
917 (&curp->regs, curp, mmsg);
918 assert(curp->pmdoc);
919 curp->mdoc = curp->pmdoc;
920 return;
921 }
922
923 if (NULL == curp->pman)
924 curp->pman = man_alloc(&curp->regs, curp, mmsg);
925 assert(curp->pman);
926 curp->man = curp->pman;
927 }
928
929 static int
930 moptions(enum intt *tflags, char *arg)
931 {
932
933 if (0 == strcmp(arg, "doc"))
934 *tflags = INTT_MDOC;
935 else if (0 == strcmp(arg, "andoc"))
936 *tflags = INTT_AUTO;
937 else if (0 == strcmp(arg, "an"))
938 *tflags = INTT_MAN;
939 else {
940 fprintf(stderr, "%s: Bad argument\n", arg);
941 return(0);
942 }
943
944 return(1);
945 }
946
947 static int
948 toptions(struct curparse *curp, char *arg)
949 {
950
951 if (0 == strcmp(arg, "ascii"))
952 curp->outtype = OUTT_ASCII;
953 else if (0 == strcmp(arg, "lint")) {
954 curp->outtype = OUTT_LINT;
955 curp->wlevel = MANDOCLEVEL_WARNING;
956 }
957 else if (0 == strcmp(arg, "tree"))
958 curp->outtype = OUTT_TREE;
959 else if (0 == strcmp(arg, "html"))
960 curp->outtype = OUTT_HTML;
961 else if (0 == strcmp(arg, "xhtml"))
962 curp->outtype = OUTT_XHTML;
963 else if (0 == strcmp(arg, "ps"))
964 curp->outtype = OUTT_PS;
965 else if (0 == strcmp(arg, "pdf"))
966 curp->outtype = OUTT_PDF;
967 else {
968 fprintf(stderr, "%s: Bad argument\n", arg);
969 return(0);
970 }
971
972 return(1);
973 }
974
975 static int
976 woptions(struct curparse *curp, char *arg)
977 {
978 char *v, *o;
979 const char *toks[6];
980
981 toks[0] = "stop";
982 toks[1] = "all";
983 toks[2] = "warning";
984 toks[3] = "error";
985 toks[4] = "fatal";
986 toks[5] = NULL;
987
988 while (*arg) {
989 o = arg;
990 switch (getsubopt(&arg, UNCONST(toks), &v)) {
991 case (0):
992 curp->wstop = 1;
993 break;
994 case (1):
995 /* FALLTHROUGH */
996 case (2):
997 curp->wlevel = MANDOCLEVEL_WARNING;
998 break;
999 case (3):
1000 curp->wlevel = MANDOCLEVEL_ERROR;
1001 break;
1002 case (4):
1003 curp->wlevel = MANDOCLEVEL_FATAL;
1004 break;
1005 default:
1006 fprintf(stderr, "-W%s: Bad argument\n", o);
1007 return(0);
1008 }
1009 }
1010
1011 return(1);
1012 }
1013
1014 static int
1015 mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
1016 {
1017 struct curparse *cp;
1018 enum mandoclevel level;
1019
1020 level = MANDOCLEVEL_FATAL;
1021 while (t < mandoclimits[level])
1022 /* LINTED */
1023 level--;
1024
1025 cp = (struct curparse *)arg;
1026 if (level < cp->wlevel)
1027 return(1);
1028
1029 fprintf(stderr, "%s:%d:%d: %s: %s",
1030 cp->file, ln, col + 1, mandoclevels[level], mandocerrs[t]);
1031 if (msg)
1032 fprintf(stderr, ": %s", msg);
1033 fputc('\n', stderr);
1034
1035 if (file_status < level)
1036 file_status = level;
1037
1038 return(level < MANDOCLEVEL_FATAL);
1039 }