]> git.cameronkatri.com Git - mandoc.git/blob - main.c
Explicit block closure macros clobber next-line block head scope,
[mandoc.git] / main.c
1 /* $Id: main.c,v 1.206 2014/12/15 18:05:57 schwarze Exp $ */
2 /*
3 * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19 #include "config.h"
20
21 #include <sys/types.h>
22
23 #include <assert.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdint.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32
33 #include "mandoc.h"
34 #include "mandoc_aux.h"
35 #include "main.h"
36 #include "mdoc.h"
37 #include "man.h"
38 #include "manpath.h"
39 #include "mansearch.h"
40
41 #if !defined(__GNUC__) || (__GNUC__ < 2)
42 # if !defined(lint)
43 # define __attribute__(x)
44 # endif
45 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
46
47 enum outmode {
48 OUTMODE_DEF = 0,
49 OUTMODE_FLN,
50 OUTMODE_LST,
51 OUTMODE_ALL,
52 OUTMODE_INT,
53 OUTMODE_ONE
54 };
55
56 typedef void (*out_mdoc)(void *, const struct mdoc *);
57 typedef void (*out_man)(void *, const struct man *);
58 typedef void (*out_free)(void *);
59
60 enum outt {
61 OUTT_ASCII = 0, /* -Tascii */
62 OUTT_LOCALE, /* -Tlocale */
63 OUTT_UTF8, /* -Tutf8 */
64 OUTT_TREE, /* -Ttree */
65 OUTT_MAN, /* -Tman */
66 OUTT_HTML, /* -Thtml */
67 OUTT_LINT, /* -Tlint */
68 OUTT_PS, /* -Tps */
69 OUTT_PDF /* -Tpdf */
70 };
71
72 struct curparse {
73 struct mparse *mp;
74 struct mchars *mchars; /* character table */
75 enum mandoclevel wlevel; /* ignore messages below this */
76 int wstop; /* stop after a file with a warning */
77 enum outt outtype; /* which output to use */
78 out_mdoc outmdoc; /* mdoc output ptr */
79 out_man outman; /* man output ptr */
80 out_free outfree; /* free output ptr */
81 void *outdata; /* data for output */
82 char outopts[BUFSIZ]; /* buf of output opts */
83 };
84
85 static int koptions(int *, char *);
86 #if HAVE_SQLITE3
87 int mandocdb(int, char**);
88 #endif
89 static int moptions(int *, char *);
90 static void mmsg(enum mandocerr, enum mandoclevel,
91 const char *, int, int, const char *);
92 static void parse(struct curparse *, int,
93 const char *, enum mandoclevel *);
94 #if HAVE_SQLITE3
95 static enum mandoclevel passthrough(const char *, int, int);
96 #endif
97 static void spawn_pager(void);
98 static int toptions(struct curparse *, char *);
99 static void usage(enum argmode) __attribute__((noreturn));
100 static void version(void) __attribute__((noreturn));
101 static int woptions(struct curparse *, char *);
102
103 static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
104 static char help_arg[] = "help";
105 static char *help_argv[] = {help_arg, NULL};
106 static const char *progname;
107
108
109 int
110 main(int argc, char *argv[])
111 {
112 struct curparse curp;
113 struct mansearch search;
114 struct manpaths paths;
115 char *auxpaths;
116 char *defos;
117 #if HAVE_SQLITE3
118 struct manpage *res, *resp;
119 char *conf_file, *defpaths;
120 size_t isec, i, sz;
121 int prio, best_prio, synopsis_only;
122 char sec;
123 #endif
124 enum mandoclevel rc;
125 enum outmode outmode;
126 int fd;
127 int show_usage;
128 int use_pager;
129 int options;
130 int c;
131
132 progname = strrchr(argv[0], '/');
133 if (progname == NULL)
134 progname = argv[0];
135 else
136 ++progname;
137
138 #if HAVE_SQLITE3
139 if (strcmp(progname, BINM_MAKEWHATIS) == 0)
140 return(mandocdb(argc, argv));
141 #endif
142
143 /* Search options. */
144
145 memset(&paths, 0, sizeof(struct manpaths));
146 #if HAVE_SQLITE3
147 conf_file = defpaths = NULL;
148 #endif
149 auxpaths = NULL;
150
151 memset(&search, 0, sizeof(struct mansearch));
152 search.outkey = "Nd";
153
154 if (strcmp(progname, BINM_MAN) == 0)
155 search.argmode = ARG_NAME;
156 else if (strcmp(progname, BINM_APROPOS) == 0)
157 search.argmode = ARG_EXPR;
158 else if (strcmp(progname, BINM_WHATIS) == 0)
159 search.argmode = ARG_WORD;
160 else if (strncmp(progname, "help", 4) == 0)
161 search.argmode = ARG_NAME;
162 else
163 search.argmode = ARG_FILE;
164
165 /* Parser and formatter options. */
166
167 memset(&curp, 0, sizeof(struct curparse));
168 curp.outtype = OUTT_LOCALE;
169 curp.wlevel = MANDOCLEVEL_FATAL;
170 options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1;
171 defos = NULL;
172
173 use_pager = 1;
174 show_usage = 0;
175 #if HAVE_SQLITE3
176 synopsis_only = 0;
177 #endif
178 outmode = OUTMODE_DEF;
179
180 while (-1 != (c = getopt(argc, argv,
181 "aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) {
182 switch (c) {
183 case 'a':
184 outmode = OUTMODE_ALL;
185 break;
186 case 'C':
187 #if HAVE_SQLITE3
188 conf_file = optarg;
189 #endif
190 break;
191 case 'c':
192 use_pager = 0;
193 break;
194 case 'f':
195 search.argmode = ARG_WORD;
196 break;
197 case 'h':
198 (void)strlcat(curp.outopts, "synopsis,", BUFSIZ);
199 #if HAVE_SQLITE3
200 synopsis_only = 1;
201 #endif
202 use_pager = 0;
203 outmode = OUTMODE_ALL;
204 break;
205 case 'I':
206 if (strncmp(optarg, "os=", 3)) {
207 fprintf(stderr,
208 "%s: -I%s: Bad argument\n",
209 progname, optarg);
210 return((int)MANDOCLEVEL_BADARG);
211 }
212 if (defos) {
213 fprintf(stderr,
214 "%s: -I%s: Duplicate argument\n",
215 progname, optarg);
216 return((int)MANDOCLEVEL_BADARG);
217 }
218 defos = mandoc_strdup(optarg + 3);
219 break;
220 case 'i':
221 outmode = OUTMODE_INT;
222 break;
223 case 'K':
224 if ( ! koptions(&options, optarg))
225 return((int)MANDOCLEVEL_BADARG);
226 break;
227 case 'k':
228 search.argmode = ARG_EXPR;
229 break;
230 case 'l':
231 search.argmode = ARG_FILE;
232 outmode = OUTMODE_ALL;
233 break;
234 case 'M':
235 #if HAVE_SQLITE3
236 defpaths = optarg;
237 #endif
238 break;
239 case 'm':
240 auxpaths = optarg;
241 break;
242 case 'O':
243 search.outkey = optarg;
244 (void)strlcat(curp.outopts, optarg, BUFSIZ);
245 (void)strlcat(curp.outopts, ",", BUFSIZ);
246 break;
247 case 'S':
248 search.arch = optarg;
249 break;
250 case 's':
251 search.sec = optarg;
252 break;
253 case 'T':
254 if ( ! toptions(&curp, optarg))
255 return((int)MANDOCLEVEL_BADARG);
256 break;
257 case 'W':
258 if ( ! woptions(&curp, optarg))
259 return((int)MANDOCLEVEL_BADARG);
260 break;
261 case 'w':
262 outmode = OUTMODE_FLN;
263 break;
264 case 'V':
265 version();
266 /* NOTREACHED */
267 default:
268 show_usage = 1;
269 break;
270 }
271 }
272
273 if (show_usage)
274 usage(search.argmode);
275
276 /* Postprocess options. */
277
278 if (outmode == OUTMODE_DEF) {
279 switch (search.argmode) {
280 case ARG_FILE:
281 outmode = OUTMODE_ALL;
282 use_pager = 0;
283 break;
284 case ARG_NAME:
285 outmode = OUTMODE_ONE;
286 break;
287 default:
288 outmode = OUTMODE_LST;
289 break;
290 }
291 }
292
293 /* Parse arguments. */
294
295 argc -= optind;
296 argv += optind;
297 #if HAVE_SQLITE3
298 resp = NULL;
299 #endif
300
301 /*
302 * Quirks for help(1)
303 * and for a man(1) section argument without -s.
304 */
305
306 if (search.argmode == ARG_NAME) {
307 if (*progname == 'h') {
308 if (argc == 0) {
309 argv = help_argv;
310 argc = 1;
311 }
312 } else if (argv[0] != NULL && (
313 (isdigit((unsigned char)argv[0][0]) &&
314 (argv[0][1] == '\0' || !strcmp(argv[0], "3p"))) ||
315 (argv[0][0] == 'n' && argv[0][1] == '\0'))) {
316 search.sec = argv[0];
317 argv++;
318 argc--;
319 }
320 }
321
322 rc = MANDOCLEVEL_OK;
323
324 /* man(1), whatis(1), apropos(1) */
325
326 if (search.argmode != ARG_FILE) {
327 #if HAVE_SQLITE3
328 if (argc == 0)
329 usage(search.argmode);
330
331 if (search.argmode == ARG_NAME &&
332 outmode == OUTMODE_ONE)
333 search.firstmatch = 1;
334
335 /* Access the mandoc database. */
336
337 manpath_parse(&paths, conf_file, defpaths, auxpaths);
338 mansearch_setup(1);
339 if( ! mansearch(&search, &paths, argc, argv, &res, &sz))
340 usage(search.argmode);
341 resp = res;
342
343 if (sz == 0) {
344 if (search.argmode == ARG_NAME)
345 fprintf(stderr, "%s: No entry for %s "
346 "in the manual.\n", progname, argv[0]);
347 rc = MANDOCLEVEL_BADARG;
348 goto out;
349 }
350
351 /*
352 * For standard man(1) and -a output mode,
353 * prepare for copying filename pointers
354 * into the program parameter array.
355 */
356
357 if (outmode == OUTMODE_ONE) {
358 argc = 1;
359 best_prio = 10;
360 } else if (outmode == OUTMODE_ALL)
361 argc = (int)sz;
362
363 /* Iterate all matching manuals. */
364
365 for (i = 0; i < sz; i++) {
366 if (outmode == OUTMODE_FLN)
367 puts(res[i].file);
368 else if (outmode == OUTMODE_LST)
369 printf("%s - %s\n", res[i].names,
370 res[i].output == NULL ? "" :
371 res[i].output);
372 else if (outmode == OUTMODE_ONE) {
373 /* Search for the best section. */
374 isec = strcspn(res[i].file, "123456789");
375 sec = res[i].file[isec];
376 if ('\0' == sec)
377 continue;
378 prio = sec_prios[sec - '1'];
379 if (prio >= best_prio)
380 continue;
381 best_prio = prio;
382 resp = res + i;
383 }
384 }
385
386 /*
387 * For man(1), -a and -i output mode, fall through
388 * to the main mandoc(1) code iterating files
389 * and running the parsers on each of them.
390 */
391
392 if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST)
393 goto out;
394 #else
395 fputs("mandoc: database support not compiled in\n",
396 stderr);
397 return((int)MANDOCLEVEL_BADARG);
398 #endif
399 }
400
401 /* mandoc(1) */
402
403 if ( ! moptions(&options, auxpaths))
404 return((int)MANDOCLEVEL_BADARG);
405
406 if (use_pager && isatty(STDOUT_FILENO))
407 spawn_pager();
408
409 curp.mchars = mchars_alloc();
410 curp.mp = mparse_alloc(options, curp.wlevel, mmsg,
411 curp.mchars, defos);
412
413 /*
414 * Conditionally start up the lookaside buffer before parsing.
415 */
416 if (OUTT_MAN == curp.outtype)
417 mparse_keep(curp.mp);
418
419 if (argc == 0)
420 parse(&curp, STDIN_FILENO, "<stdin>", &rc);
421
422 while (argc) {
423 #if HAVE_SQLITE3
424 if (resp != NULL) {
425 rc = mparse_open(curp.mp, &fd, resp->file);
426 if (fd == -1)
427 /* nothing */;
428 else if (resp->form & FORM_SRC) {
429 /* For .so only; ignore failure. */
430 chdir(paths.paths[resp->ipath]);
431 parse(&curp, fd, resp->file, &rc);
432 } else
433 rc = passthrough(resp->file, fd,
434 synopsis_only);
435 resp++;
436 } else
437 #endif
438 {
439 rc = mparse_open(curp.mp, &fd, *argv++);
440 if (fd != -1)
441 parse(&curp, fd, argv[-1], &rc);
442 }
443
444 if (mparse_wait(curp.mp) != MANDOCLEVEL_OK)
445 rc = MANDOCLEVEL_SYSERR;
446
447 if (MANDOCLEVEL_OK != rc && curp.wstop)
448 break;
449 argc--;
450 }
451
452 if (curp.outfree)
453 (*curp.outfree)(curp.outdata);
454 mparse_free(curp.mp);
455 mchars_free(curp.mchars);
456
457 #if HAVE_SQLITE3
458 out:
459 if (search.argmode != ARG_FILE) {
460 manpath_free(&paths);
461 mansearch_free(res, sz);
462 mansearch_setup(0);
463 }
464 #endif
465
466 free(defos);
467
468 return((int)rc);
469 }
470
471 static void
472 version(void)
473 {
474
475 printf("mandoc %s\n", VERSION);
476 exit((int)MANDOCLEVEL_OK);
477 }
478
479 static void
480 usage(enum argmode argmode)
481 {
482
483 switch (argmode) {
484 case ARG_FILE:
485 fputs("usage: mandoc [-acfhklV] [-Ios=name] "
486 "[-Kencoding] [-mformat] [-Ooption]\n"
487 "\t [-Toutput] [-Wlevel] [file ...]\n", stderr);
488 break;
489 case ARG_NAME:
490 fputs("usage: man [-acfhklVw] [-C file] "
491 "[-M path] [-m path] [-S arch] [-s section]\n"
492 "\t [section] name ...\n", stderr);
493 break;
494 case ARG_WORD:
495 fputs("usage: whatis [-acfhklVw] [-C file] "
496 "[-M path] [-m path] [-O outkey] [-S arch]\n"
497 "\t [-s section] name ...\n", stderr);
498 break;
499 case ARG_EXPR:
500 fputs("usage: apropos [-acfhklVw] [-C file] "
501 "[-M path] [-m path] [-O outkey] [-S arch]\n"
502 "\t [-s section] expression ...\n", stderr);
503 break;
504 }
505 exit((int)MANDOCLEVEL_BADARG);
506 }
507
508 static void
509 parse(struct curparse *curp, int fd, const char *file,
510 enum mandoclevel *level)
511 {
512 enum mandoclevel rc;
513 struct mdoc *mdoc;
514 struct man *man;
515
516 /* Begin by parsing the file itself. */
517
518 assert(file);
519 assert(fd >= -1);
520
521 rc = mparse_readfd(curp->mp, fd, file);
522
523 /* Stop immediately if the parse has failed. */
524
525 if (MANDOCLEVEL_FATAL <= rc)
526 goto cleanup;
527
528 /*
529 * With -Wstop and warnings or errors of at least the requested
530 * level, do not produce output.
531 */
532
533 if (MANDOCLEVEL_OK != rc && curp->wstop)
534 goto cleanup;
535
536 /* If unset, allocate output dev now (if applicable). */
537
538 if ( ! (curp->outman && curp->outmdoc)) {
539 switch (curp->outtype) {
540 case OUTT_HTML:
541 curp->outdata = html_alloc(curp->mchars,
542 curp->outopts);
543 curp->outfree = html_free;
544 break;
545 case OUTT_UTF8:
546 curp->outdata = utf8_alloc(curp->mchars,
547 curp->outopts);
548 curp->outfree = ascii_free;
549 break;
550 case OUTT_LOCALE:
551 curp->outdata = locale_alloc(curp->mchars,
552 curp->outopts);
553 curp->outfree = ascii_free;
554 break;
555 case OUTT_ASCII:
556 curp->outdata = ascii_alloc(curp->mchars,
557 curp->outopts);
558 curp->outfree = ascii_free;
559 break;
560 case OUTT_PDF:
561 curp->outdata = pdf_alloc(curp->mchars,
562 curp->outopts);
563 curp->outfree = pspdf_free;
564 break;
565 case OUTT_PS:
566 curp->outdata = ps_alloc(curp->mchars,
567 curp->outopts);
568 curp->outfree = pspdf_free;
569 break;
570 default:
571 break;
572 }
573
574 switch (curp->outtype) {
575 case OUTT_HTML:
576 curp->outman = html_man;
577 curp->outmdoc = html_mdoc;
578 break;
579 case OUTT_TREE:
580 curp->outman = tree_man;
581 curp->outmdoc = tree_mdoc;
582 break;
583 case OUTT_MAN:
584 curp->outmdoc = man_mdoc;
585 curp->outman = man_man;
586 break;
587 case OUTT_PDF:
588 /* FALLTHROUGH */
589 case OUTT_ASCII:
590 /* FALLTHROUGH */
591 case OUTT_UTF8:
592 /* FALLTHROUGH */
593 case OUTT_LOCALE:
594 /* FALLTHROUGH */
595 case OUTT_PS:
596 curp->outman = terminal_man;
597 curp->outmdoc = terminal_mdoc;
598 break;
599 default:
600 break;
601 }
602 }
603
604 mparse_result(curp->mp, &mdoc, &man, NULL);
605
606 /* Execute the out device, if it exists. */
607
608 if (man && curp->outman)
609 (*curp->outman)(curp->outdata, man);
610 if (mdoc && curp->outmdoc)
611 (*curp->outmdoc)(curp->outdata, mdoc);
612
613 cleanup:
614
615 mparse_reset(curp->mp);
616
617 if (*level < rc)
618 *level = rc;
619 }
620
621 #if HAVE_SQLITE3
622 static enum mandoclevel
623 passthrough(const char *file, int fd, int synopsis_only)
624 {
625 const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS";
626 const char synr[] = "SYNOPSIS";
627
628 FILE *stream;
629 const char *syscall;
630 char *line;
631 size_t len, off;
632 ssize_t nw;
633 int print;
634
635 if ((stream = fdopen(fd, "r")) == NULL) {
636 close(fd);
637 syscall = "fdopen";
638 goto fail;
639 }
640
641 print = 0;
642 while ((line = fgetln(stream, &len)) != NULL) {
643 if (synopsis_only) {
644 if (print) {
645 if ( ! isspace((unsigned char)*line))
646 goto done;
647 while (len &&
648 isspace((unsigned char)*line)) {
649 line++;
650 len--;
651 }
652 } else {
653 if ((len == sizeof(synb) &&
654 ! strncmp(line, synb, len - 1)) ||
655 (len == sizeof(synr) &&
656 ! strncmp(line, synr, len - 1)))
657 print = 1;
658 continue;
659 }
660 }
661 for (off = 0; off < len; off += nw)
662 if ((nw = write(STDOUT_FILENO, line + off,
663 len - off)) == -1 || nw == 0) {
664 fclose(stream);
665 syscall = "write";
666 goto fail;
667 }
668 }
669
670 if (ferror(stream)) {
671 fclose(stream);
672 syscall = "fgetln";
673 goto fail;
674 }
675
676 done:
677 fclose(stream);
678 return(MANDOCLEVEL_OK);
679
680 fail:
681 fprintf(stderr, "%s: %s: SYSERR: %s: %s",
682 progname, file, syscall, strerror(errno));
683 return(MANDOCLEVEL_SYSERR);
684 }
685 #endif
686
687 static int
688 koptions(int *options, char *arg)
689 {
690
691 if ( ! strcmp(arg, "utf-8")) {
692 *options |= MPARSE_UTF8;
693 *options &= ~MPARSE_LATIN1;
694 } else if ( ! strcmp(arg, "iso-8859-1")) {
695 *options |= MPARSE_LATIN1;
696 *options &= ~MPARSE_UTF8;
697 } else if ( ! strcmp(arg, "us-ascii")) {
698 *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1);
699 } else {
700 fprintf(stderr, "%s: -K%s: Bad argument\n",
701 progname, arg);
702 return(0);
703 }
704 return(1);
705 }
706
707 static int
708 moptions(int *options, char *arg)
709 {
710
711 if (arg == NULL)
712 /* nothing to do */;
713 else if (0 == strcmp(arg, "doc"))
714 *options |= MPARSE_MDOC;
715 else if (0 == strcmp(arg, "andoc"))
716 /* nothing to do */;
717 else if (0 == strcmp(arg, "an"))
718 *options |= MPARSE_MAN;
719 else {
720 fprintf(stderr, "%s: -m%s: Bad argument\n",
721 progname, arg);
722 return(0);
723 }
724
725 return(1);
726 }
727
728 static int
729 toptions(struct curparse *curp, char *arg)
730 {
731
732 if (0 == strcmp(arg, "ascii"))
733 curp->outtype = OUTT_ASCII;
734 else if (0 == strcmp(arg, "lint")) {
735 curp->outtype = OUTT_LINT;
736 curp->wlevel = MANDOCLEVEL_WARNING;
737 } else if (0 == strcmp(arg, "tree"))
738 curp->outtype = OUTT_TREE;
739 else if (0 == strcmp(arg, "man"))
740 curp->outtype = OUTT_MAN;
741 else if (0 == strcmp(arg, "html"))
742 curp->outtype = OUTT_HTML;
743 else if (0 == strcmp(arg, "utf8"))
744 curp->outtype = OUTT_UTF8;
745 else if (0 == strcmp(arg, "locale"))
746 curp->outtype = OUTT_LOCALE;
747 else if (0 == strcmp(arg, "xhtml"))
748 curp->outtype = OUTT_HTML;
749 else if (0 == strcmp(arg, "ps"))
750 curp->outtype = OUTT_PS;
751 else if (0 == strcmp(arg, "pdf"))
752 curp->outtype = OUTT_PDF;
753 else {
754 fprintf(stderr, "%s: -T%s: Bad argument\n",
755 progname, arg);
756 return(0);
757 }
758
759 return(1);
760 }
761
762 static int
763 woptions(struct curparse *curp, char *arg)
764 {
765 char *v, *o;
766 const char *toks[6];
767
768 toks[0] = "stop";
769 toks[1] = "all";
770 toks[2] = "warning";
771 toks[3] = "error";
772 toks[4] = "fatal";
773 toks[5] = NULL;
774
775 while (*arg) {
776 o = arg;
777 switch (getsubopt(&arg, UNCONST(toks), &v)) {
778 case 0:
779 curp->wstop = 1;
780 break;
781 case 1:
782 /* FALLTHROUGH */
783 case 2:
784 curp->wlevel = MANDOCLEVEL_WARNING;
785 break;
786 case 3:
787 curp->wlevel = MANDOCLEVEL_ERROR;
788 break;
789 case 4:
790 curp->wlevel = MANDOCLEVEL_FATAL;
791 break;
792 default:
793 fprintf(stderr, "%s: -W%s: Bad argument\n",
794 progname, o);
795 return(0);
796 }
797 }
798
799 return(1);
800 }
801
802 static void
803 mmsg(enum mandocerr t, enum mandoclevel lvl,
804 const char *file, int line, int col, const char *msg)
805 {
806 const char *mparse_msg;
807
808 fprintf(stderr, "%s: %s:", progname, file);
809
810 if (line)
811 fprintf(stderr, "%d:%d:", line, col + 1);
812
813 fprintf(stderr, " %s", mparse_strlevel(lvl));
814
815 if (NULL != (mparse_msg = mparse_strerror(t)))
816 fprintf(stderr, ": %s", mparse_msg);
817
818 if (msg)
819 fprintf(stderr, ": %s", msg);
820
821 fputc('\n', stderr);
822 }
823
824 static void
825 spawn_pager(void)
826 {
827 #define MAX_PAGER_ARGS 16
828 char *argv[MAX_PAGER_ARGS];
829 const char *pager;
830 char *cp;
831 int fildes[2];
832 int argc;
833
834 if (pipe(fildes) == -1) {
835 fprintf(stderr, "%s: pipe: %s\n",
836 progname, strerror(errno));
837 return;
838 }
839
840 switch (fork()) {
841 case -1:
842 fprintf(stderr, "%s: fork: %s\n",
843 progname, strerror(errno));
844 exit((int)MANDOCLEVEL_SYSERR);
845 case 0:
846 close(fildes[0]);
847 if (dup2(fildes[1], STDOUT_FILENO) == -1) {
848 fprintf(stderr, "%s: dup output: %s\n",
849 progname, strerror(errno));
850 exit((int)MANDOCLEVEL_SYSERR);
851 }
852 return;
853 default:
854 break;
855 }
856
857 /* The original process becomes the pager. */
858
859 close(fildes[1]);
860 if (dup2(fildes[0], STDIN_FILENO) == -1) {
861 fprintf(stderr, "%s: dup input: %s\n",
862 progname, strerror(errno));
863 exit((int)MANDOCLEVEL_SYSERR);
864 }
865
866 pager = getenv("MANPAGER");
867 if (pager == NULL || *pager == '\0')
868 pager = getenv("PAGER");
869 if (pager == NULL || *pager == '\0')
870 pager = "/usr/bin/more -s";
871 cp = mandoc_strdup(pager);
872
873 /*
874 * Parse the pager command into words.
875 * Intentionally do not do anything fancy here.
876 */
877
878 argc = 0;
879 while (argc + 1 < MAX_PAGER_ARGS) {
880 argv[argc++] = cp;
881 cp = strchr(cp, ' ');
882 if (cp == NULL)
883 break;
884 *cp++ = '\0';
885 while (*cp == ' ')
886 cp++;
887 if (*cp == '\0')
888 break;
889 }
890 argv[argc] = NULL;
891
892 /* Hand over to the pager. */
893
894 execvp(argv[0], argv);
895 fprintf(stderr, "%s: exec: %s\n",
896 progname, strerror(errno));
897 exit((int)MANDOCLEVEL_SYSERR);
898 }