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