xref: /illumos-gate/usr/src/cmd/mandoc/main.c (revision 260e9a87725c090ba5835b1f9f0b62fa2f96036f)
1*260e9a87SYuri Pankov /*	$Id: main.c,v 1.225 2015/03/10 13:50:03 schwarze Exp $ */
295c635efSGarrett D'Amore /*
3*260e9a87SYuri Pankov  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4*260e9a87SYuri Pankov  * Copyright (c) 2010-2012, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
5*260e9a87SYuri Pankov  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
695c635efSGarrett D'Amore  *
795c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
895c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
995c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
1095c635efSGarrett D'Amore  *
1195c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1295c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1395c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1495c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1595c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1695c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1795c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1895c635efSGarrett D'Amore  */
1995c635efSGarrett D'Amore #include "config.h"
20*260e9a87SYuri Pankov 
21*260e9a87SYuri Pankov #include <sys/types.h>
22*260e9a87SYuri Pankov #include <sys/param.h>	/* MACHINE */
23*260e9a87SYuri Pankov #include <sys/wait.h>
2495c635efSGarrett D'Amore 
2595c635efSGarrett D'Amore #include <assert.h>
26*260e9a87SYuri Pankov #include <ctype.h>
27*260e9a87SYuri Pankov #include <errno.h>
28*260e9a87SYuri Pankov #include <fcntl.h>
29*260e9a87SYuri Pankov #include <glob.h>
3095c635efSGarrett D'Amore #include <stdio.h>
3195c635efSGarrett D'Amore #include <stdint.h>
3295c635efSGarrett D'Amore #include <stdlib.h>
3395c635efSGarrett D'Amore #include <string.h>
3495c635efSGarrett D'Amore #include <unistd.h>
3595c635efSGarrett D'Amore 
3695c635efSGarrett D'Amore #include "mandoc.h"
37*260e9a87SYuri Pankov #include "mandoc_aux.h"
3895c635efSGarrett D'Amore #include "main.h"
3995c635efSGarrett D'Amore #include "mdoc.h"
4095c635efSGarrett D'Amore #include "man.h"
41*260e9a87SYuri Pankov #include "manpath.h"
42*260e9a87SYuri Pankov #include "mansearch.h"
4395c635efSGarrett D'Amore 
4495c635efSGarrett D'Amore #if !defined(__GNUC__) || (__GNUC__ < 2)
4595c635efSGarrett D'Amore # if !defined(lint)
4695c635efSGarrett D'Amore #  define __attribute__(x)
4795c635efSGarrett D'Amore # endif
4895c635efSGarrett D'Amore #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
4995c635efSGarrett D'Amore 
50*260e9a87SYuri Pankov enum	outmode {
51*260e9a87SYuri Pankov 	OUTMODE_DEF = 0,
52*260e9a87SYuri Pankov 	OUTMODE_FLN,
53*260e9a87SYuri Pankov 	OUTMODE_LST,
54*260e9a87SYuri Pankov 	OUTMODE_ALL,
55*260e9a87SYuri Pankov 	OUTMODE_INT,
56*260e9a87SYuri Pankov 	OUTMODE_ONE
57*260e9a87SYuri Pankov };
58*260e9a87SYuri Pankov 
5995c635efSGarrett D'Amore typedef	void		(*out_mdoc)(void *, const struct mdoc *);
6095c635efSGarrett D'Amore typedef	void		(*out_man)(void *, const struct man *);
6195c635efSGarrett D'Amore typedef	void		(*out_free)(void *);
6295c635efSGarrett D'Amore 
6395c635efSGarrett D'Amore enum	outt {
6495c635efSGarrett D'Amore 	OUTT_ASCII = 0,	/* -Tascii */
6595c635efSGarrett D'Amore 	OUTT_LOCALE,	/* -Tlocale */
6695c635efSGarrett D'Amore 	OUTT_UTF8,	/* -Tutf8 */
6795c635efSGarrett D'Amore 	OUTT_TREE,	/* -Ttree */
6895c635efSGarrett D'Amore 	OUTT_MAN,	/* -Tman */
6995c635efSGarrett D'Amore 	OUTT_HTML,	/* -Thtml */
7095c635efSGarrett D'Amore 	OUTT_LINT,	/* -Tlint */
7195c635efSGarrett D'Amore 	OUTT_PS,	/* -Tps */
7295c635efSGarrett D'Amore 	OUTT_PDF	/* -Tpdf */
7395c635efSGarrett D'Amore };
7495c635efSGarrett D'Amore 
7595c635efSGarrett D'Amore struct	curparse {
7695c635efSGarrett D'Amore 	struct mparse	 *mp;
77*260e9a87SYuri Pankov 	struct mchars	 *mchars;	/* character table */
7895c635efSGarrett D'Amore 	enum mandoclevel  wlevel;	/* ignore messages below this */
7995c635efSGarrett D'Amore 	int		  wstop;	/* stop after a file with a warning */
80*260e9a87SYuri Pankov 	enum outt	  outtype;	/* which output to use */
8195c635efSGarrett D'Amore 	out_mdoc	  outmdoc;	/* mdoc output ptr */
82*260e9a87SYuri Pankov 	out_man		  outman;	/* man output ptr */
8395c635efSGarrett D'Amore 	out_free	  outfree;	/* free output ptr */
8495c635efSGarrett D'Amore 	void		 *outdata;	/* data for output */
8595c635efSGarrett D'Amore 	char		  outopts[BUFSIZ]; /* buf of output opts */
8695c635efSGarrett D'Amore };
8795c635efSGarrett D'Amore 
88*260e9a87SYuri Pankov static	int		  fs_lookup(const struct manpaths *,
89*260e9a87SYuri Pankov 				size_t ipath, const char *,
90*260e9a87SYuri Pankov 				const char *, const char *,
91*260e9a87SYuri Pankov 				struct manpage **, size_t *);
92*260e9a87SYuri Pankov static	void		  fs_search(const struct mansearch *,
93*260e9a87SYuri Pankov 				const struct manpaths *, int, char**,
94*260e9a87SYuri Pankov 				struct manpage **, size_t *);
95*260e9a87SYuri Pankov static	int		  koptions(int *, char *);
96*260e9a87SYuri Pankov #if HAVE_SQLITE3
97*260e9a87SYuri Pankov int			  mandocdb(int, char**);
98*260e9a87SYuri Pankov #endif
99*260e9a87SYuri Pankov static	int		  moptions(int *, char *);
10095c635efSGarrett D'Amore static	void		  mmsg(enum mandocerr, enum mandoclevel,
10195c635efSGarrett D'Amore 				const char *, int, int, const char *);
102*260e9a87SYuri Pankov static	void		  parse(struct curparse *, int,
10395c635efSGarrett D'Amore 				const char *, enum mandoclevel *);
104*260e9a87SYuri Pankov static	enum mandoclevel  passthrough(const char *, int, int);
105*260e9a87SYuri Pankov static	pid_t		  spawn_pager(void);
10695c635efSGarrett D'Amore static	int		  toptions(struct curparse *, char *);
107*260e9a87SYuri Pankov static	void		  usage(enum argmode) __attribute__((noreturn));
10895c635efSGarrett D'Amore static	int		  woptions(struct curparse *, char *);
10995c635efSGarrett D'Amore 
110*260e9a87SYuri Pankov static	const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
111*260e9a87SYuri Pankov static	char		  help_arg[] = "help";
112*260e9a87SYuri Pankov static	char		 *help_argv[] = {help_arg, NULL};
11395c635efSGarrett D'Amore static	const char	 *progname;
11495c635efSGarrett D'Amore 
115*260e9a87SYuri Pankov 
11695c635efSGarrett D'Amore int
11795c635efSGarrett D'Amore main(int argc, char *argv[])
11895c635efSGarrett D'Amore {
11995c635efSGarrett D'Amore 	struct curparse	 curp;
120*260e9a87SYuri Pankov 	struct mansearch search;
121*260e9a87SYuri Pankov 	struct manpaths	 paths;
122*260e9a87SYuri Pankov 	char		*auxpaths;
123698f87a4SGarrett D'Amore 	char		*defos;
124*260e9a87SYuri Pankov 	unsigned char	*uc;
125*260e9a87SYuri Pankov 	struct manpage	*res, *resp;
126*260e9a87SYuri Pankov 	char		*conf_file, *defpaths;
127*260e9a87SYuri Pankov 	size_t		 isec, i, sz;
128*260e9a87SYuri Pankov 	int		 prio, best_prio, synopsis_only;
129*260e9a87SYuri Pankov 	char		 sec;
130*260e9a87SYuri Pankov 	enum mandoclevel rc, rctmp;
131*260e9a87SYuri Pankov 	enum outmode	 outmode;
132*260e9a87SYuri Pankov 	int		 fd;
133*260e9a87SYuri Pankov 	int		 show_usage;
134*260e9a87SYuri Pankov 	int		 options;
135*260e9a87SYuri Pankov 	int		 c;
136*260e9a87SYuri Pankov 	pid_t		 pager_pid;  /* 0: don't use; 1: not yet spawned. */
13795c635efSGarrett D'Amore 
138*260e9a87SYuri Pankov 	if (argc < 1)
139*260e9a87SYuri Pankov 		progname = "mandoc";
140*260e9a87SYuri Pankov 	else if ((progname = strrchr(argv[0], '/')) == NULL)
14195c635efSGarrett D'Amore 		progname = argv[0];
14295c635efSGarrett D'Amore 	else
14395c635efSGarrett D'Amore 		++progname;
14495c635efSGarrett D'Amore 
145*260e9a87SYuri Pankov #if HAVE_SQLITE3
146*260e9a87SYuri Pankov 	if (strcmp(progname, BINM_MAKEWHATIS) == 0)
147*260e9a87SYuri Pankov 		return(mandocdb(argc, argv));
148*260e9a87SYuri Pankov #endif
149*260e9a87SYuri Pankov 
150*260e9a87SYuri Pankov 	/* Search options. */
15195c635efSGarrett D'Amore 
152*260e9a87SYuri Pankov 	memset(&paths, 0, sizeof(struct manpaths));
153*260e9a87SYuri Pankov 	conf_file = defpaths = NULL;
154*260e9a87SYuri Pankov 	auxpaths = NULL;
155*260e9a87SYuri Pankov 
156*260e9a87SYuri Pankov 	memset(&search, 0, sizeof(struct mansearch));
157*260e9a87SYuri Pankov 	search.outkey = "Nd";
158*260e9a87SYuri Pankov 
159*260e9a87SYuri Pankov 	if (strcmp(progname, BINM_MAN) == 0)
160*260e9a87SYuri Pankov 		search.argmode = ARG_NAME;
161*260e9a87SYuri Pankov 	else if (strcmp(progname, BINM_APROPOS) == 0)
162*260e9a87SYuri Pankov 		search.argmode = ARG_EXPR;
163*260e9a87SYuri Pankov 	else if (strcmp(progname, BINM_WHATIS) == 0)
164*260e9a87SYuri Pankov 		search.argmode = ARG_WORD;
165*260e9a87SYuri Pankov 	else if (strncmp(progname, "help", 4) == 0)
166*260e9a87SYuri Pankov 		search.argmode = ARG_NAME;
167*260e9a87SYuri Pankov 	else
168*260e9a87SYuri Pankov 		search.argmode = ARG_FILE;
169*260e9a87SYuri Pankov 
170*260e9a87SYuri Pankov 	/* Parser and formatter options. */
171*260e9a87SYuri Pankov 
172*260e9a87SYuri Pankov 	memset(&curp, 0, sizeof(struct curparse));
173*260e9a87SYuri Pankov 	curp.outtype = OUTT_LOCALE;
174*260e9a87SYuri Pankov 	curp.wlevel  = MANDOCLEVEL_BADARG;
175*260e9a87SYuri Pankov 	options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1;
176698f87a4SGarrett D'Amore 	defos = NULL;
17795c635efSGarrett D'Amore 
178*260e9a87SYuri Pankov 	pager_pid = 1;
179*260e9a87SYuri Pankov 	show_usage = 0;
180*260e9a87SYuri Pankov 	synopsis_only = 0;
181*260e9a87SYuri Pankov 	outmode = OUTMODE_DEF;
182*260e9a87SYuri Pankov 
183*260e9a87SYuri Pankov 	while (-1 != (c = getopt(argc, argv,
184*260e9a87SYuri Pankov 			"aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) {
18595c635efSGarrett D'Amore 		switch (c) {
186*260e9a87SYuri Pankov 		case 'a':
187*260e9a87SYuri Pankov 			outmode = OUTMODE_ALL;
188*260e9a87SYuri Pankov 			break;
189*260e9a87SYuri Pankov 		case 'C':
190*260e9a87SYuri Pankov 			conf_file = optarg;
191*260e9a87SYuri Pankov 			break;
192*260e9a87SYuri Pankov 		case 'c':
193*260e9a87SYuri Pankov 			pager_pid = 0;
194*260e9a87SYuri Pankov 			break;
195*260e9a87SYuri Pankov 		case 'f':
196*260e9a87SYuri Pankov 			search.argmode = ARG_WORD;
197*260e9a87SYuri Pankov 			break;
198*260e9a87SYuri Pankov 		case 'h':
199*260e9a87SYuri Pankov 			(void)strlcat(curp.outopts, "synopsis,", BUFSIZ);
200*260e9a87SYuri Pankov 			synopsis_only = 1;
201*260e9a87SYuri Pankov 			pager_pid = 0;
202*260e9a87SYuri Pankov 			outmode = OUTMODE_ALL;
203*260e9a87SYuri Pankov 			break;
204*260e9a87SYuri Pankov 		case 'I':
205698f87a4SGarrett D'Amore 			if (strncmp(optarg, "os=", 3)) {
206*260e9a87SYuri Pankov 				fprintf(stderr,
207*260e9a87SYuri Pankov 				    "%s: -I %s: Bad argument\n",
208*260e9a87SYuri Pankov 				    progname, optarg);
209698f87a4SGarrett D'Amore 				return((int)MANDOCLEVEL_BADARG);
210698f87a4SGarrett D'Amore 			}
211698f87a4SGarrett D'Amore 			if (defos) {
212*260e9a87SYuri Pankov 				fprintf(stderr,
213*260e9a87SYuri Pankov 				    "%s: -I %s: Duplicate argument\n",
214*260e9a87SYuri Pankov 				    progname, optarg);
215698f87a4SGarrett D'Amore 				return((int)MANDOCLEVEL_BADARG);
216698f87a4SGarrett D'Amore 			}
217698f87a4SGarrett D'Amore 			defos = mandoc_strdup(optarg + 3);
218698f87a4SGarrett D'Amore 			break;
219*260e9a87SYuri Pankov 		case 'i':
220*260e9a87SYuri Pankov 			outmode = OUTMODE_INT;
221*260e9a87SYuri Pankov 			break;
222*260e9a87SYuri Pankov 		case 'K':
223*260e9a87SYuri Pankov 			if ( ! koptions(&options, optarg))
22495c635efSGarrett D'Amore 				return((int)MANDOCLEVEL_BADARG);
22595c635efSGarrett D'Amore 			break;
226*260e9a87SYuri Pankov 		case 'k':
227*260e9a87SYuri Pankov 			search.argmode = ARG_EXPR;
228*260e9a87SYuri Pankov 			break;
229*260e9a87SYuri Pankov 		case 'l':
230*260e9a87SYuri Pankov 			search.argmode = ARG_FILE;
231*260e9a87SYuri Pankov 			outmode = OUTMODE_ALL;
232*260e9a87SYuri Pankov 			break;
233*260e9a87SYuri Pankov 		case 'M':
234*260e9a87SYuri Pankov 			defpaths = optarg;
235*260e9a87SYuri Pankov 			break;
236*260e9a87SYuri Pankov 		case 'm':
237*260e9a87SYuri Pankov 			auxpaths = optarg;
238*260e9a87SYuri Pankov 			break;
239*260e9a87SYuri Pankov 		case 'O':
240*260e9a87SYuri Pankov 			search.outkey = optarg;
24195c635efSGarrett D'Amore 			(void)strlcat(curp.outopts, optarg, BUFSIZ);
24295c635efSGarrett D'Amore 			(void)strlcat(curp.outopts, ",", BUFSIZ);
24395c635efSGarrett D'Amore 			break;
244*260e9a87SYuri Pankov 		case 'S':
245*260e9a87SYuri Pankov 			search.arch = optarg;
246*260e9a87SYuri Pankov 			break;
247*260e9a87SYuri Pankov 		case 's':
248*260e9a87SYuri Pankov 			search.sec = optarg;
249*260e9a87SYuri Pankov 			break;
250*260e9a87SYuri Pankov 		case 'T':
25195c635efSGarrett D'Amore 			if ( ! toptions(&curp, optarg))
25295c635efSGarrett D'Amore 				return((int)MANDOCLEVEL_BADARG);
25395c635efSGarrett D'Amore 			break;
254*260e9a87SYuri Pankov 		case 'W':
25595c635efSGarrett D'Amore 			if ( ! woptions(&curp, optarg))
25695c635efSGarrett D'Amore 				return((int)MANDOCLEVEL_BADARG);
25795c635efSGarrett D'Amore 			break;
258*260e9a87SYuri Pankov 		case 'w':
259*260e9a87SYuri Pankov 			outmode = OUTMODE_FLN;
260*260e9a87SYuri Pankov 			break;
26195c635efSGarrett D'Amore 		default:
262*260e9a87SYuri Pankov 			show_usage = 1;
263*260e9a87SYuri Pankov 			break;
26495c635efSGarrett D'Amore 		}
265*260e9a87SYuri Pankov 	}
266*260e9a87SYuri Pankov 
267*260e9a87SYuri Pankov 	if (show_usage)
268*260e9a87SYuri Pankov 		usage(search.argmode);
26995c635efSGarrett D'Amore 
270*260e9a87SYuri Pankov 	/* Postprocess options. */
271*260e9a87SYuri Pankov 
272*260e9a87SYuri Pankov 	if (outmode == OUTMODE_DEF) {
273*260e9a87SYuri Pankov 		switch (search.argmode) {
274*260e9a87SYuri Pankov 		case ARG_FILE:
275*260e9a87SYuri Pankov 			outmode = OUTMODE_ALL;
276*260e9a87SYuri Pankov 			pager_pid = 0;
277*260e9a87SYuri Pankov 			break;
278*260e9a87SYuri Pankov 		case ARG_NAME:
279*260e9a87SYuri Pankov 			outmode = OUTMODE_ONE;
280*260e9a87SYuri Pankov 			break;
281*260e9a87SYuri Pankov 		default:
282*260e9a87SYuri Pankov 			outmode = OUTMODE_LST;
283*260e9a87SYuri Pankov 			break;
284*260e9a87SYuri Pankov 		}
285*260e9a87SYuri Pankov 	}
286*260e9a87SYuri Pankov 
287*260e9a87SYuri Pankov 	/* Parse arguments. */
288*260e9a87SYuri Pankov 
289*260e9a87SYuri Pankov 	if (argc > 0) {
290*260e9a87SYuri Pankov 		argc -= optind;
291*260e9a87SYuri Pankov 		argv += optind;
292*260e9a87SYuri Pankov 	}
293*260e9a87SYuri Pankov 	resp = NULL;
294*260e9a87SYuri Pankov 
295*260e9a87SYuri Pankov 	/*
296*260e9a87SYuri Pankov 	 * Quirks for help(1)
297*260e9a87SYuri Pankov 	 * and for a man(1) section argument without -s.
298*260e9a87SYuri Pankov 	 */
299*260e9a87SYuri Pankov 
300*260e9a87SYuri Pankov 	if (search.argmode == ARG_NAME) {
301*260e9a87SYuri Pankov 		if (*progname == 'h') {
302*260e9a87SYuri Pankov 			if (argc == 0) {
303*260e9a87SYuri Pankov 				argv = help_argv;
304*260e9a87SYuri Pankov 				argc = 1;
305*260e9a87SYuri Pankov 			}
306*260e9a87SYuri Pankov 		} else if (argc > 1 &&
307*260e9a87SYuri Pankov 		    ((uc = (unsigned char *)argv[0]) != NULL) &&
308*260e9a87SYuri Pankov 		    ((isdigit(uc[0]) && (uc[1] == '\0' ||
309*260e9a87SYuri Pankov 		      (isalpha(uc[1]) && uc[2] == '\0'))) ||
310*260e9a87SYuri Pankov 		     (uc[0] == 'n' && uc[1] == '\0'))) {
311*260e9a87SYuri Pankov 			search.sec = (char *)uc;
312*260e9a87SYuri Pankov 			argv++;
313*260e9a87SYuri Pankov 			argc--;
314*260e9a87SYuri Pankov 		}
315*260e9a87SYuri Pankov 		if (search.arch == NULL)
316*260e9a87SYuri Pankov 			search.arch = getenv("MACHINE");
317*260e9a87SYuri Pankov #ifdef MACHINE
318*260e9a87SYuri Pankov 		if (search.arch == NULL)
319*260e9a87SYuri Pankov 			search.arch = MACHINE;
320*260e9a87SYuri Pankov #endif
321*260e9a87SYuri Pankov 	}
322*260e9a87SYuri Pankov 
323*260e9a87SYuri Pankov 	rc = MANDOCLEVEL_OK;
324*260e9a87SYuri Pankov 
325*260e9a87SYuri Pankov 	/* man(1), whatis(1), apropos(1) */
326*260e9a87SYuri Pankov 
327*260e9a87SYuri Pankov 	if (search.argmode != ARG_FILE) {
328*260e9a87SYuri Pankov 		if (argc == 0)
329*260e9a87SYuri Pankov 			usage(search.argmode);
330*260e9a87SYuri Pankov 
331*260e9a87SYuri Pankov 		if (search.argmode == ARG_NAME &&
332*260e9a87SYuri Pankov 		    outmode == OUTMODE_ONE)
333*260e9a87SYuri Pankov 			search.firstmatch = 1;
334*260e9a87SYuri Pankov 
335*260e9a87SYuri Pankov 		/* Access the mandoc database. */
336*260e9a87SYuri Pankov 
337*260e9a87SYuri Pankov 		manpath_parse(&paths, conf_file, defpaths, auxpaths);
338*260e9a87SYuri Pankov #if HAVE_SQLITE3
339*260e9a87SYuri Pankov 		mansearch_setup(1);
340*260e9a87SYuri Pankov 		if( ! mansearch(&search, &paths, argc, argv, &res, &sz))
341*260e9a87SYuri Pankov 			usage(search.argmode);
342*260e9a87SYuri Pankov #else
343*260e9a87SYuri Pankov 		if (search.argmode != ARG_NAME) {
344*260e9a87SYuri Pankov 			fputs("mandoc: database support not compiled in\n",
345*260e9a87SYuri Pankov 			    stderr);
346*260e9a87SYuri Pankov 			return((int)MANDOCLEVEL_BADARG);
347*260e9a87SYuri Pankov 		}
348*260e9a87SYuri Pankov 		sz = 0;
349*260e9a87SYuri Pankov #endif
350*260e9a87SYuri Pankov 
351*260e9a87SYuri Pankov 		if (sz == 0 && search.argmode == ARG_NAME)
352*260e9a87SYuri Pankov 			fs_search(&search, &paths, argc, argv, &res, &sz);
353*260e9a87SYuri Pankov 
354*260e9a87SYuri Pankov 		if (sz == 0) {
355*260e9a87SYuri Pankov 			rc = MANDOCLEVEL_BADARG;
356*260e9a87SYuri Pankov 			goto out;
357*260e9a87SYuri Pankov 		}
358*260e9a87SYuri Pankov 
359*260e9a87SYuri Pankov 		/*
360*260e9a87SYuri Pankov 		 * For standard man(1) and -a output mode,
361*260e9a87SYuri Pankov 		 * prepare for copying filename pointers
362*260e9a87SYuri Pankov 		 * into the program parameter array.
363*260e9a87SYuri Pankov 		 */
364*260e9a87SYuri Pankov 
365*260e9a87SYuri Pankov 		if (outmode == OUTMODE_ONE) {
366*260e9a87SYuri Pankov 			argc = 1;
367*260e9a87SYuri Pankov 			best_prio = 10;
368*260e9a87SYuri Pankov 		} else if (outmode == OUTMODE_ALL)
369*260e9a87SYuri Pankov 			argc = (int)sz;
370*260e9a87SYuri Pankov 
371*260e9a87SYuri Pankov 		/* Iterate all matching manuals. */
372*260e9a87SYuri Pankov 
373*260e9a87SYuri Pankov 		resp = res;
374*260e9a87SYuri Pankov 		for (i = 0; i < sz; i++) {
375*260e9a87SYuri Pankov 			if (outmode == OUTMODE_FLN)
376*260e9a87SYuri Pankov 				puts(res[i].file);
377*260e9a87SYuri Pankov 			else if (outmode == OUTMODE_LST)
378*260e9a87SYuri Pankov 				printf("%s - %s\n", res[i].names,
379*260e9a87SYuri Pankov 				    res[i].output == NULL ? "" :
380*260e9a87SYuri Pankov 				    res[i].output);
381*260e9a87SYuri Pankov 			else if (outmode == OUTMODE_ONE) {
382*260e9a87SYuri Pankov 				/* Search for the best section. */
383*260e9a87SYuri Pankov 				isec = strcspn(res[i].file, "123456789");
384*260e9a87SYuri Pankov 				sec = res[i].file[isec];
385*260e9a87SYuri Pankov 				if ('\0' == sec)
386*260e9a87SYuri Pankov 					continue;
387*260e9a87SYuri Pankov 				prio = sec_prios[sec - '1'];
388*260e9a87SYuri Pankov 				if (prio >= best_prio)
389*260e9a87SYuri Pankov 					continue;
390*260e9a87SYuri Pankov 				best_prio = prio;
391*260e9a87SYuri Pankov 				resp = res + i;
392*260e9a87SYuri Pankov 			}
393*260e9a87SYuri Pankov 		}
394*260e9a87SYuri Pankov 
395*260e9a87SYuri Pankov 		/*
396*260e9a87SYuri Pankov 		 * For man(1), -a and -i output mode, fall through
397*260e9a87SYuri Pankov 		 * to the main mandoc(1) code iterating files
398*260e9a87SYuri Pankov 		 * and running the parsers on each of them.
399*260e9a87SYuri Pankov 		 */
400*260e9a87SYuri Pankov 
401*260e9a87SYuri Pankov 		if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST)
402*260e9a87SYuri Pankov 			goto out;
403*260e9a87SYuri Pankov 	}
404*260e9a87SYuri Pankov 
405*260e9a87SYuri Pankov 	/* mandoc(1) */
406*260e9a87SYuri Pankov 
407*260e9a87SYuri Pankov 	if (search.argmode == ARG_FILE && ! moptions(&options, auxpaths))
408*260e9a87SYuri Pankov 		return((int)MANDOCLEVEL_BADARG);
409*260e9a87SYuri Pankov 
410*260e9a87SYuri Pankov 	curp.mchars = mchars_alloc();
411*260e9a87SYuri Pankov 	curp.mp = mparse_alloc(options, curp.wlevel, mmsg,
412*260e9a87SYuri Pankov 	    curp.mchars, defos);
41395c635efSGarrett D'Amore 
41495c635efSGarrett D'Amore 	/*
41595c635efSGarrett D'Amore 	 * Conditionally start up the lookaside buffer before parsing.
41695c635efSGarrett D'Amore 	 */
41795c635efSGarrett D'Amore 	if (OUTT_MAN == curp.outtype)
41895c635efSGarrett D'Amore 		mparse_keep(curp.mp);
41995c635efSGarrett D'Amore 
420*260e9a87SYuri Pankov 	if (argc < 1) {
421*260e9a87SYuri Pankov 		if (pager_pid == 1 && isatty(STDOUT_FILENO))
422*260e9a87SYuri Pankov 			pager_pid = spawn_pager();
423*260e9a87SYuri Pankov 		parse(&curp, STDIN_FILENO, "<stdin>", &rc);
424*260e9a87SYuri Pankov 	}
42595c635efSGarrett D'Amore 
426*260e9a87SYuri Pankov 	while (argc > 0) {
427*260e9a87SYuri Pankov 		rctmp = mparse_open(curp.mp, &fd,
428*260e9a87SYuri Pankov 		    resp != NULL ? resp->file : *argv);
429*260e9a87SYuri Pankov 		if (rc < rctmp)
430*260e9a87SYuri Pankov 			rc = rctmp;
431*260e9a87SYuri Pankov 
432*260e9a87SYuri Pankov 		if (fd != -1) {
433*260e9a87SYuri Pankov 			if (pager_pid == 1 && isatty(STDOUT_FILENO))
434*260e9a87SYuri Pankov 				pager_pid = spawn_pager();
435*260e9a87SYuri Pankov 
436*260e9a87SYuri Pankov 			if (resp == NULL)
437*260e9a87SYuri Pankov 				parse(&curp, fd, *argv, &rc);
438*260e9a87SYuri Pankov 			else if (resp->form & FORM_SRC) {
439*260e9a87SYuri Pankov 				/* For .so only; ignore failure. */
440*260e9a87SYuri Pankov 				chdir(paths.paths[resp->ipath]);
441*260e9a87SYuri Pankov 				parse(&curp, fd, resp->file, &rc);
442*260e9a87SYuri Pankov 			} else {
443*260e9a87SYuri Pankov 				rctmp = passthrough(resp->file, fd,
444*260e9a87SYuri Pankov 				    synopsis_only);
445*260e9a87SYuri Pankov 				if (rc < rctmp)
446*260e9a87SYuri Pankov 					rc = rctmp;
447*260e9a87SYuri Pankov 			}
44895c635efSGarrett D'Amore 
449*260e9a87SYuri Pankov 			rctmp = mparse_wait(curp.mp);
450*260e9a87SYuri Pankov 			if (rc < rctmp)
451*260e9a87SYuri Pankov 				rc = rctmp;
452*260e9a87SYuri Pankov 
453*260e9a87SYuri Pankov 			if (argc > 1 && curp.outtype <= OUTT_UTF8)
454*260e9a87SYuri Pankov 				ascii_sepline(curp.outdata);
455*260e9a87SYuri Pankov 		}
45695c635efSGarrett D'Amore 
45795c635efSGarrett D'Amore 		if (MANDOCLEVEL_OK != rc && curp.wstop)
45895c635efSGarrett D'Amore 			break;
459*260e9a87SYuri Pankov 
460*260e9a87SYuri Pankov 		if (resp != NULL)
461*260e9a87SYuri Pankov 			resp++;
462*260e9a87SYuri Pankov 		else
463*260e9a87SYuri Pankov 			argv++;
464*260e9a87SYuri Pankov 		if (--argc)
465*260e9a87SYuri Pankov 			mparse_reset(curp.mp);
46695c635efSGarrett D'Amore 	}
46795c635efSGarrett D'Amore 
46895c635efSGarrett D'Amore 	if (curp.outfree)
46995c635efSGarrett D'Amore 		(*curp.outfree)(curp.outdata);
470*260e9a87SYuri Pankov 	mparse_free(curp.mp);
471*260e9a87SYuri Pankov 	mchars_free(curp.mchars);
472*260e9a87SYuri Pankov 
473*260e9a87SYuri Pankov out:
474*260e9a87SYuri Pankov 	if (search.argmode != ARG_FILE) {
475*260e9a87SYuri Pankov 		manpath_free(&paths);
476*260e9a87SYuri Pankov #if HAVE_SQLITE3
477*260e9a87SYuri Pankov 		mansearch_free(res, sz);
478*260e9a87SYuri Pankov 		mansearch_setup(0);
479*260e9a87SYuri Pankov #endif
480*260e9a87SYuri Pankov 	}
481*260e9a87SYuri Pankov 
482698f87a4SGarrett D'Amore 	free(defos);
48395c635efSGarrett D'Amore 
484*260e9a87SYuri Pankov 	/*
485*260e9a87SYuri Pankov 	 * If a pager is attached, flush the pipe leading to it
486*260e9a87SYuri Pankov 	 * and signal end of file such that the user can browse
487*260e9a87SYuri Pankov 	 * to the end.  Then wait for the user to close the pager.
488*260e9a87SYuri Pankov 	 */
489*260e9a87SYuri Pankov 
490*260e9a87SYuri Pankov 	if (pager_pid != 0 && pager_pid != 1) {
491*260e9a87SYuri Pankov 		fclose(stdout);
492*260e9a87SYuri Pankov 		waitpid(pager_pid, NULL, 0);
493*260e9a87SYuri Pankov 	}
494*260e9a87SYuri Pankov 
49595c635efSGarrett D'Amore 	return((int)rc);
49695c635efSGarrett D'Amore }
49795c635efSGarrett D'Amore 
49895c635efSGarrett D'Amore static void
499*260e9a87SYuri Pankov usage(enum argmode argmode)
50095c635efSGarrett D'Amore {
50195c635efSGarrett D'Amore 
502*260e9a87SYuri Pankov 	switch (argmode) {
503*260e9a87SYuri Pankov 	case ARG_FILE:
504*260e9a87SYuri Pankov 		fputs("usage: mandoc [-acfhkl] [-Ios=name] "
505*260e9a87SYuri Pankov 		    "[-Kencoding] [-mformat] [-Ooption]\n"
506*260e9a87SYuri Pankov 		    "\t      [-Toutput] [-Wlevel] [file ...]\n", stderr);
507*260e9a87SYuri Pankov 		break;
508*260e9a87SYuri Pankov 	case ARG_NAME:
509*260e9a87SYuri Pankov 		fputs("usage: man [-acfhklw] [-C file] [-I os=name] "
510*260e9a87SYuri Pankov 		    "[-K encoding] [-M path] [-m path]\n"
511*260e9a87SYuri Pankov 		    "\t   [-O option=value] [-S subsection] [-s section] "
512*260e9a87SYuri Pankov 		    "[-T output] [-W level]\n"
513*260e9a87SYuri Pankov 		    "\t   [section] name ...\n", stderr);
514*260e9a87SYuri Pankov 		break;
515*260e9a87SYuri Pankov 	case ARG_WORD:
516*260e9a87SYuri Pankov 		fputs("usage: whatis [-acfhklw] [-C file] "
517*260e9a87SYuri Pankov 		    "[-M path] [-m path] [-O outkey] [-S arch]\n"
518*260e9a87SYuri Pankov 		    "\t      [-s section] name ...\n", stderr);
519*260e9a87SYuri Pankov 		break;
520*260e9a87SYuri Pankov 	case ARG_EXPR:
521*260e9a87SYuri Pankov 		fputs("usage: apropos [-acfhklw] [-C file] "
522*260e9a87SYuri Pankov 		    "[-M path] [-m path] [-O outkey] [-S arch]\n"
523*260e9a87SYuri Pankov 		    "\t       [-s section] expression ...\n", stderr);
524*260e9a87SYuri Pankov 		break;
525*260e9a87SYuri Pankov 	}
526*260e9a87SYuri Pankov 	exit((int)MANDOCLEVEL_BADARG);
52795c635efSGarrett D'Amore }
52895c635efSGarrett D'Amore 
529*260e9a87SYuri Pankov static int
530*260e9a87SYuri Pankov fs_lookup(const struct manpaths *paths, size_t ipath,
531*260e9a87SYuri Pankov 	const char *sec, const char *arch, const char *name,
532*260e9a87SYuri Pankov 	struct manpage **res, size_t *ressz)
53395c635efSGarrett D'Amore {
534*260e9a87SYuri Pankov 	glob_t		 globinfo;
535*260e9a87SYuri Pankov 	struct manpage	*page;
536*260e9a87SYuri Pankov 	char		*file;
537*260e9a87SYuri Pankov 	int		 form, globres;
538*260e9a87SYuri Pankov 
539*260e9a87SYuri Pankov 	form = FORM_SRC;
540*260e9a87SYuri Pankov 	mandoc_asprintf(&file, "%s/man%s/%s.%s",
541*260e9a87SYuri Pankov 	    paths->paths[ipath], sec, name, sec);
542*260e9a87SYuri Pankov 	if (access(file, R_OK) != -1)
543*260e9a87SYuri Pankov 		goto found;
544*260e9a87SYuri Pankov 	free(file);
545*260e9a87SYuri Pankov 
546*260e9a87SYuri Pankov 	mandoc_asprintf(&file, "%s/cat%s/%s.0",
547*260e9a87SYuri Pankov 	    paths->paths[ipath], sec, name);
548*260e9a87SYuri Pankov 	if (access(file, R_OK) != -1) {
549*260e9a87SYuri Pankov 		form = FORM_CAT;
550*260e9a87SYuri Pankov 		goto found;
551*260e9a87SYuri Pankov 	}
552*260e9a87SYuri Pankov 	free(file);
553*260e9a87SYuri Pankov 
554*260e9a87SYuri Pankov 	if (arch != NULL) {
555*260e9a87SYuri Pankov 		mandoc_asprintf(&file, "%s/man%s/%s/%s.%s",
556*260e9a87SYuri Pankov 		    paths->paths[ipath], sec, arch, name, sec);
557*260e9a87SYuri Pankov 		if (access(file, R_OK) != -1)
558*260e9a87SYuri Pankov 			goto found;
559*260e9a87SYuri Pankov 		free(file);
560*260e9a87SYuri Pankov 	}
561*260e9a87SYuri Pankov 
562*260e9a87SYuri Pankov 	mandoc_asprintf(&file, "%s/man%s/%s.*",
563*260e9a87SYuri Pankov 	    paths->paths[ipath], sec, name);
564*260e9a87SYuri Pankov 	globres = glob(file, 0, NULL, &globinfo);
565*260e9a87SYuri Pankov 	if (globres != 0 && globres != GLOB_NOMATCH)
566*260e9a87SYuri Pankov 		fprintf(stderr, "%s: %s: glob: %s\n",
567*260e9a87SYuri Pankov 		    progname, file, strerror(errno));
568*260e9a87SYuri Pankov 	free(file);
569*260e9a87SYuri Pankov 	if (globres == 0)
570*260e9a87SYuri Pankov 		file = mandoc_strdup(*globinfo.gl_pathv);
571*260e9a87SYuri Pankov 	globfree(&globinfo);
572*260e9a87SYuri Pankov 	if (globres != 0)
573*260e9a87SYuri Pankov 		return(0);
57495c635efSGarrett D'Amore 
575*260e9a87SYuri Pankov found:
576*260e9a87SYuri Pankov #if HAVE_SQLITE3
577*260e9a87SYuri Pankov 	fprintf(stderr, "%s: outdated mandoc.db lacks %s(%s) entry,\n"
578*260e9a87SYuri Pankov 	    "     consider running  # makewhatis %s\n",
579*260e9a87SYuri Pankov 	    progname, name, sec, paths->paths[ipath]);
580*260e9a87SYuri Pankov #endif
58195c635efSGarrett D'Amore 
582*260e9a87SYuri Pankov 	*res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage));
583*260e9a87SYuri Pankov 	page = *res + (*ressz - 1);
584*260e9a87SYuri Pankov 	page->file = file;
585*260e9a87SYuri Pankov 	page->names = NULL;
586*260e9a87SYuri Pankov 	page->output = NULL;
587*260e9a87SYuri Pankov 	page->ipath = ipath;
588*260e9a87SYuri Pankov 	page->bits = NAME_FILE & NAME_MASK;
589*260e9a87SYuri Pankov 	page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10;
590*260e9a87SYuri Pankov 	page->form = form;
591*260e9a87SYuri Pankov 	return(1);
592*260e9a87SYuri Pankov }
593*260e9a87SYuri Pankov 
594*260e9a87SYuri Pankov static void
595*260e9a87SYuri Pankov fs_search(const struct mansearch *cfg, const struct manpaths *paths,
596*260e9a87SYuri Pankov 	int argc, char **argv, struct manpage **res, size_t *ressz)
597*260e9a87SYuri Pankov {
598*260e9a87SYuri Pankov 	const char *const sections[] =
599*260e9a87SYuri Pankov 	    {"1", "8", "6", "2", "3", "3p", "5", "7", "4", "9"};
600*260e9a87SYuri Pankov 	const size_t nsec = sizeof(sections)/sizeof(sections[0]);
601*260e9a87SYuri Pankov 
602*260e9a87SYuri Pankov 	size_t		 ipath, isec, lastsz;
603*260e9a87SYuri Pankov 
604*260e9a87SYuri Pankov 	assert(cfg->argmode == ARG_NAME);
605*260e9a87SYuri Pankov 
606*260e9a87SYuri Pankov 	*res = NULL;
607*260e9a87SYuri Pankov 	*ressz = lastsz = 0;
608*260e9a87SYuri Pankov 	while (argc) {
609*260e9a87SYuri Pankov 		for (ipath = 0; ipath < paths->sz; ipath++) {
610*260e9a87SYuri Pankov 			if (cfg->sec != NULL) {
611*260e9a87SYuri Pankov 				if (fs_lookup(paths, ipath, cfg->sec,
612*260e9a87SYuri Pankov 				    cfg->arch, *argv, res, ressz) &&
613*260e9a87SYuri Pankov 				    cfg->firstmatch)
614*260e9a87SYuri Pankov 					return;
615*260e9a87SYuri Pankov 			} else for (isec = 0; isec < nsec; isec++)
616*260e9a87SYuri Pankov 				if (fs_lookup(paths, ipath, sections[isec],
617*260e9a87SYuri Pankov 				    cfg->arch, *argv, res, ressz) &&
618*260e9a87SYuri Pankov 				    cfg->firstmatch)
619*260e9a87SYuri Pankov 					return;
620*260e9a87SYuri Pankov 		}
621*260e9a87SYuri Pankov 		if (*ressz == lastsz)
622*260e9a87SYuri Pankov 			fprintf(stderr,
623*260e9a87SYuri Pankov 			    "%s: No entry for %s in the manual.\n",
624*260e9a87SYuri Pankov 			    progname, *argv);
625*260e9a87SYuri Pankov 		lastsz = *ressz;
626*260e9a87SYuri Pankov 		argv++;
627*260e9a87SYuri Pankov 		argc--;
628*260e9a87SYuri Pankov 	}
62995c635efSGarrett D'Amore }
63095c635efSGarrett D'Amore 
63195c635efSGarrett D'Amore static void
632*260e9a87SYuri Pankov parse(struct curparse *curp, int fd, const char *file,
633*260e9a87SYuri Pankov 	enum mandoclevel *level)
63495c635efSGarrett D'Amore {
63595c635efSGarrett D'Amore 	enum mandoclevel  rc;
63695c635efSGarrett D'Amore 	struct mdoc	 *mdoc;
63795c635efSGarrett D'Amore 	struct man	 *man;
63895c635efSGarrett D'Amore 
63995c635efSGarrett D'Amore 	/* Begin by parsing the file itself. */
64095c635efSGarrett D'Amore 
64195c635efSGarrett D'Amore 	assert(file);
64295c635efSGarrett D'Amore 	assert(fd >= -1);
64395c635efSGarrett D'Amore 
64495c635efSGarrett D'Amore 	rc = mparse_readfd(curp->mp, fd, file);
64595c635efSGarrett D'Amore 
64695c635efSGarrett D'Amore 	/*
64795c635efSGarrett D'Amore 	 * With -Wstop and warnings or errors of at least the requested
64895c635efSGarrett D'Amore 	 * level, do not produce output.
64995c635efSGarrett D'Amore 	 */
65095c635efSGarrett D'Amore 
65195c635efSGarrett D'Amore 	if (MANDOCLEVEL_OK != rc && curp->wstop)
65295c635efSGarrett D'Amore 		goto cleanup;
65395c635efSGarrett D'Amore 
65495c635efSGarrett D'Amore 	/* If unset, allocate output dev now (if applicable). */
65595c635efSGarrett D'Amore 
65695c635efSGarrett D'Amore 	if ( ! (curp->outman && curp->outmdoc)) {
65795c635efSGarrett D'Amore 		switch (curp->outtype) {
658*260e9a87SYuri Pankov 		case OUTT_HTML:
659*260e9a87SYuri Pankov 			curp->outdata = html_alloc(curp->mchars,
660*260e9a87SYuri Pankov 			    curp->outopts);
66195c635efSGarrett D'Amore 			curp->outfree = html_free;
66295c635efSGarrett D'Amore 			break;
663*260e9a87SYuri Pankov 		case OUTT_UTF8:
664*260e9a87SYuri Pankov 			curp->outdata = utf8_alloc(curp->mchars,
665*260e9a87SYuri Pankov 			    curp->outopts);
66695c635efSGarrett D'Amore 			curp->outfree = ascii_free;
66795c635efSGarrett D'Amore 			break;
668*260e9a87SYuri Pankov 		case OUTT_LOCALE:
669*260e9a87SYuri Pankov 			curp->outdata = locale_alloc(curp->mchars,
670*260e9a87SYuri Pankov 			    curp->outopts);
67195c635efSGarrett D'Amore 			curp->outfree = ascii_free;
67295c635efSGarrett D'Amore 			break;
673*260e9a87SYuri Pankov 		case OUTT_ASCII:
674*260e9a87SYuri Pankov 			curp->outdata = ascii_alloc(curp->mchars,
675*260e9a87SYuri Pankov 			    curp->outopts);
67695c635efSGarrett D'Amore 			curp->outfree = ascii_free;
67795c635efSGarrett D'Amore 			break;
678*260e9a87SYuri Pankov 		case OUTT_PDF:
679*260e9a87SYuri Pankov 			curp->outdata = pdf_alloc(curp->mchars,
680*260e9a87SYuri Pankov 			    curp->outopts);
68195c635efSGarrett D'Amore 			curp->outfree = pspdf_free;
68295c635efSGarrett D'Amore 			break;
683*260e9a87SYuri Pankov 		case OUTT_PS:
684*260e9a87SYuri Pankov 			curp->outdata = ps_alloc(curp->mchars,
685*260e9a87SYuri Pankov 			    curp->outopts);
68695c635efSGarrett D'Amore 			curp->outfree = pspdf_free;
68795c635efSGarrett D'Amore 			break;
68895c635efSGarrett D'Amore 		default:
68995c635efSGarrett D'Amore 			break;
69095c635efSGarrett D'Amore 		}
69195c635efSGarrett D'Amore 
69295c635efSGarrett D'Amore 		switch (curp->outtype) {
693*260e9a87SYuri Pankov 		case OUTT_HTML:
69495c635efSGarrett D'Amore 			curp->outman = html_man;
69595c635efSGarrett D'Amore 			curp->outmdoc = html_mdoc;
69695c635efSGarrett D'Amore 			break;
697*260e9a87SYuri Pankov 		case OUTT_TREE:
69895c635efSGarrett D'Amore 			curp->outman = tree_man;
69995c635efSGarrett D'Amore 			curp->outmdoc = tree_mdoc;
70095c635efSGarrett D'Amore 			break;
701*260e9a87SYuri Pankov 		case OUTT_MAN:
70295c635efSGarrett D'Amore 			curp->outmdoc = man_mdoc;
70395c635efSGarrett D'Amore 			curp->outman = man_man;
70495c635efSGarrett D'Amore 			break;
705*260e9a87SYuri Pankov 		case OUTT_PDF:
70695c635efSGarrett D'Amore 			/* FALLTHROUGH */
707*260e9a87SYuri Pankov 		case OUTT_ASCII:
70895c635efSGarrett D'Amore 			/* FALLTHROUGH */
709*260e9a87SYuri Pankov 		case OUTT_UTF8:
71095c635efSGarrett D'Amore 			/* FALLTHROUGH */
711*260e9a87SYuri Pankov 		case OUTT_LOCALE:
71295c635efSGarrett D'Amore 			/* FALLTHROUGH */
713*260e9a87SYuri Pankov 		case OUTT_PS:
71495c635efSGarrett D'Amore 			curp->outman = terminal_man;
71595c635efSGarrett D'Amore 			curp->outmdoc = terminal_mdoc;
71695c635efSGarrett D'Amore 			break;
71795c635efSGarrett D'Amore 		default:
71895c635efSGarrett D'Amore 			break;
71995c635efSGarrett D'Amore 		}
72095c635efSGarrett D'Amore 	}
72195c635efSGarrett D'Amore 
722*260e9a87SYuri Pankov 	mparse_result(curp->mp, &mdoc, &man, NULL);
72395c635efSGarrett D'Amore 
72495c635efSGarrett D'Amore 	/* Execute the out device, if it exists. */
72595c635efSGarrett D'Amore 
72695c635efSGarrett D'Amore 	if (man && curp->outman)
72795c635efSGarrett D'Amore 		(*curp->outman)(curp->outdata, man);
72895c635efSGarrett D'Amore 	if (mdoc && curp->outmdoc)
72995c635efSGarrett D'Amore 		(*curp->outmdoc)(curp->outdata, mdoc);
73095c635efSGarrett D'Amore 
731*260e9a87SYuri Pankov cleanup:
73295c635efSGarrett D'Amore 	if (*level < rc)
73395c635efSGarrett D'Amore 		*level = rc;
73495c635efSGarrett D'Amore }
73595c635efSGarrett D'Amore 
736*260e9a87SYuri Pankov static enum mandoclevel
737*260e9a87SYuri Pankov passthrough(const char *file, int fd, int synopsis_only)
738*260e9a87SYuri Pankov {
739*260e9a87SYuri Pankov 	const char	 synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS";
740*260e9a87SYuri Pankov 	const char	 synr[] = "SYNOPSIS";
741*260e9a87SYuri Pankov 
742*260e9a87SYuri Pankov 	FILE		*stream;
743*260e9a87SYuri Pankov 	const char	*syscall;
744*260e9a87SYuri Pankov 	char		*line;
745*260e9a87SYuri Pankov 	size_t		 len, off;
746*260e9a87SYuri Pankov 	ssize_t		 nw;
747*260e9a87SYuri Pankov 	int		 print;
748*260e9a87SYuri Pankov 
749*260e9a87SYuri Pankov 	fflush(stdout);
750*260e9a87SYuri Pankov 
751*260e9a87SYuri Pankov 	if ((stream = fdopen(fd, "r")) == NULL) {
752*260e9a87SYuri Pankov 		close(fd);
753*260e9a87SYuri Pankov 		syscall = "fdopen";
754*260e9a87SYuri Pankov 		goto fail;
755*260e9a87SYuri Pankov 	}
756*260e9a87SYuri Pankov 
757*260e9a87SYuri Pankov 	print = 0;
758*260e9a87SYuri Pankov 	while ((line = fgetln(stream, &len)) != NULL) {
759*260e9a87SYuri Pankov 		if (synopsis_only) {
760*260e9a87SYuri Pankov 			if (print) {
761*260e9a87SYuri Pankov 				if ( ! isspace((unsigned char)*line))
762*260e9a87SYuri Pankov 					goto done;
763*260e9a87SYuri Pankov 				while (len &&
764*260e9a87SYuri Pankov 				    isspace((unsigned char)*line)) {
765*260e9a87SYuri Pankov 					line++;
766*260e9a87SYuri Pankov 					len--;
767*260e9a87SYuri Pankov 				}
768*260e9a87SYuri Pankov 			} else {
769*260e9a87SYuri Pankov 				if ((len == sizeof(synb) &&
770*260e9a87SYuri Pankov 				     ! strncmp(line, synb, len - 1)) ||
771*260e9a87SYuri Pankov 				    (len == sizeof(synr) &&
772*260e9a87SYuri Pankov 				     ! strncmp(line, synr, len - 1)))
773*260e9a87SYuri Pankov 					print = 1;
774*260e9a87SYuri Pankov 				continue;
775*260e9a87SYuri Pankov 			}
776*260e9a87SYuri Pankov 		}
777*260e9a87SYuri Pankov 		for (off = 0; off < len; off += nw)
778*260e9a87SYuri Pankov 			if ((nw = write(STDOUT_FILENO, line + off,
779*260e9a87SYuri Pankov 			    len - off)) == -1 || nw == 0) {
780*260e9a87SYuri Pankov 				fclose(stream);
781*260e9a87SYuri Pankov 				syscall = "write";
782*260e9a87SYuri Pankov 				goto fail;
783*260e9a87SYuri Pankov 			}
784*260e9a87SYuri Pankov 	}
785*260e9a87SYuri Pankov 
786*260e9a87SYuri Pankov 	if (ferror(stream)) {
787*260e9a87SYuri Pankov 		fclose(stream);
788*260e9a87SYuri Pankov 		syscall = "fgetln";
789*260e9a87SYuri Pankov 		goto fail;
790*260e9a87SYuri Pankov 	}
791*260e9a87SYuri Pankov 
792*260e9a87SYuri Pankov done:
793*260e9a87SYuri Pankov 	fclose(stream);
794*260e9a87SYuri Pankov 	return(MANDOCLEVEL_OK);
795*260e9a87SYuri Pankov 
796*260e9a87SYuri Pankov fail:
797*260e9a87SYuri Pankov 	fprintf(stderr, "%s: %s: SYSERR: %s: %s",
798*260e9a87SYuri Pankov 	    progname, file, syscall, strerror(errno));
799*260e9a87SYuri Pankov 	return(MANDOCLEVEL_SYSERR);
800*260e9a87SYuri Pankov }
801*260e9a87SYuri Pankov 
802*260e9a87SYuri Pankov static int
803*260e9a87SYuri Pankov koptions(int *options, char *arg)
804*260e9a87SYuri Pankov {
805*260e9a87SYuri Pankov 
806*260e9a87SYuri Pankov 	if ( ! strcmp(arg, "utf-8")) {
807*260e9a87SYuri Pankov 		*options |=  MPARSE_UTF8;
808*260e9a87SYuri Pankov 		*options &= ~MPARSE_LATIN1;
809*260e9a87SYuri Pankov 	} else if ( ! strcmp(arg, "iso-8859-1")) {
810*260e9a87SYuri Pankov 		*options |=  MPARSE_LATIN1;
811*260e9a87SYuri Pankov 		*options &= ~MPARSE_UTF8;
812*260e9a87SYuri Pankov 	} else if ( ! strcmp(arg, "us-ascii")) {
813*260e9a87SYuri Pankov 		*options &= ~(MPARSE_UTF8 | MPARSE_LATIN1);
814*260e9a87SYuri Pankov 	} else {
815*260e9a87SYuri Pankov 		fprintf(stderr, "%s: -K %s: Bad argument\n",
816*260e9a87SYuri Pankov 		    progname, arg);
817*260e9a87SYuri Pankov 		return(0);
818*260e9a87SYuri Pankov 	}
819*260e9a87SYuri Pankov 	return(1);
820*260e9a87SYuri Pankov }
821*260e9a87SYuri Pankov 
82295c635efSGarrett D'Amore static int
823*260e9a87SYuri Pankov moptions(int *options, char *arg)
82495c635efSGarrett D'Amore {
82595c635efSGarrett D'Amore 
826*260e9a87SYuri Pankov 	if (arg == NULL)
827*260e9a87SYuri Pankov 		/* nothing to do */;
828*260e9a87SYuri Pankov 	else if (0 == strcmp(arg, "doc"))
829*260e9a87SYuri Pankov 		*options |= MPARSE_MDOC;
83095c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "andoc"))
831*260e9a87SYuri Pankov 		/* nothing to do */;
83295c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "an"))
833*260e9a87SYuri Pankov 		*options |= MPARSE_MAN;
83495c635efSGarrett D'Amore 	else {
835*260e9a87SYuri Pankov 		fprintf(stderr, "%s: -m %s: Bad argument\n",
836*260e9a87SYuri Pankov 		    progname, arg);
83795c635efSGarrett D'Amore 		return(0);
83895c635efSGarrett D'Amore 	}
83995c635efSGarrett D'Amore 
84095c635efSGarrett D'Amore 	return(1);
84195c635efSGarrett D'Amore }
84295c635efSGarrett D'Amore 
84395c635efSGarrett D'Amore static int
84495c635efSGarrett D'Amore toptions(struct curparse *curp, char *arg)
84595c635efSGarrett D'Amore {
84695c635efSGarrett D'Amore 
84795c635efSGarrett D'Amore 	if (0 == strcmp(arg, "ascii"))
84895c635efSGarrett D'Amore 		curp->outtype = OUTT_ASCII;
84995c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "lint")) {
85095c635efSGarrett D'Amore 		curp->outtype = OUTT_LINT;
85195c635efSGarrett D'Amore 		curp->wlevel  = MANDOCLEVEL_WARNING;
85295c635efSGarrett D'Amore 	} else if (0 == strcmp(arg, "tree"))
85395c635efSGarrett D'Amore 		curp->outtype = OUTT_TREE;
85495c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "man"))
85595c635efSGarrett D'Amore 		curp->outtype = OUTT_MAN;
85695c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "html"))
85795c635efSGarrett D'Amore 		curp->outtype = OUTT_HTML;
85895c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "utf8"))
85995c635efSGarrett D'Amore 		curp->outtype = OUTT_UTF8;
86095c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "locale"))
86195c635efSGarrett D'Amore 		curp->outtype = OUTT_LOCALE;
86295c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "xhtml"))
863*260e9a87SYuri Pankov 		curp->outtype = OUTT_HTML;
86495c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "ps"))
86595c635efSGarrett D'Amore 		curp->outtype = OUTT_PS;
86695c635efSGarrett D'Amore 	else if (0 == strcmp(arg, "pdf"))
86795c635efSGarrett D'Amore 		curp->outtype = OUTT_PDF;
86895c635efSGarrett D'Amore 	else {
869*260e9a87SYuri Pankov 		fprintf(stderr, "%s: -T %s: Bad argument\n",
870*260e9a87SYuri Pankov 		    progname, arg);
87195c635efSGarrett D'Amore 		return(0);
87295c635efSGarrett D'Amore 	}
87395c635efSGarrett D'Amore 
87495c635efSGarrett D'Amore 	return(1);
87595c635efSGarrett D'Amore }
87695c635efSGarrett D'Amore 
87795c635efSGarrett D'Amore static int
87895c635efSGarrett D'Amore woptions(struct curparse *curp, char *arg)
87995c635efSGarrett D'Amore {
88095c635efSGarrett D'Amore 	char		*v, *o;
881*260e9a87SYuri Pankov 	const char	*toks[7];
88295c635efSGarrett D'Amore 
88395c635efSGarrett D'Amore 	toks[0] = "stop";
88495c635efSGarrett D'Amore 	toks[1] = "all";
88595c635efSGarrett D'Amore 	toks[2] = "warning";
88695c635efSGarrett D'Amore 	toks[3] = "error";
887*260e9a87SYuri Pankov 	toks[4] = "unsupp";
888*260e9a87SYuri Pankov 	toks[5] = "fatal";
889*260e9a87SYuri Pankov 	toks[6] = NULL;
89095c635efSGarrett D'Amore 
89195c635efSGarrett D'Amore 	while (*arg) {
89295c635efSGarrett D'Amore 		o = arg;
89395c635efSGarrett D'Amore 		switch (getsubopt(&arg, UNCONST(toks), &v)) {
894*260e9a87SYuri Pankov 		case 0:
89595c635efSGarrett D'Amore 			curp->wstop = 1;
89695c635efSGarrett D'Amore 			break;
897*260e9a87SYuri Pankov 		case 1:
89895c635efSGarrett D'Amore 			/* FALLTHROUGH */
899*260e9a87SYuri Pankov 		case 2:
90095c635efSGarrett D'Amore 			curp->wlevel = MANDOCLEVEL_WARNING;
90195c635efSGarrett D'Amore 			break;
902*260e9a87SYuri Pankov 		case 3:
90395c635efSGarrett D'Amore 			curp->wlevel = MANDOCLEVEL_ERROR;
90495c635efSGarrett D'Amore 			break;
905*260e9a87SYuri Pankov 		case 4:
906*260e9a87SYuri Pankov 			curp->wlevel = MANDOCLEVEL_UNSUPP;
907*260e9a87SYuri Pankov 			break;
908*260e9a87SYuri Pankov 		case 5:
909*260e9a87SYuri Pankov 			curp->wlevel = MANDOCLEVEL_BADARG;
91095c635efSGarrett D'Amore 			break;
91195c635efSGarrett D'Amore 		default:
912*260e9a87SYuri Pankov 			fprintf(stderr, "%s: -W %s: Bad argument\n",
913*260e9a87SYuri Pankov 			    progname, o);
91495c635efSGarrett D'Amore 			return(0);
91595c635efSGarrett D'Amore 		}
91695c635efSGarrett D'Amore 	}
91795c635efSGarrett D'Amore 
91895c635efSGarrett D'Amore 	return(1);
91995c635efSGarrett D'Amore }
92095c635efSGarrett D'Amore 
92195c635efSGarrett D'Amore static void
922*260e9a87SYuri Pankov mmsg(enum mandocerr t, enum mandoclevel lvl,
92395c635efSGarrett D'Amore 		const char *file, int line, int col, const char *msg)
92495c635efSGarrett D'Amore {
925*260e9a87SYuri Pankov 	const char	*mparse_msg;
926*260e9a87SYuri Pankov 
927*260e9a87SYuri Pankov 	fprintf(stderr, "%s: %s:", progname, file);
928*260e9a87SYuri Pankov 
929*260e9a87SYuri Pankov 	if (line)
930*260e9a87SYuri Pankov 		fprintf(stderr, "%d:%d:", line, col + 1);
93195c635efSGarrett D'Amore 
932*260e9a87SYuri Pankov 	fprintf(stderr, " %s", mparse_strlevel(lvl));
933*260e9a87SYuri Pankov 
934*260e9a87SYuri Pankov 	if (NULL != (mparse_msg = mparse_strerror(t)))
935*260e9a87SYuri Pankov 		fprintf(stderr, ": %s", mparse_msg);
93695c635efSGarrett D'Amore 
93795c635efSGarrett D'Amore 	if (msg)
93895c635efSGarrett D'Amore 		fprintf(stderr, ": %s", msg);
93995c635efSGarrett D'Amore 
94095c635efSGarrett D'Amore 	fputc('\n', stderr);
94195c635efSGarrett D'Amore }
942*260e9a87SYuri Pankov 
943*260e9a87SYuri Pankov static pid_t
944*260e9a87SYuri Pankov spawn_pager(void)
945*260e9a87SYuri Pankov {
946*260e9a87SYuri Pankov #define MAX_PAGER_ARGS 16
947*260e9a87SYuri Pankov 	char		*argv[MAX_PAGER_ARGS];
948*260e9a87SYuri Pankov 	const char	*pager;
949*260e9a87SYuri Pankov 	char		*cp;
950*260e9a87SYuri Pankov 	int		 fildes[2];
951*260e9a87SYuri Pankov 	int		 argc;
952*260e9a87SYuri Pankov 	pid_t		 pager_pid;
953*260e9a87SYuri Pankov 
954*260e9a87SYuri Pankov 	if (pipe(fildes) == -1) {
955*260e9a87SYuri Pankov 		fprintf(stderr, "%s: pipe: %s\n",
956*260e9a87SYuri Pankov 		    progname, strerror(errno));
957*260e9a87SYuri Pankov 		return(0);
958*260e9a87SYuri Pankov 	}
959*260e9a87SYuri Pankov 
960*260e9a87SYuri Pankov 	switch (pager_pid = fork()) {
961*260e9a87SYuri Pankov 	case -1:
962*260e9a87SYuri Pankov 		fprintf(stderr, "%s: fork: %s\n",
963*260e9a87SYuri Pankov 		    progname, strerror(errno));
964*260e9a87SYuri Pankov 		exit((int)MANDOCLEVEL_SYSERR);
965*260e9a87SYuri Pankov 	case 0:
966*260e9a87SYuri Pankov 		break;
967*260e9a87SYuri Pankov 	default:
968*260e9a87SYuri Pankov 		close(fildes[0]);
969*260e9a87SYuri Pankov 		if (dup2(fildes[1], STDOUT_FILENO) == -1) {
970*260e9a87SYuri Pankov 			fprintf(stderr, "%s: dup output: %s\n",
971*260e9a87SYuri Pankov 			    progname, strerror(errno));
972*260e9a87SYuri Pankov 			exit((int)MANDOCLEVEL_SYSERR);
973*260e9a87SYuri Pankov 		}
974*260e9a87SYuri Pankov 		close(fildes[1]);
975*260e9a87SYuri Pankov 		return(pager_pid);
976*260e9a87SYuri Pankov 	}
977*260e9a87SYuri Pankov 
978*260e9a87SYuri Pankov 	/* The child process becomes the pager. */
979*260e9a87SYuri Pankov 
980*260e9a87SYuri Pankov 	close(fildes[1]);
981*260e9a87SYuri Pankov 	if (dup2(fildes[0], STDIN_FILENO) == -1) {
982*260e9a87SYuri Pankov 		fprintf(stderr, "%s: dup input: %s\n",
983*260e9a87SYuri Pankov 		    progname, strerror(errno));
984*260e9a87SYuri Pankov 		exit((int)MANDOCLEVEL_SYSERR);
985*260e9a87SYuri Pankov 	}
986*260e9a87SYuri Pankov 	close(fildes[0]);
987*260e9a87SYuri Pankov 
988*260e9a87SYuri Pankov 	pager = getenv("MANPAGER");
989*260e9a87SYuri Pankov 	if (pager == NULL || *pager == '\0')
990*260e9a87SYuri Pankov 		pager = getenv("PAGER");
991*260e9a87SYuri Pankov 	if (pager == NULL || *pager == '\0')
992*260e9a87SYuri Pankov 		pager = "/usr/bin/more -s";
993*260e9a87SYuri Pankov 	cp = mandoc_strdup(pager);
994*260e9a87SYuri Pankov 
995*260e9a87SYuri Pankov 	/*
996*260e9a87SYuri Pankov 	 * Parse the pager command into words.
997*260e9a87SYuri Pankov 	 * Intentionally do not do anything fancy here.
998*260e9a87SYuri Pankov 	 */
999*260e9a87SYuri Pankov 
1000*260e9a87SYuri Pankov 	argc = 0;
1001*260e9a87SYuri Pankov 	while (argc + 1 < MAX_PAGER_ARGS) {
1002*260e9a87SYuri Pankov 		argv[argc++] = cp;
1003*260e9a87SYuri Pankov 		cp = strchr(cp, ' ');
1004*260e9a87SYuri Pankov 		if (cp == NULL)
1005*260e9a87SYuri Pankov 			break;
1006*260e9a87SYuri Pankov 		*cp++ = '\0';
1007*260e9a87SYuri Pankov 		while (*cp == ' ')
1008*260e9a87SYuri Pankov 			cp++;
1009*260e9a87SYuri Pankov 		if (*cp == '\0')
1010*260e9a87SYuri Pankov 			break;
1011*260e9a87SYuri Pankov 	}
1012*260e9a87SYuri Pankov 	argv[argc] = NULL;
1013*260e9a87SYuri Pankov 
1014*260e9a87SYuri Pankov 	/* Hand over to the pager. */
1015*260e9a87SYuri Pankov 
1016*260e9a87SYuri Pankov 	execvp(argv[0], argv);
1017*260e9a87SYuri Pankov 	fprintf(stderr, "%s: exec: %s\n",
1018*260e9a87SYuri Pankov 	    progname, strerror(errno));
1019*260e9a87SYuri Pankov 	exit((int)MANDOCLEVEL_SYSERR);
1020*260e9a87SYuri Pankov }
1021