]> git.cameronkatri.com Git - mandoc.git/blob - main.c
Guard some come needed for database support only by #if HAVE_SQLITE3.
[mandoc.git] / main.c
1 /* $Id: main.c,v 1.205 2014/12/11 19:19:35 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 search.sec = argv[0];
316 argv++;
317 argc--;
318 }
319 }
320
321 rc = MANDOCLEVEL_OK;
322
323 /* man(1), whatis(1), apropos(1) */
324
325 if (search.argmode != ARG_FILE) {
326 #if HAVE_SQLITE3
327 if (argc == 0)
328 usage(search.argmode);
329
330 if (search.argmode == ARG_NAME &&
331 outmode == OUTMODE_ONE)
332 search.firstmatch = 1;
333
334 /* Access the mandoc database. */
335
336 manpath_parse(&paths, conf_file, defpaths, auxpaths);
337 mansearch_setup(1);
338 if( ! mansearch(&search, &paths, argc, argv, &res, &sz))
339 usage(search.argmode);
340 resp = res;
341
342 if (sz == 0) {
343 if (search.argmode == ARG_NAME)
344 fprintf(stderr, "%s: No entry for %s "
345 "in the manual.\n", progname, argv[0]);
346 rc = MANDOCLEVEL_BADARG;
347 goto out;
348 }
349
350 /*
351 * For standard man(1) and -a output mode,
352 * prepare for copying filename pointers
353 * into the program parameter array.
354 */
355
356 if (outmode == OUTMODE_ONE) {
357 argc = 1;
358 best_prio = 10;
359 } else if (outmode == OUTMODE_ALL)
360 argc = (int)sz;
361
362 /* Iterate all matching manuals. */
363
364 for (i = 0; i < sz; i++) {
365 if (outmode == OUTMODE_FLN)
366 puts(res[i].file);
367 else if (outmode == OUTMODE_LST)
368 printf("%s - %s\n", res[i].names,
369 res[i].output == NULL ? "" :
370 res[i].output);
371 else if (outmode == OUTMODE_ONE) {
372 /* Search for the best section. */
373 isec = strcspn(res[i].file, "123456789");
374 sec = res[i].file[isec];
375 if ('\0' == sec)
376 continue;
377 prio = sec_prios[sec - '1'];
378 if (prio >= best_prio)
379 continue;
380 best_prio = prio;
381 resp = res + i;
382 }
383 }
384
385 /*
386 * For man(1), -a and -i output mode, fall through
387 * to the main mandoc(1) code iterating files
388 * and running the parsers on each of them.
389 */
390
391 if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST)
392 goto out;
393 #else
394 fputs("mandoc: database support not compiled in\n",
395 stderr);
396 return((int)MANDOCLEVEL_BADARG);
397 #endif
398 }
399
400 /* mandoc(1) */
401
402 if ( ! moptions(&options, auxpaths))
403 return((int)MANDOCLEVEL_BADARG);
404
405 if (use_pager && isatty(STDOUT_FILENO))
406 spawn_pager();
407
408 curp.mchars = mchars_alloc();
409 curp.mp = mparse_alloc(options, curp.wlevel, mmsg,
410 curp.mchars, defos);
411
412 /*
413 * Conditionally start up the lookaside buffer before parsing.
414 */
415 if (OUTT_MAN == curp.outtype)
416 mparse_keep(curp.mp);
417
418 if (argc == 0)
419 parse(&curp, STDIN_FILENO, "<stdin>", &rc);
420
421 while (argc) {
422 #if HAVE_SQLITE3
423 if (resp != NULL) {
424 rc = mparse_open(curp.mp, &fd, resp->file);
425 if (fd == -1)
426 /* nothing */;
427 else if (resp->form & FORM_SRC) {
428 /* For .so only; ignore failure. */
429 chdir(paths.paths[resp->ipath]);
430 parse(&curp, fd, resp->file, &rc);
431 } else
432 rc = passthrough(resp->file, fd,
433 synopsis_only);
434 resp++;
435 } else
436 #endif
437 {
438 rc = mparse_open(curp.mp, &fd, *argv++);
439 if (fd != -1)
440 parse(&curp, fd, argv[-1], &rc);
441 }
442
443 if (mparse_wait(curp.mp) != MANDOCLEVEL_OK)
444 rc = MANDOCLEVEL_SYSERR;
445
446 if (MANDOCLEVEL_OK != rc && curp.wstop)
447 break;
448 argc--;
449 }
450
451 if (curp.outfree)
452 (*curp.outfree)(curp.outdata);
453 mparse_free(curp.mp);
454 mchars_free(curp.mchars);
455
456 #if HAVE_SQLITE3
457 out:
458 if (search.argmode != ARG_FILE) {
459 manpath_free(&paths);
460 mansearch_free(res, sz);
461 mansearch_setup(0);
462 }
463 #endif
464
465 free(defos);
466
467 return((int)rc);
468 }
469
470 static void
471 version(void)
472 {
473
474 printf("mandoc %s\n", VERSION);
475 exit((int)MANDOCLEVEL_OK);
476 }
477
478 static void
479 usage(enum argmode argmode)
480 {
481
482 switch (argmode) {
483 case ARG_FILE:
484 fputs("usage: mandoc [-acfhklV] [-Ios=name] "
485 "[-Kencoding] [-mformat] [-Ooption]\n"
486 "\t [-Toutput] [-Wlevel] [file ...]\n", stderr);
487 break;
488 case ARG_NAME:
489 fputs("usage: man [-acfhklVw] [-C file] "
490 "[-M path] [-m path] [-S arch] [-s section]\n"
491 "\t [section] name ...\n", stderr);
492 break;
493 case ARG_WORD:
494 fputs("usage: whatis [-acfhklVw] [-C file] "
495 "[-M path] [-m path] [-O outkey] [-S arch]\n"
496 "\t [-s section] name ...\n", stderr);
497 break;
498 case ARG_EXPR:
499 fputs("usage: apropos [-acfhklVw] [-C file] "
500 "[-M path] [-m path] [-O outkey] [-S arch]\n"
501 "\t [-s section] expression ...\n", stderr);
502 break;
503 }
504 exit((int)MANDOCLEVEL_BADARG);
505 }
506
507 static void
508 parse(struct curparse *curp, int fd, const char *file,
509 enum mandoclevel *level)
510 {
511 enum mandoclevel rc;
512 struct mdoc *mdoc;
513 struct man *man;
514
515 /* Begin by parsing the file itself. */
516
517 assert(file);
518 assert(fd >= -1);
519
520 rc = mparse_readfd(curp->mp, fd, file);
521
522 /* Stop immediately if the parse has failed. */
523
524 if (MANDOCLEVEL_FATAL <= rc)
525 goto cleanup;
526
527 /*
528 * With -Wstop and warnings or errors of at least the requested
529 * level, do not produce output.
530 */
531
532 if (MANDOCLEVEL_OK != rc && curp->wstop)
533 goto cleanup;
534
535 /* If unset, allocate output dev now (if applicable). */
536
537 if ( ! (curp->outman && curp->outmdoc)) {
538 switch (curp->outtype) {
539 case OUTT_HTML:
540 curp->outdata = html_alloc(curp->mchars,
541 curp->outopts);
542 curp->outfree = html_free;
543 break;
544 case OUTT_UTF8:
545 curp->outdata = utf8_alloc(curp->mchars,
546 curp->outopts);
547 curp->outfree = ascii_free;
548 break;
549 case OUTT_LOCALE:
550 curp->outdata = locale_alloc(curp->mchars,
551 curp->outopts);
552 curp->outfree = ascii_free;
553 break;
554 case OUTT_ASCII:
555 curp->outdata = ascii_alloc(curp->mchars,
556 curp->outopts);
557 curp->outfree = ascii_free;
558 break;
559 case OUTT_PDF:
560 curp->outdata = pdf_alloc(curp->mchars,
561 curp->outopts);
562 curp->outfree = pspdf_free;
563 break;
564 case OUTT_PS:
565 curp->outdata = ps_alloc(curp->mchars,
566 curp->outopts);
567 curp->outfree = pspdf_free;
568 break;
569 default:
570 break;
571 }
572
573 switch (curp->outtype) {
574 case OUTT_HTML:
575 curp->outman = html_man;
576 curp->outmdoc = html_mdoc;
577 break;
578 case OUTT_TREE:
579 curp->outman = tree_man;
580 curp->outmdoc = tree_mdoc;
581 break;
582 case OUTT_MAN:
583 curp->outmdoc = man_mdoc;
584 curp->outman = man_man;
585 break;
586 case OUTT_PDF:
587 /* FALLTHROUGH */
588 case OUTT_ASCII:
589 /* FALLTHROUGH */
590 case OUTT_UTF8:
591 /* FALLTHROUGH */
592 case OUTT_LOCALE:
593 /* FALLTHROUGH */
594 case OUTT_PS:
595 curp->outman = terminal_man;
596 curp->outmdoc = terminal_mdoc;
597 break;
598 default:
599 break;
600 }
601 }
602
603 mparse_result(curp->mp, &mdoc, &man, NULL);
604
605 /* Execute the out device, if it exists. */
606
607 if (man && curp->outman)
608 (*curp->outman)(curp->outdata, man);
609 if (mdoc && curp->outmdoc)
610 (*curp->outmdoc)(curp->outdata, mdoc);
611
612 cleanup:
613
614 mparse_reset(curp->mp);
615
616 if (*level < rc)
617 *level = rc;
618 }
619
620 #if HAVE_SQLITE3
621 static enum mandoclevel
622 passthrough(const char *file, int fd, int synopsis_only)
623 {
624 const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS";
625 const char synr[] = "SYNOPSIS";
626
627 FILE *stream;
628 const char *syscall;
629 char *line;
630 size_t len, off;
631 ssize_t nw;
632 int print;
633
634 if ((stream = fdopen(fd, "r")) == NULL) {
635 close(fd);
636 syscall = "fdopen";
637 goto fail;
638 }
639
640 print = 0;
641 while ((line = fgetln(stream, &len)) != NULL) {
642 if (synopsis_only) {
643 if (print) {
644 if ( ! isspace((unsigned char)*line))
645 goto done;
646 while (len &&
647 isspace((unsigned char)*line)) {
648 line++;
649 len--;
650 }
651 } else {
652 if ((len == sizeof(synb) &&
653 ! strncmp(line, synb, len - 1)) ||
654 (len == sizeof(synr) &&
655 ! strncmp(line, synr, len - 1)))
656 print = 1;
657 continue;
658 }
659 }
660 for (off = 0; off < len; off += nw)
661 if ((nw = write(STDOUT_FILENO, line + off,
662 len - off)) == -1 || nw == 0) {
663 fclose(stream);
664 syscall = "write";
665 goto fail;
666 }
667 }
668
669 if (ferror(stream)) {
670 fclose(stream);
671 syscall = "fgetln";
672 goto fail;
673 }
674
675 done:
676 fclose(stream);
677 return(MANDOCLEVEL_OK);
678
679 fail:
680 fprintf(stderr, "%s: %s: SYSERR: %s: %s",
681 progname, file, syscall, strerror(errno));
682 return(MANDOCLEVEL_SYSERR);
683 }
684 #endif
685
686 static int
687 koptions(int *options, char *arg)
688 {
689
690 if ( ! strcmp(arg, "utf-8")) {
691 *options |= MPARSE_UTF8;
692 *options &= ~MPARSE_LATIN1;
693 } else if ( ! strcmp(arg, "iso-8859-1")) {
694 *options |= MPARSE_LATIN1;
695 *options &= ~MPARSE_UTF8;
696 } else if ( ! strcmp(arg, "us-ascii")) {
697 *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1);
698 } else {
699 fprintf(stderr, "%s: -K%s: Bad argument\n",
700 progname, arg);
701 return(0);
702 }
703 return(1);
704 }
705
706 static int
707 moptions(int *options, char *arg)
708 {
709
710 if (arg == NULL)
711 /* nothing to do */;
712 else if (0 == strcmp(arg, "doc"))
713 *options |= MPARSE_MDOC;
714 else if (0 == strcmp(arg, "andoc"))
715 /* nothing to do */;
716 else if (0 == strcmp(arg, "an"))
717 *options |= MPARSE_MAN;
718 else {
719 fprintf(stderr, "%s: -m%s: Bad argument\n",
720 progname, arg);
721 return(0);
722 }
723
724 return(1);
725 }
726
727 static int
728 toptions(struct curparse *curp, char *arg)
729 {
730
731 if (0 == strcmp(arg, "ascii"))
732 curp->outtype = OUTT_ASCII;
733 else if (0 == strcmp(arg, "lint")) {
734 curp->outtype = OUTT_LINT;
735 curp->wlevel = MANDOCLEVEL_WARNING;
736 } else if (0 == strcmp(arg, "tree"))
737 curp->outtype = OUTT_TREE;
738 else if (0 == strcmp(arg, "man"))
739 curp->outtype = OUTT_MAN;
740 else if (0 == strcmp(arg, "html"))
741 curp->outtype = OUTT_HTML;
742 else if (0 == strcmp(arg, "utf8"))
743 curp->outtype = OUTT_UTF8;
744 else if (0 == strcmp(arg, "locale"))
745 curp->outtype = OUTT_LOCALE;
746 else if (0 == strcmp(arg, "xhtml"))
747 curp->outtype = OUTT_HTML;
748 else if (0 == strcmp(arg, "ps"))
749 curp->outtype = OUTT_PS;
750 else if (0 == strcmp(arg, "pdf"))
751 curp->outtype = OUTT_PDF;
752 else {
753 fprintf(stderr, "%s: -T%s: Bad argument\n",
754 progname, arg);
755 return(0);
756 }
757
758 return(1);
759 }
760
761 static int
762 woptions(struct curparse *curp, char *arg)
763 {
764 char *v, *o;
765 const char *toks[6];
766
767 toks[0] = "stop";
768 toks[1] = "all";
769 toks[2] = "warning";
770 toks[3] = "error";
771 toks[4] = "fatal";
772 toks[5] = NULL;
773
774 while (*arg) {
775 o = arg;
776 switch (getsubopt(&arg, UNCONST(toks), &v)) {
777 case 0:
778 curp->wstop = 1;
779 break;
780 case 1:
781 /* FALLTHROUGH */
782 case 2:
783 curp->wlevel = MANDOCLEVEL_WARNING;
784 break;
785 case 3:
786 curp->wlevel = MANDOCLEVEL_ERROR;
787 break;
788 case 4:
789 curp->wlevel = MANDOCLEVEL_FATAL;
790 break;
791 default:
792 fprintf(stderr, "%s: -W%s: Bad argument\n",
793 progname, o);
794 return(0);
795 }
796 }
797
798 return(1);
799 }
800
801 static void
802 mmsg(enum mandocerr t, enum mandoclevel lvl,
803 const char *file, int line, int col, const char *msg)
804 {
805 const char *mparse_msg;
806
807 fprintf(stderr, "%s: %s:", progname, file);
808
809 if (line)
810 fprintf(stderr, "%d:%d:", line, col + 1);
811
812 fprintf(stderr, " %s", mparse_strlevel(lvl));
813
814 if (NULL != (mparse_msg = mparse_strerror(t)))
815 fprintf(stderr, ": %s", mparse_msg);
816
817 if (msg)
818 fprintf(stderr, ": %s", msg);
819
820 fputc('\n', stderr);
821 }
822
823 static void
824 spawn_pager(void)
825 {
826 #define MAX_PAGER_ARGS 16
827 char *argv[MAX_PAGER_ARGS];
828 const char *pager;
829 char *cp;
830 int fildes[2];
831 int argc;
832
833 if (pipe(fildes) == -1) {
834 fprintf(stderr, "%s: pipe: %s\n",
835 progname, strerror(errno));
836 return;
837 }
838
839 switch (fork()) {
840 case -1:
841 fprintf(stderr, "%s: fork: %s\n",
842 progname, strerror(errno));
843 exit((int)MANDOCLEVEL_SYSERR);
844 case 0:
845 close(fildes[0]);
846 if (dup2(fildes[1], STDOUT_FILENO) == -1) {
847 fprintf(stderr, "%s: dup output: %s\n",
848 progname, strerror(errno));
849 exit((int)MANDOCLEVEL_SYSERR);
850 }
851 return;
852 default:
853 break;
854 }
855
856 /* The original process becomes the pager. */
857
858 close(fildes[1]);
859 if (dup2(fildes[0], STDIN_FILENO) == -1) {
860 fprintf(stderr, "%s: dup input: %s\n",
861 progname, strerror(errno));
862 exit((int)MANDOCLEVEL_SYSERR);
863 }
864
865 pager = getenv("MANPAGER");
866 if (pager == NULL || *pager == '\0')
867 pager = getenv("PAGER");
868 if (pager == NULL || *pager == '\0')
869 pager = "/usr/bin/more -s";
870 cp = mandoc_strdup(pager);
871
872 /*
873 * Parse the pager command into words.
874 * Intentionally do not do anything fancy here.
875 */
876
877 argc = 0;
878 while (argc + 1 < MAX_PAGER_ARGS) {
879 argv[argc++] = cp;
880 cp = strchr(cp, ' ');
881 if (cp == NULL)
882 break;
883 *cp++ = '\0';
884 while (*cp == ' ')
885 cp++;
886 if (*cp == '\0')
887 break;
888 }
889 argv[argc] = NULL;
890
891 /* Hand over to the pager. */
892
893 execvp(argv[0], argv);
894 fprintf(stderr, "%s: exec: %s\n",
895 progname, strerror(errno));
896 exit((int)MANDOCLEVEL_SYSERR);
897 }