xref: /freebsd/contrib/mandoc/main.c (revision 61d06d6bd19dafe8ea971dd43e8328fa1b473456)
1*61d06d6bSBaptiste Daroussin /*	$Id: main.c,v 1.306 2018/05/14 14:10:23 schwarze Exp $ */
2*61d06d6bSBaptiste Daroussin /*
3*61d06d6bSBaptiste Daroussin  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
4*61d06d6bSBaptiste Daroussin  * Copyright (c) 2010-2012, 2014-2018 Ingo Schwarze <schwarze@openbsd.org>
5*61d06d6bSBaptiste Daroussin  * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
6*61d06d6bSBaptiste Daroussin  *
7*61d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
8*61d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
9*61d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
10*61d06d6bSBaptiste Daroussin  *
11*61d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
12*61d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*61d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
14*61d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15*61d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16*61d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17*61d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*61d06d6bSBaptiste Daroussin  */
19*61d06d6bSBaptiste Daroussin #include "config.h"
20*61d06d6bSBaptiste Daroussin 
21*61d06d6bSBaptiste Daroussin #include <sys/types.h>
22*61d06d6bSBaptiste Daroussin #include <sys/ioctl.h>
23*61d06d6bSBaptiste Daroussin #include <sys/param.h>	/* MACHINE */
24*61d06d6bSBaptiste Daroussin #include <sys/wait.h>
25*61d06d6bSBaptiste Daroussin 
26*61d06d6bSBaptiste Daroussin #include <assert.h>
27*61d06d6bSBaptiste Daroussin #include <ctype.h>
28*61d06d6bSBaptiste Daroussin #if HAVE_ERR
29*61d06d6bSBaptiste Daroussin #include <err.h>
30*61d06d6bSBaptiste Daroussin #endif
31*61d06d6bSBaptiste Daroussin #include <errno.h>
32*61d06d6bSBaptiste Daroussin #include <fcntl.h>
33*61d06d6bSBaptiste Daroussin #include <glob.h>
34*61d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT
35*61d06d6bSBaptiste Daroussin #include <sandbox.h>
36*61d06d6bSBaptiste Daroussin #endif
37*61d06d6bSBaptiste Daroussin #include <signal.h>
38*61d06d6bSBaptiste Daroussin #include <stdio.h>
39*61d06d6bSBaptiste Daroussin #include <stdint.h>
40*61d06d6bSBaptiste Daroussin #include <stdlib.h>
41*61d06d6bSBaptiste Daroussin #include <string.h>
42*61d06d6bSBaptiste Daroussin #include <termios.h>
43*61d06d6bSBaptiste Daroussin #include <time.h>
44*61d06d6bSBaptiste Daroussin #include <unistd.h>
45*61d06d6bSBaptiste Daroussin 
46*61d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
47*61d06d6bSBaptiste Daroussin #include "mandoc.h"
48*61d06d6bSBaptiste Daroussin #include "mandoc_xr.h"
49*61d06d6bSBaptiste Daroussin #include "roff.h"
50*61d06d6bSBaptiste Daroussin #include "mdoc.h"
51*61d06d6bSBaptiste Daroussin #include "man.h"
52*61d06d6bSBaptiste Daroussin #include "tag.h"
53*61d06d6bSBaptiste Daroussin #include "main.h"
54*61d06d6bSBaptiste Daroussin #include "manconf.h"
55*61d06d6bSBaptiste Daroussin #include "mansearch.h"
56*61d06d6bSBaptiste Daroussin 
57*61d06d6bSBaptiste Daroussin enum	outmode {
58*61d06d6bSBaptiste Daroussin 	OUTMODE_DEF = 0,
59*61d06d6bSBaptiste Daroussin 	OUTMODE_FLN,
60*61d06d6bSBaptiste Daroussin 	OUTMODE_LST,
61*61d06d6bSBaptiste Daroussin 	OUTMODE_ALL,
62*61d06d6bSBaptiste Daroussin 	OUTMODE_ONE
63*61d06d6bSBaptiste Daroussin };
64*61d06d6bSBaptiste Daroussin 
65*61d06d6bSBaptiste Daroussin enum	outt {
66*61d06d6bSBaptiste Daroussin 	OUTT_ASCII = 0,	/* -Tascii */
67*61d06d6bSBaptiste Daroussin 	OUTT_LOCALE,	/* -Tlocale */
68*61d06d6bSBaptiste Daroussin 	OUTT_UTF8,	/* -Tutf8 */
69*61d06d6bSBaptiste Daroussin 	OUTT_TREE,	/* -Ttree */
70*61d06d6bSBaptiste Daroussin 	OUTT_MAN,	/* -Tman */
71*61d06d6bSBaptiste Daroussin 	OUTT_HTML,	/* -Thtml */
72*61d06d6bSBaptiste Daroussin 	OUTT_MARKDOWN,	/* -Tmarkdown */
73*61d06d6bSBaptiste Daroussin 	OUTT_LINT,	/* -Tlint */
74*61d06d6bSBaptiste Daroussin 	OUTT_PS,	/* -Tps */
75*61d06d6bSBaptiste Daroussin 	OUTT_PDF	/* -Tpdf */
76*61d06d6bSBaptiste Daroussin };
77*61d06d6bSBaptiste Daroussin 
78*61d06d6bSBaptiste Daroussin struct	curparse {
79*61d06d6bSBaptiste Daroussin 	struct mparse	 *mp;
80*61d06d6bSBaptiste Daroussin 	struct manoutput *outopts;	/* output options */
81*61d06d6bSBaptiste Daroussin 	void		 *outdata;	/* data for output */
82*61d06d6bSBaptiste Daroussin 	char		 *os_s;		/* operating system for display */
83*61d06d6bSBaptiste Daroussin 	int		  wstop;	/* stop after a file with a warning */
84*61d06d6bSBaptiste Daroussin 	enum mandocerr	  mmin;		/* ignore messages below this */
85*61d06d6bSBaptiste Daroussin 	enum mandoc_os	  os_e;		/* check base system conventions */
86*61d06d6bSBaptiste Daroussin 	enum outt	  outtype;	/* which output to use */
87*61d06d6bSBaptiste Daroussin };
88*61d06d6bSBaptiste Daroussin 
89*61d06d6bSBaptiste Daroussin 
90*61d06d6bSBaptiste Daroussin int			  mandocdb(int, char *[]);
91*61d06d6bSBaptiste Daroussin 
92*61d06d6bSBaptiste Daroussin static	void		  check_xr(const char *);
93*61d06d6bSBaptiste Daroussin static	int		  fs_lookup(const struct manpaths *,
94*61d06d6bSBaptiste Daroussin 				size_t ipath, const char *,
95*61d06d6bSBaptiste Daroussin 				const char *, const char *,
96*61d06d6bSBaptiste Daroussin 				struct manpage **, size_t *);
97*61d06d6bSBaptiste Daroussin static	int		  fs_search(const struct mansearch *,
98*61d06d6bSBaptiste Daroussin 				const struct manpaths *, int, char**,
99*61d06d6bSBaptiste Daroussin 				struct manpage **, size_t *);
100*61d06d6bSBaptiste Daroussin static	int		  koptions(int *, char *);
101*61d06d6bSBaptiste Daroussin static	void		  moptions(int *, char *);
102*61d06d6bSBaptiste Daroussin static	void		  mmsg(enum mandocerr, enum mandoclevel,
103*61d06d6bSBaptiste Daroussin 				const char *, int, int, const char *);
104*61d06d6bSBaptiste Daroussin static	void		  outdata_alloc(struct curparse *);
105*61d06d6bSBaptiste Daroussin static	void		  parse(struct curparse *, int, const char *);
106*61d06d6bSBaptiste Daroussin static	void		  passthrough(const char *, int, int);
107*61d06d6bSBaptiste Daroussin static	pid_t		  spawn_pager(struct tag_files *);
108*61d06d6bSBaptiste Daroussin static	int		  toptions(struct curparse *, char *);
109*61d06d6bSBaptiste Daroussin static	void		  usage(enum argmode) __attribute__((__noreturn__));
110*61d06d6bSBaptiste Daroussin static	int		  woptions(struct curparse *, char *);
111*61d06d6bSBaptiste Daroussin 
112*61d06d6bSBaptiste Daroussin static	const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
113*61d06d6bSBaptiste Daroussin static	char		  help_arg[] = "help";
114*61d06d6bSBaptiste Daroussin static	char		 *help_argv[] = {help_arg, NULL};
115*61d06d6bSBaptiste Daroussin static	enum mandoclevel  rc;
116*61d06d6bSBaptiste Daroussin static	FILE		 *mmsg_stream;
117*61d06d6bSBaptiste Daroussin 
118*61d06d6bSBaptiste Daroussin 
119*61d06d6bSBaptiste Daroussin int
120*61d06d6bSBaptiste Daroussin main(int argc, char *argv[])
121*61d06d6bSBaptiste Daroussin {
122*61d06d6bSBaptiste Daroussin 	struct manconf	 conf;
123*61d06d6bSBaptiste Daroussin 	struct mansearch search;
124*61d06d6bSBaptiste Daroussin 	struct curparse	 curp;
125*61d06d6bSBaptiste Daroussin 	struct winsize	 ws;
126*61d06d6bSBaptiste Daroussin 	struct tag_files *tag_files;
127*61d06d6bSBaptiste Daroussin 	struct manpage	*res, *resp;
128*61d06d6bSBaptiste Daroussin 	const char	*progname, *sec, *thisarg;
129*61d06d6bSBaptiste Daroussin 	char		*conf_file, *defpaths, *auxpaths;
130*61d06d6bSBaptiste Daroussin 	char		*oarg;
131*61d06d6bSBaptiste Daroussin 	unsigned char	*uc;
132*61d06d6bSBaptiste Daroussin 	size_t		 i, sz;
133*61d06d6bSBaptiste Daroussin 	int		 prio, best_prio;
134*61d06d6bSBaptiste Daroussin 	enum outmode	 outmode;
135*61d06d6bSBaptiste Daroussin 	int		 fd, startdir;
136*61d06d6bSBaptiste Daroussin 	int		 show_usage;
137*61d06d6bSBaptiste Daroussin 	int		 options;
138*61d06d6bSBaptiste Daroussin 	int		 use_pager;
139*61d06d6bSBaptiste Daroussin 	int		 status, signum;
140*61d06d6bSBaptiste Daroussin 	int		 c;
141*61d06d6bSBaptiste Daroussin 	pid_t		 pager_pid, tc_pgid, man_pgid, pid;
142*61d06d6bSBaptiste Daroussin 
143*61d06d6bSBaptiste Daroussin #if HAVE_PROGNAME
144*61d06d6bSBaptiste Daroussin 	progname = getprogname();
145*61d06d6bSBaptiste Daroussin #else
146*61d06d6bSBaptiste Daroussin 	if (argc < 1)
147*61d06d6bSBaptiste Daroussin 		progname = mandoc_strdup("mandoc");
148*61d06d6bSBaptiste Daroussin 	else if ((progname = strrchr(argv[0], '/')) == NULL)
149*61d06d6bSBaptiste Daroussin 		progname = argv[0];
150*61d06d6bSBaptiste Daroussin 	else
151*61d06d6bSBaptiste Daroussin 		++progname;
152*61d06d6bSBaptiste Daroussin 	setprogname(progname);
153*61d06d6bSBaptiste Daroussin #endif
154*61d06d6bSBaptiste Daroussin 
155*61d06d6bSBaptiste Daroussin 	if (strncmp(progname, "mandocdb", 8) == 0 ||
156*61d06d6bSBaptiste Daroussin 	    strcmp(progname, BINM_MAKEWHATIS) == 0)
157*61d06d6bSBaptiste Daroussin 		return mandocdb(argc, argv);
158*61d06d6bSBaptiste Daroussin 
159*61d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
160*61d06d6bSBaptiste Daroussin 	if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1)
161*61d06d6bSBaptiste Daroussin 		err((int)MANDOCLEVEL_SYSERR, "pledge");
162*61d06d6bSBaptiste Daroussin #endif
163*61d06d6bSBaptiste Daroussin 
164*61d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT
165*61d06d6bSBaptiste Daroussin 	if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1)
166*61d06d6bSBaptiste Daroussin 		errx((int)MANDOCLEVEL_SYSERR, "sandbox_init");
167*61d06d6bSBaptiste Daroussin #endif
168*61d06d6bSBaptiste Daroussin 
169*61d06d6bSBaptiste Daroussin 	/* Search options. */
170*61d06d6bSBaptiste Daroussin 
171*61d06d6bSBaptiste Daroussin 	memset(&conf, 0, sizeof(conf));
172*61d06d6bSBaptiste Daroussin 	conf_file = defpaths = NULL;
173*61d06d6bSBaptiste Daroussin 	auxpaths = NULL;
174*61d06d6bSBaptiste Daroussin 
175*61d06d6bSBaptiste Daroussin 	memset(&search, 0, sizeof(struct mansearch));
176*61d06d6bSBaptiste Daroussin 	search.outkey = "Nd";
177*61d06d6bSBaptiste Daroussin 	oarg = NULL;
178*61d06d6bSBaptiste Daroussin 
179*61d06d6bSBaptiste Daroussin 	if (strcmp(progname, BINM_MAN) == 0)
180*61d06d6bSBaptiste Daroussin 		search.argmode = ARG_NAME;
181*61d06d6bSBaptiste Daroussin 	else if (strcmp(progname, BINM_APROPOS) == 0)
182*61d06d6bSBaptiste Daroussin 		search.argmode = ARG_EXPR;
183*61d06d6bSBaptiste Daroussin 	else if (strcmp(progname, BINM_WHATIS) == 0)
184*61d06d6bSBaptiste Daroussin 		search.argmode = ARG_WORD;
185*61d06d6bSBaptiste Daroussin 	else if (strncmp(progname, "help", 4) == 0)
186*61d06d6bSBaptiste Daroussin 		search.argmode = ARG_NAME;
187*61d06d6bSBaptiste Daroussin 	else
188*61d06d6bSBaptiste Daroussin 		search.argmode = ARG_FILE;
189*61d06d6bSBaptiste Daroussin 
190*61d06d6bSBaptiste Daroussin 	/* Parser and formatter options. */
191*61d06d6bSBaptiste Daroussin 
192*61d06d6bSBaptiste Daroussin 	memset(&curp, 0, sizeof(struct curparse));
193*61d06d6bSBaptiste Daroussin 	curp.outtype = OUTT_LOCALE;
194*61d06d6bSBaptiste Daroussin 	curp.mmin = MANDOCERR_MAX;
195*61d06d6bSBaptiste Daroussin 	curp.outopts = &conf.output;
196*61d06d6bSBaptiste Daroussin 	options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1;
197*61d06d6bSBaptiste Daroussin 	mmsg_stream = stderr;
198*61d06d6bSBaptiste Daroussin 
199*61d06d6bSBaptiste Daroussin 	use_pager = 1;
200*61d06d6bSBaptiste Daroussin 	tag_files = NULL;
201*61d06d6bSBaptiste Daroussin 	show_usage = 0;
202*61d06d6bSBaptiste Daroussin 	outmode = OUTMODE_DEF;
203*61d06d6bSBaptiste Daroussin 
204*61d06d6bSBaptiste Daroussin 	while ((c = getopt(argc, argv,
205*61d06d6bSBaptiste Daroussin 	    "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) {
206*61d06d6bSBaptiste Daroussin 		if (c == 'i' && search.argmode == ARG_EXPR) {
207*61d06d6bSBaptiste Daroussin 			optind--;
208*61d06d6bSBaptiste Daroussin 			break;
209*61d06d6bSBaptiste Daroussin 		}
210*61d06d6bSBaptiste Daroussin 		switch (c) {
211*61d06d6bSBaptiste Daroussin 		case 'a':
212*61d06d6bSBaptiste Daroussin 			outmode = OUTMODE_ALL;
213*61d06d6bSBaptiste Daroussin 			break;
214*61d06d6bSBaptiste Daroussin 		case 'C':
215*61d06d6bSBaptiste Daroussin 			conf_file = optarg;
216*61d06d6bSBaptiste Daroussin 			break;
217*61d06d6bSBaptiste Daroussin 		case 'c':
218*61d06d6bSBaptiste Daroussin 			use_pager = 0;
219*61d06d6bSBaptiste Daroussin 			break;
220*61d06d6bSBaptiste Daroussin 		case 'f':
221*61d06d6bSBaptiste Daroussin 			search.argmode = ARG_WORD;
222*61d06d6bSBaptiste Daroussin 			break;
223*61d06d6bSBaptiste Daroussin 		case 'h':
224*61d06d6bSBaptiste Daroussin 			conf.output.synopsisonly = 1;
225*61d06d6bSBaptiste Daroussin 			use_pager = 0;
226*61d06d6bSBaptiste Daroussin 			outmode = OUTMODE_ALL;
227*61d06d6bSBaptiste Daroussin 			break;
228*61d06d6bSBaptiste Daroussin 		case 'I':
229*61d06d6bSBaptiste Daroussin 			if (strncmp(optarg, "os=", 3)) {
230*61d06d6bSBaptiste Daroussin 				warnx("-I %s: Bad argument", optarg);
231*61d06d6bSBaptiste Daroussin 				return (int)MANDOCLEVEL_BADARG;
232*61d06d6bSBaptiste Daroussin 			}
233*61d06d6bSBaptiste Daroussin 			if (curp.os_s != NULL) {
234*61d06d6bSBaptiste Daroussin 				warnx("-I %s: Duplicate argument", optarg);
235*61d06d6bSBaptiste Daroussin 				return (int)MANDOCLEVEL_BADARG;
236*61d06d6bSBaptiste Daroussin 			}
237*61d06d6bSBaptiste Daroussin 			curp.os_s = mandoc_strdup(optarg + 3);
238*61d06d6bSBaptiste Daroussin 			break;
239*61d06d6bSBaptiste Daroussin 		case 'K':
240*61d06d6bSBaptiste Daroussin 			if ( ! koptions(&options, optarg))
241*61d06d6bSBaptiste Daroussin 				return (int)MANDOCLEVEL_BADARG;
242*61d06d6bSBaptiste Daroussin 			break;
243*61d06d6bSBaptiste Daroussin 		case 'k':
244*61d06d6bSBaptiste Daroussin 			search.argmode = ARG_EXPR;
245*61d06d6bSBaptiste Daroussin 			break;
246*61d06d6bSBaptiste Daroussin 		case 'l':
247*61d06d6bSBaptiste Daroussin 			search.argmode = ARG_FILE;
248*61d06d6bSBaptiste Daroussin 			outmode = OUTMODE_ALL;
249*61d06d6bSBaptiste Daroussin 			break;
250*61d06d6bSBaptiste Daroussin 		case 'M':
251*61d06d6bSBaptiste Daroussin 			defpaths = optarg;
252*61d06d6bSBaptiste Daroussin 			break;
253*61d06d6bSBaptiste Daroussin 		case 'm':
254*61d06d6bSBaptiste Daroussin 			auxpaths = optarg;
255*61d06d6bSBaptiste Daroussin 			break;
256*61d06d6bSBaptiste Daroussin 		case 'O':
257*61d06d6bSBaptiste Daroussin 			oarg = optarg;
258*61d06d6bSBaptiste Daroussin 			break;
259*61d06d6bSBaptiste Daroussin 		case 'S':
260*61d06d6bSBaptiste Daroussin 			search.arch = optarg;
261*61d06d6bSBaptiste Daroussin 			break;
262*61d06d6bSBaptiste Daroussin 		case 's':
263*61d06d6bSBaptiste Daroussin 			search.sec = optarg;
264*61d06d6bSBaptiste Daroussin 			break;
265*61d06d6bSBaptiste Daroussin 		case 'T':
266*61d06d6bSBaptiste Daroussin 			if ( ! toptions(&curp, optarg))
267*61d06d6bSBaptiste Daroussin 				return (int)MANDOCLEVEL_BADARG;
268*61d06d6bSBaptiste Daroussin 			break;
269*61d06d6bSBaptiste Daroussin 		case 'W':
270*61d06d6bSBaptiste Daroussin 			if ( ! woptions(&curp, optarg))
271*61d06d6bSBaptiste Daroussin 				return (int)MANDOCLEVEL_BADARG;
272*61d06d6bSBaptiste Daroussin 			break;
273*61d06d6bSBaptiste Daroussin 		case 'w':
274*61d06d6bSBaptiste Daroussin 			outmode = OUTMODE_FLN;
275*61d06d6bSBaptiste Daroussin 			break;
276*61d06d6bSBaptiste Daroussin 		default:
277*61d06d6bSBaptiste Daroussin 			show_usage = 1;
278*61d06d6bSBaptiste Daroussin 			break;
279*61d06d6bSBaptiste Daroussin 		}
280*61d06d6bSBaptiste Daroussin 	}
281*61d06d6bSBaptiste Daroussin 
282*61d06d6bSBaptiste Daroussin 	if (show_usage)
283*61d06d6bSBaptiste Daroussin 		usage(search.argmode);
284*61d06d6bSBaptiste Daroussin 
285*61d06d6bSBaptiste Daroussin 	/* Postprocess options. */
286*61d06d6bSBaptiste Daroussin 
287*61d06d6bSBaptiste Daroussin 	if (outmode == OUTMODE_DEF) {
288*61d06d6bSBaptiste Daroussin 		switch (search.argmode) {
289*61d06d6bSBaptiste Daroussin 		case ARG_FILE:
290*61d06d6bSBaptiste Daroussin 			outmode = OUTMODE_ALL;
291*61d06d6bSBaptiste Daroussin 			use_pager = 0;
292*61d06d6bSBaptiste Daroussin 			break;
293*61d06d6bSBaptiste Daroussin 		case ARG_NAME:
294*61d06d6bSBaptiste Daroussin 			outmode = OUTMODE_ONE;
295*61d06d6bSBaptiste Daroussin 			break;
296*61d06d6bSBaptiste Daroussin 		default:
297*61d06d6bSBaptiste Daroussin 			outmode = OUTMODE_LST;
298*61d06d6bSBaptiste Daroussin 			break;
299*61d06d6bSBaptiste Daroussin 		}
300*61d06d6bSBaptiste Daroussin 	}
301*61d06d6bSBaptiste Daroussin 
302*61d06d6bSBaptiste Daroussin 	if (oarg != NULL) {
303*61d06d6bSBaptiste Daroussin 		if (outmode == OUTMODE_LST)
304*61d06d6bSBaptiste Daroussin 			search.outkey = oarg;
305*61d06d6bSBaptiste Daroussin 		else {
306*61d06d6bSBaptiste Daroussin 			while (oarg != NULL) {
307*61d06d6bSBaptiste Daroussin 				thisarg = oarg;
308*61d06d6bSBaptiste Daroussin 				if (manconf_output(&conf.output,
309*61d06d6bSBaptiste Daroussin 				    strsep(&oarg, ","), 0) == 0)
310*61d06d6bSBaptiste Daroussin 					continue;
311*61d06d6bSBaptiste Daroussin 				warnx("-O %s: Bad argument", thisarg);
312*61d06d6bSBaptiste Daroussin 				return (int)MANDOCLEVEL_BADARG;
313*61d06d6bSBaptiste Daroussin 			}
314*61d06d6bSBaptiste Daroussin 		}
315*61d06d6bSBaptiste Daroussin 	}
316*61d06d6bSBaptiste Daroussin 
317*61d06d6bSBaptiste Daroussin 	if (outmode == OUTMODE_FLN ||
318*61d06d6bSBaptiste Daroussin 	    outmode == OUTMODE_LST ||
319*61d06d6bSBaptiste Daroussin 	    !isatty(STDOUT_FILENO))
320*61d06d6bSBaptiste Daroussin 		use_pager = 0;
321*61d06d6bSBaptiste Daroussin 
322*61d06d6bSBaptiste Daroussin 	if (use_pager &&
323*61d06d6bSBaptiste Daroussin 	    (conf.output.width == 0 || conf.output.indent == 0) &&
324*61d06d6bSBaptiste Daroussin 	    ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 &&
325*61d06d6bSBaptiste Daroussin 	    ws.ws_col > 1) {
326*61d06d6bSBaptiste Daroussin 		if (conf.output.width == 0 && ws.ws_col < 79)
327*61d06d6bSBaptiste Daroussin 			conf.output.width = ws.ws_col - 1;
328*61d06d6bSBaptiste Daroussin 		if (conf.output.indent == 0 && ws.ws_col < 66)
329*61d06d6bSBaptiste Daroussin 			conf.output.indent = 3;
330*61d06d6bSBaptiste Daroussin 	}
331*61d06d6bSBaptiste Daroussin 
332*61d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
333*61d06d6bSBaptiste Daroussin 	if (!use_pager)
334*61d06d6bSBaptiste Daroussin 		if (pledge("stdio rpath", NULL) == -1)
335*61d06d6bSBaptiste Daroussin 			err((int)MANDOCLEVEL_SYSERR, "pledge");
336*61d06d6bSBaptiste Daroussin #endif
337*61d06d6bSBaptiste Daroussin 
338*61d06d6bSBaptiste Daroussin 	/* Parse arguments. */
339*61d06d6bSBaptiste Daroussin 
340*61d06d6bSBaptiste Daroussin 	if (argc > 0) {
341*61d06d6bSBaptiste Daroussin 		argc -= optind;
342*61d06d6bSBaptiste Daroussin 		argv += optind;
343*61d06d6bSBaptiste Daroussin 	}
344*61d06d6bSBaptiste Daroussin 	resp = NULL;
345*61d06d6bSBaptiste Daroussin 
346*61d06d6bSBaptiste Daroussin 	/*
347*61d06d6bSBaptiste Daroussin 	 * Quirks for help(1)
348*61d06d6bSBaptiste Daroussin 	 * and for a man(1) section argument without -s.
349*61d06d6bSBaptiste Daroussin 	 */
350*61d06d6bSBaptiste Daroussin 
351*61d06d6bSBaptiste Daroussin 	if (search.argmode == ARG_NAME) {
352*61d06d6bSBaptiste Daroussin 		if (*progname == 'h') {
353*61d06d6bSBaptiste Daroussin 			if (argc == 0) {
354*61d06d6bSBaptiste Daroussin 				argv = help_argv;
355*61d06d6bSBaptiste Daroussin 				argc = 1;
356*61d06d6bSBaptiste Daroussin 			}
357*61d06d6bSBaptiste Daroussin 		} else if (argc > 1 &&
358*61d06d6bSBaptiste Daroussin 		    ((uc = (unsigned char *)argv[0]) != NULL) &&
359*61d06d6bSBaptiste Daroussin 		    ((isdigit(uc[0]) && (uc[1] == '\0' ||
360*61d06d6bSBaptiste Daroussin 		      (isalpha(uc[1]) && uc[2] == '\0'))) ||
361*61d06d6bSBaptiste Daroussin 		     (uc[0] == 'n' && uc[1] == '\0'))) {
362*61d06d6bSBaptiste Daroussin 			search.sec = (char *)uc;
363*61d06d6bSBaptiste Daroussin 			argv++;
364*61d06d6bSBaptiste Daroussin 			argc--;
365*61d06d6bSBaptiste Daroussin 		}
366*61d06d6bSBaptiste Daroussin 		if (search.arch == NULL)
367*61d06d6bSBaptiste Daroussin 			search.arch = getenv("MACHINE");
368*61d06d6bSBaptiste Daroussin #ifdef MACHINE
369*61d06d6bSBaptiste Daroussin 		if (search.arch == NULL)
370*61d06d6bSBaptiste Daroussin 			search.arch = MACHINE;
371*61d06d6bSBaptiste Daroussin #endif
372*61d06d6bSBaptiste Daroussin 	}
373*61d06d6bSBaptiste Daroussin 
374*61d06d6bSBaptiste Daroussin 	rc = MANDOCLEVEL_OK;
375*61d06d6bSBaptiste Daroussin 
376*61d06d6bSBaptiste Daroussin 	/* man(1), whatis(1), apropos(1) */
377*61d06d6bSBaptiste Daroussin 
378*61d06d6bSBaptiste Daroussin 	if (search.argmode != ARG_FILE) {
379*61d06d6bSBaptiste Daroussin 		if (search.argmode == ARG_NAME &&
380*61d06d6bSBaptiste Daroussin 		    outmode == OUTMODE_ONE)
381*61d06d6bSBaptiste Daroussin 			search.firstmatch = 1;
382*61d06d6bSBaptiste Daroussin 
383*61d06d6bSBaptiste Daroussin 		/* Access the mandoc database. */
384*61d06d6bSBaptiste Daroussin 
385*61d06d6bSBaptiste Daroussin 		manconf_parse(&conf, conf_file, defpaths, auxpaths);
386*61d06d6bSBaptiste Daroussin 		if ( ! mansearch(&search, &conf.manpath,
387*61d06d6bSBaptiste Daroussin 		    argc, argv, &res, &sz))
388*61d06d6bSBaptiste Daroussin 			usage(search.argmode);
389*61d06d6bSBaptiste Daroussin 
390*61d06d6bSBaptiste Daroussin 		if (sz == 0 && search.argmode == ARG_NAME)
391*61d06d6bSBaptiste Daroussin 			fs_search(&search, &conf.manpath,
392*61d06d6bSBaptiste Daroussin 			    argc, argv, &res, &sz);
393*61d06d6bSBaptiste Daroussin 
394*61d06d6bSBaptiste Daroussin 		if (search.argmode == ARG_NAME) {
395*61d06d6bSBaptiste Daroussin 			for (c = 0; c < argc; c++) {
396*61d06d6bSBaptiste Daroussin 				if (strchr(argv[c], '/') == NULL)
397*61d06d6bSBaptiste Daroussin 					continue;
398*61d06d6bSBaptiste Daroussin 				if (access(argv[c], R_OK) == -1) {
399*61d06d6bSBaptiste Daroussin 					warn("%s", argv[c]);
400*61d06d6bSBaptiste Daroussin 					continue;
401*61d06d6bSBaptiste Daroussin 				}
402*61d06d6bSBaptiste Daroussin 				res = mandoc_reallocarray(res,
403*61d06d6bSBaptiste Daroussin 				    sz + 1, sizeof(*res));
404*61d06d6bSBaptiste Daroussin 				res[sz].file = mandoc_strdup(argv[c]);
405*61d06d6bSBaptiste Daroussin 				res[sz].names = NULL;
406*61d06d6bSBaptiste Daroussin 				res[sz].output = NULL;
407*61d06d6bSBaptiste Daroussin 				res[sz].ipath = SIZE_MAX;
408*61d06d6bSBaptiste Daroussin 				res[sz].bits = 0;
409*61d06d6bSBaptiste Daroussin 				res[sz].sec = 10;
410*61d06d6bSBaptiste Daroussin 				res[sz].form = FORM_SRC;
411*61d06d6bSBaptiste Daroussin 				sz++;
412*61d06d6bSBaptiste Daroussin 			}
413*61d06d6bSBaptiste Daroussin 		}
414*61d06d6bSBaptiste Daroussin 
415*61d06d6bSBaptiste Daroussin 		if (sz == 0) {
416*61d06d6bSBaptiste Daroussin 			if (search.argmode != ARG_NAME)
417*61d06d6bSBaptiste Daroussin 				warnx("nothing appropriate");
418*61d06d6bSBaptiste Daroussin 			rc = MANDOCLEVEL_BADARG;
419*61d06d6bSBaptiste Daroussin 			goto out;
420*61d06d6bSBaptiste Daroussin 		}
421*61d06d6bSBaptiste Daroussin 
422*61d06d6bSBaptiste Daroussin 		/*
423*61d06d6bSBaptiste Daroussin 		 * For standard man(1) and -a output mode,
424*61d06d6bSBaptiste Daroussin 		 * prepare for copying filename pointers
425*61d06d6bSBaptiste Daroussin 		 * into the program parameter array.
426*61d06d6bSBaptiste Daroussin 		 */
427*61d06d6bSBaptiste Daroussin 
428*61d06d6bSBaptiste Daroussin 		if (outmode == OUTMODE_ONE) {
429*61d06d6bSBaptiste Daroussin 			argc = 1;
430*61d06d6bSBaptiste Daroussin 			best_prio = 20;
431*61d06d6bSBaptiste Daroussin 		} else if (outmode == OUTMODE_ALL)
432*61d06d6bSBaptiste Daroussin 			argc = (int)sz;
433*61d06d6bSBaptiste Daroussin 
434*61d06d6bSBaptiste Daroussin 		/* Iterate all matching manuals. */
435*61d06d6bSBaptiste Daroussin 
436*61d06d6bSBaptiste Daroussin 		resp = res;
437*61d06d6bSBaptiste Daroussin 		for (i = 0; i < sz; i++) {
438*61d06d6bSBaptiste Daroussin 			if (outmode == OUTMODE_FLN)
439*61d06d6bSBaptiste Daroussin 				puts(res[i].file);
440*61d06d6bSBaptiste Daroussin 			else if (outmode == OUTMODE_LST)
441*61d06d6bSBaptiste Daroussin 				printf("%s - %s\n", res[i].names,
442*61d06d6bSBaptiste Daroussin 				    res[i].output == NULL ? "" :
443*61d06d6bSBaptiste Daroussin 				    res[i].output);
444*61d06d6bSBaptiste Daroussin 			else if (outmode == OUTMODE_ONE) {
445*61d06d6bSBaptiste Daroussin 				/* Search for the best section. */
446*61d06d6bSBaptiste Daroussin 				sec = res[i].file;
447*61d06d6bSBaptiste Daroussin 				sec += strcspn(sec, "123456789");
448*61d06d6bSBaptiste Daroussin 				if (sec[0] == '\0')
449*61d06d6bSBaptiste Daroussin 					continue;
450*61d06d6bSBaptiste Daroussin 				prio = sec_prios[sec[0] - '1'];
451*61d06d6bSBaptiste Daroussin 				if (sec[1] != '/')
452*61d06d6bSBaptiste Daroussin 					prio += 10;
453*61d06d6bSBaptiste Daroussin 				if (prio >= best_prio)
454*61d06d6bSBaptiste Daroussin 					continue;
455*61d06d6bSBaptiste Daroussin 				best_prio = prio;
456*61d06d6bSBaptiste Daroussin 				resp = res + i;
457*61d06d6bSBaptiste Daroussin 			}
458*61d06d6bSBaptiste Daroussin 		}
459*61d06d6bSBaptiste Daroussin 
460*61d06d6bSBaptiste Daroussin 		/*
461*61d06d6bSBaptiste Daroussin 		 * For man(1), -a and -i output mode, fall through
462*61d06d6bSBaptiste Daroussin 		 * to the main mandoc(1) code iterating files
463*61d06d6bSBaptiste Daroussin 		 * and running the parsers on each of them.
464*61d06d6bSBaptiste Daroussin 		 */
465*61d06d6bSBaptiste Daroussin 
466*61d06d6bSBaptiste Daroussin 		if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST)
467*61d06d6bSBaptiste Daroussin 			goto out;
468*61d06d6bSBaptiste Daroussin 	}
469*61d06d6bSBaptiste Daroussin 
470*61d06d6bSBaptiste Daroussin 	/* mandoc(1) */
471*61d06d6bSBaptiste Daroussin 
472*61d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
473*61d06d6bSBaptiste Daroussin 	if (use_pager) {
474*61d06d6bSBaptiste Daroussin 		if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1)
475*61d06d6bSBaptiste Daroussin 			err((int)MANDOCLEVEL_SYSERR, "pledge");
476*61d06d6bSBaptiste Daroussin 	} else {
477*61d06d6bSBaptiste Daroussin 		if (pledge("stdio rpath", NULL) == -1)
478*61d06d6bSBaptiste Daroussin 			err((int)MANDOCLEVEL_SYSERR, "pledge");
479*61d06d6bSBaptiste Daroussin 	}
480*61d06d6bSBaptiste Daroussin #endif
481*61d06d6bSBaptiste Daroussin 
482*61d06d6bSBaptiste Daroussin 	if (search.argmode == ARG_FILE)
483*61d06d6bSBaptiste Daroussin 		moptions(&options, auxpaths);
484*61d06d6bSBaptiste Daroussin 
485*61d06d6bSBaptiste Daroussin 	mchars_alloc();
486*61d06d6bSBaptiste Daroussin 	curp.mp = mparse_alloc(options, curp.mmin, mmsg,
487*61d06d6bSBaptiste Daroussin 	    curp.os_e, curp.os_s);
488*61d06d6bSBaptiste Daroussin 
489*61d06d6bSBaptiste Daroussin 	/*
490*61d06d6bSBaptiste Daroussin 	 * Conditionally start up the lookaside buffer before parsing.
491*61d06d6bSBaptiste Daroussin 	 */
492*61d06d6bSBaptiste Daroussin 	if (OUTT_MAN == curp.outtype)
493*61d06d6bSBaptiste Daroussin 		mparse_keep(curp.mp);
494*61d06d6bSBaptiste Daroussin 
495*61d06d6bSBaptiste Daroussin 	if (argc < 1) {
496*61d06d6bSBaptiste Daroussin 		if (use_pager)
497*61d06d6bSBaptiste Daroussin 			tag_files = tag_init();
498*61d06d6bSBaptiste Daroussin 		parse(&curp, STDIN_FILENO, "<stdin>");
499*61d06d6bSBaptiste Daroussin 	}
500*61d06d6bSBaptiste Daroussin 
501*61d06d6bSBaptiste Daroussin 	/*
502*61d06d6bSBaptiste Daroussin 	 * Remember the original working directory, if possible.
503*61d06d6bSBaptiste Daroussin 	 * This will be needed if some names on the command line
504*61d06d6bSBaptiste Daroussin 	 * are page names and some are relative file names.
505*61d06d6bSBaptiste Daroussin 	 * Do not error out if the current directory is not
506*61d06d6bSBaptiste Daroussin 	 * readable: Maybe it won't be needed after all.
507*61d06d6bSBaptiste Daroussin 	 */
508*61d06d6bSBaptiste Daroussin 	startdir = open(".", O_RDONLY | O_DIRECTORY);
509*61d06d6bSBaptiste Daroussin 
510*61d06d6bSBaptiste Daroussin 	while (argc > 0) {
511*61d06d6bSBaptiste Daroussin 
512*61d06d6bSBaptiste Daroussin 		/*
513*61d06d6bSBaptiste Daroussin 		 * Changing directories is not needed in ARG_FILE mode.
514*61d06d6bSBaptiste Daroussin 		 * Do it on a best-effort basis.  Even in case of
515*61d06d6bSBaptiste Daroussin 		 * failure, some functionality may still work.
516*61d06d6bSBaptiste Daroussin 		 */
517*61d06d6bSBaptiste Daroussin 		if (resp != NULL) {
518*61d06d6bSBaptiste Daroussin 			if (resp->ipath != SIZE_MAX)
519*61d06d6bSBaptiste Daroussin 				(void)chdir(conf.manpath.paths[resp->ipath]);
520*61d06d6bSBaptiste Daroussin 			else if (startdir != -1)
521*61d06d6bSBaptiste Daroussin 				(void)fchdir(startdir);
522*61d06d6bSBaptiste Daroussin 		}
523*61d06d6bSBaptiste Daroussin 
524*61d06d6bSBaptiste Daroussin 		fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv);
525*61d06d6bSBaptiste Daroussin 		if (fd != -1) {
526*61d06d6bSBaptiste Daroussin 			if (use_pager) {
527*61d06d6bSBaptiste Daroussin 				tag_files = tag_init();
528*61d06d6bSBaptiste Daroussin 				use_pager = 0;
529*61d06d6bSBaptiste Daroussin 			}
530*61d06d6bSBaptiste Daroussin 
531*61d06d6bSBaptiste Daroussin 			if (resp == NULL)
532*61d06d6bSBaptiste Daroussin 				parse(&curp, fd, *argv);
533*61d06d6bSBaptiste Daroussin 			else if (resp->form == FORM_SRC)
534*61d06d6bSBaptiste Daroussin 				parse(&curp, fd, resp->file);
535*61d06d6bSBaptiste Daroussin 			else
536*61d06d6bSBaptiste Daroussin 				passthrough(resp->file, fd,
537*61d06d6bSBaptiste Daroussin 				    conf.output.synopsisonly);
538*61d06d6bSBaptiste Daroussin 
539*61d06d6bSBaptiste Daroussin 			if (ferror(stdout)) {
540*61d06d6bSBaptiste Daroussin 				if (tag_files != NULL) {
541*61d06d6bSBaptiste Daroussin 					warn("%s", tag_files->ofn);
542*61d06d6bSBaptiste Daroussin 					tag_unlink();
543*61d06d6bSBaptiste Daroussin 					tag_files = NULL;
544*61d06d6bSBaptiste Daroussin 				} else
545*61d06d6bSBaptiste Daroussin 					warn("stdout");
546*61d06d6bSBaptiste Daroussin 				rc = MANDOCLEVEL_SYSERR;
547*61d06d6bSBaptiste Daroussin 				break;
548*61d06d6bSBaptiste Daroussin 			}
549*61d06d6bSBaptiste Daroussin 
550*61d06d6bSBaptiste Daroussin 			if (argc > 1 && curp.outtype <= OUTT_UTF8) {
551*61d06d6bSBaptiste Daroussin 				if (curp.outdata == NULL)
552*61d06d6bSBaptiste Daroussin 					outdata_alloc(&curp);
553*61d06d6bSBaptiste Daroussin 				terminal_sepline(curp.outdata);
554*61d06d6bSBaptiste Daroussin 			}
555*61d06d6bSBaptiste Daroussin 		} else if (rc < MANDOCLEVEL_ERROR)
556*61d06d6bSBaptiste Daroussin 			rc = MANDOCLEVEL_ERROR;
557*61d06d6bSBaptiste Daroussin 
558*61d06d6bSBaptiste Daroussin 		if (MANDOCLEVEL_OK != rc && curp.wstop)
559*61d06d6bSBaptiste Daroussin 			break;
560*61d06d6bSBaptiste Daroussin 
561*61d06d6bSBaptiste Daroussin 		if (resp != NULL)
562*61d06d6bSBaptiste Daroussin 			resp++;
563*61d06d6bSBaptiste Daroussin 		else
564*61d06d6bSBaptiste Daroussin 			argv++;
565*61d06d6bSBaptiste Daroussin 		if (--argc)
566*61d06d6bSBaptiste Daroussin 			mparse_reset(curp.mp);
567*61d06d6bSBaptiste Daroussin 	}
568*61d06d6bSBaptiste Daroussin 	if (startdir != -1) {
569*61d06d6bSBaptiste Daroussin 		(void)fchdir(startdir);
570*61d06d6bSBaptiste Daroussin 		close(startdir);
571*61d06d6bSBaptiste Daroussin 	}
572*61d06d6bSBaptiste Daroussin 
573*61d06d6bSBaptiste Daroussin 	if (curp.outdata != NULL) {
574*61d06d6bSBaptiste Daroussin 		switch (curp.outtype) {
575*61d06d6bSBaptiste Daroussin 		case OUTT_HTML:
576*61d06d6bSBaptiste Daroussin 			html_free(curp.outdata);
577*61d06d6bSBaptiste Daroussin 			break;
578*61d06d6bSBaptiste Daroussin 		case OUTT_UTF8:
579*61d06d6bSBaptiste Daroussin 		case OUTT_LOCALE:
580*61d06d6bSBaptiste Daroussin 		case OUTT_ASCII:
581*61d06d6bSBaptiste Daroussin 			ascii_free(curp.outdata);
582*61d06d6bSBaptiste Daroussin 			break;
583*61d06d6bSBaptiste Daroussin 		case OUTT_PDF:
584*61d06d6bSBaptiste Daroussin 		case OUTT_PS:
585*61d06d6bSBaptiste Daroussin 			pspdf_free(curp.outdata);
586*61d06d6bSBaptiste Daroussin 			break;
587*61d06d6bSBaptiste Daroussin 		default:
588*61d06d6bSBaptiste Daroussin 			break;
589*61d06d6bSBaptiste Daroussin 		}
590*61d06d6bSBaptiste Daroussin 	}
591*61d06d6bSBaptiste Daroussin 	mandoc_xr_free();
592*61d06d6bSBaptiste Daroussin 	mparse_free(curp.mp);
593*61d06d6bSBaptiste Daroussin 	mchars_free();
594*61d06d6bSBaptiste Daroussin 
595*61d06d6bSBaptiste Daroussin out:
596*61d06d6bSBaptiste Daroussin 	if (search.argmode != ARG_FILE) {
597*61d06d6bSBaptiste Daroussin 		manconf_free(&conf);
598*61d06d6bSBaptiste Daroussin 		mansearch_free(res, sz);
599*61d06d6bSBaptiste Daroussin 	}
600*61d06d6bSBaptiste Daroussin 
601*61d06d6bSBaptiste Daroussin 	free(curp.os_s);
602*61d06d6bSBaptiste Daroussin 
603*61d06d6bSBaptiste Daroussin 	/*
604*61d06d6bSBaptiste Daroussin 	 * When using a pager, finish writing both temporary files,
605*61d06d6bSBaptiste Daroussin 	 * fork it, wait for the user to close it, and clean up.
606*61d06d6bSBaptiste Daroussin 	 */
607*61d06d6bSBaptiste Daroussin 
608*61d06d6bSBaptiste Daroussin 	if (tag_files != NULL) {
609*61d06d6bSBaptiste Daroussin 		fclose(stdout);
610*61d06d6bSBaptiste Daroussin 		tag_write();
611*61d06d6bSBaptiste Daroussin 		man_pgid = getpgid(0);
612*61d06d6bSBaptiste Daroussin 		tag_files->tcpgid = man_pgid == getpid() ?
613*61d06d6bSBaptiste Daroussin 		    getpgid(getppid()) : man_pgid;
614*61d06d6bSBaptiste Daroussin 		pager_pid = 0;
615*61d06d6bSBaptiste Daroussin 		signum = SIGSTOP;
616*61d06d6bSBaptiste Daroussin 		for (;;) {
617*61d06d6bSBaptiste Daroussin 
618*61d06d6bSBaptiste Daroussin 			/* Stop here until moved to the foreground. */
619*61d06d6bSBaptiste Daroussin 
620*61d06d6bSBaptiste Daroussin 			tc_pgid = tcgetpgrp(tag_files->ofd);
621*61d06d6bSBaptiste Daroussin 			if (tc_pgid != man_pgid) {
622*61d06d6bSBaptiste Daroussin 				if (tc_pgid == pager_pid) {
623*61d06d6bSBaptiste Daroussin 					(void)tcsetpgrp(tag_files->ofd,
624*61d06d6bSBaptiste Daroussin 					    man_pgid);
625*61d06d6bSBaptiste Daroussin 					if (signum == SIGTTIN)
626*61d06d6bSBaptiste Daroussin 						continue;
627*61d06d6bSBaptiste Daroussin 				} else
628*61d06d6bSBaptiste Daroussin 					tag_files->tcpgid = tc_pgid;
629*61d06d6bSBaptiste Daroussin 				kill(0, signum);
630*61d06d6bSBaptiste Daroussin 				continue;
631*61d06d6bSBaptiste Daroussin 			}
632*61d06d6bSBaptiste Daroussin 
633*61d06d6bSBaptiste Daroussin 			/* Once in the foreground, activate the pager. */
634*61d06d6bSBaptiste Daroussin 
635*61d06d6bSBaptiste Daroussin 			if (pager_pid) {
636*61d06d6bSBaptiste Daroussin 				(void)tcsetpgrp(tag_files->ofd, pager_pid);
637*61d06d6bSBaptiste Daroussin 				kill(pager_pid, SIGCONT);
638*61d06d6bSBaptiste Daroussin 			} else
639*61d06d6bSBaptiste Daroussin 				pager_pid = spawn_pager(tag_files);
640*61d06d6bSBaptiste Daroussin 
641*61d06d6bSBaptiste Daroussin 			/* Wait for the pager to stop or exit. */
642*61d06d6bSBaptiste Daroussin 
643*61d06d6bSBaptiste Daroussin 			while ((pid = waitpid(pager_pid, &status,
644*61d06d6bSBaptiste Daroussin 			    WUNTRACED)) == -1 && errno == EINTR)
645*61d06d6bSBaptiste Daroussin 				continue;
646*61d06d6bSBaptiste Daroussin 
647*61d06d6bSBaptiste Daroussin 			if (pid == -1) {
648*61d06d6bSBaptiste Daroussin 				warn("wait");
649*61d06d6bSBaptiste Daroussin 				rc = MANDOCLEVEL_SYSERR;
650*61d06d6bSBaptiste Daroussin 				break;
651*61d06d6bSBaptiste Daroussin 			}
652*61d06d6bSBaptiste Daroussin 			if (!WIFSTOPPED(status))
653*61d06d6bSBaptiste Daroussin 				break;
654*61d06d6bSBaptiste Daroussin 
655*61d06d6bSBaptiste Daroussin 			signum = WSTOPSIG(status);
656*61d06d6bSBaptiste Daroussin 		}
657*61d06d6bSBaptiste Daroussin 		tag_unlink();
658*61d06d6bSBaptiste Daroussin 	}
659*61d06d6bSBaptiste Daroussin 
660*61d06d6bSBaptiste Daroussin 	return (int)rc;
661*61d06d6bSBaptiste Daroussin }
662*61d06d6bSBaptiste Daroussin 
663*61d06d6bSBaptiste Daroussin static void
664*61d06d6bSBaptiste Daroussin usage(enum argmode argmode)
665*61d06d6bSBaptiste Daroussin {
666*61d06d6bSBaptiste Daroussin 
667*61d06d6bSBaptiste Daroussin 	switch (argmode) {
668*61d06d6bSBaptiste Daroussin 	case ARG_FILE:
669*61d06d6bSBaptiste Daroussin 		fputs("usage: mandoc [-ac] [-I os=name] "
670*61d06d6bSBaptiste Daroussin 		    "[-K encoding] [-mdoc | -man] [-O options]\n"
671*61d06d6bSBaptiste Daroussin 		    "\t      [-T output] [-W level] [file ...]\n", stderr);
672*61d06d6bSBaptiste Daroussin 		break;
673*61d06d6bSBaptiste Daroussin 	case ARG_NAME:
674*61d06d6bSBaptiste Daroussin 		fputs("usage: man [-acfhklw] [-C file] [-M path] "
675*61d06d6bSBaptiste Daroussin 		    "[-m path] [-S subsection]\n"
676*61d06d6bSBaptiste Daroussin 		    "\t   [[-s] section] name ...\n", stderr);
677*61d06d6bSBaptiste Daroussin 		break;
678*61d06d6bSBaptiste Daroussin 	case ARG_WORD:
679*61d06d6bSBaptiste Daroussin 		fputs("usage: whatis [-afk] [-C file] "
680*61d06d6bSBaptiste Daroussin 		    "[-M path] [-m path] [-O outkey] [-S arch]\n"
681*61d06d6bSBaptiste Daroussin 		    "\t      [-s section] name ...\n", stderr);
682*61d06d6bSBaptiste Daroussin 		break;
683*61d06d6bSBaptiste Daroussin 	case ARG_EXPR:
684*61d06d6bSBaptiste Daroussin 		fputs("usage: apropos [-afk] [-C file] "
685*61d06d6bSBaptiste Daroussin 		    "[-M path] [-m path] [-O outkey] [-S arch]\n"
686*61d06d6bSBaptiste Daroussin 		    "\t       [-s section] expression ...\n", stderr);
687*61d06d6bSBaptiste Daroussin 		break;
688*61d06d6bSBaptiste Daroussin 	}
689*61d06d6bSBaptiste Daroussin 	exit((int)MANDOCLEVEL_BADARG);
690*61d06d6bSBaptiste Daroussin }
691*61d06d6bSBaptiste Daroussin 
692*61d06d6bSBaptiste Daroussin static int
693*61d06d6bSBaptiste Daroussin fs_lookup(const struct manpaths *paths, size_t ipath,
694*61d06d6bSBaptiste Daroussin 	const char *sec, const char *arch, const char *name,
695*61d06d6bSBaptiste Daroussin 	struct manpage **res, size_t *ressz)
696*61d06d6bSBaptiste Daroussin {
697*61d06d6bSBaptiste Daroussin 	glob_t		 globinfo;
698*61d06d6bSBaptiste Daroussin 	struct manpage	*page;
699*61d06d6bSBaptiste Daroussin 	char		*file;
700*61d06d6bSBaptiste Daroussin 	int		 globres;
701*61d06d6bSBaptiste Daroussin 	enum form	 form;
702*61d06d6bSBaptiste Daroussin 
703*61d06d6bSBaptiste Daroussin 	form = FORM_SRC;
704*61d06d6bSBaptiste Daroussin 	mandoc_asprintf(&file, "%s/man%s/%s.%s",
705*61d06d6bSBaptiste Daroussin 	    paths->paths[ipath], sec, name, sec);
706*61d06d6bSBaptiste Daroussin 	if (access(file, R_OK) != -1)
707*61d06d6bSBaptiste Daroussin 		goto found;
708*61d06d6bSBaptiste Daroussin 	free(file);
709*61d06d6bSBaptiste Daroussin 
710*61d06d6bSBaptiste Daroussin 	mandoc_asprintf(&file, "%s/cat%s/%s.0",
711*61d06d6bSBaptiste Daroussin 	    paths->paths[ipath], sec, name);
712*61d06d6bSBaptiste Daroussin 	if (access(file, R_OK) != -1) {
713*61d06d6bSBaptiste Daroussin 		form = FORM_CAT;
714*61d06d6bSBaptiste Daroussin 		goto found;
715*61d06d6bSBaptiste Daroussin 	}
716*61d06d6bSBaptiste Daroussin 	free(file);
717*61d06d6bSBaptiste Daroussin 
718*61d06d6bSBaptiste Daroussin 	if (arch != NULL) {
719*61d06d6bSBaptiste Daroussin 		mandoc_asprintf(&file, "%s/man%s/%s/%s.%s",
720*61d06d6bSBaptiste Daroussin 		    paths->paths[ipath], sec, arch, name, sec);
721*61d06d6bSBaptiste Daroussin 		if (access(file, R_OK) != -1)
722*61d06d6bSBaptiste Daroussin 			goto found;
723*61d06d6bSBaptiste Daroussin 		free(file);
724*61d06d6bSBaptiste Daroussin 	}
725*61d06d6bSBaptiste Daroussin 
726*61d06d6bSBaptiste Daroussin 	mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*",
727*61d06d6bSBaptiste Daroussin 	    paths->paths[ipath], sec, name);
728*61d06d6bSBaptiste Daroussin 	globres = glob(file, 0, NULL, &globinfo);
729*61d06d6bSBaptiste Daroussin 	if (globres != 0 && globres != GLOB_NOMATCH)
730*61d06d6bSBaptiste Daroussin 		warn("%s: glob", file);
731*61d06d6bSBaptiste Daroussin 	free(file);
732*61d06d6bSBaptiste Daroussin 	if (globres == 0)
733*61d06d6bSBaptiste Daroussin 		file = mandoc_strdup(*globinfo.gl_pathv);
734*61d06d6bSBaptiste Daroussin 	globfree(&globinfo);
735*61d06d6bSBaptiste Daroussin 	if (globres == 0)
736*61d06d6bSBaptiste Daroussin 		goto found;
737*61d06d6bSBaptiste Daroussin 	if (res != NULL || ipath + 1 != paths->sz)
738*61d06d6bSBaptiste Daroussin 		return 0;
739*61d06d6bSBaptiste Daroussin 
740*61d06d6bSBaptiste Daroussin 	mandoc_asprintf(&file, "%s.%s", name, sec);
741*61d06d6bSBaptiste Daroussin 	globres = access(file, R_OK);
742*61d06d6bSBaptiste Daroussin 	free(file);
743*61d06d6bSBaptiste Daroussin 	return globres != -1;
744*61d06d6bSBaptiste Daroussin 
745*61d06d6bSBaptiste Daroussin found:
746*61d06d6bSBaptiste Daroussin 	warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s",
747*61d06d6bSBaptiste Daroussin 	    name, sec, BINM_MAKEWHATIS, paths->paths[ipath]);
748*61d06d6bSBaptiste Daroussin 	if (res == NULL) {
749*61d06d6bSBaptiste Daroussin 		free(file);
750*61d06d6bSBaptiste Daroussin 		return 1;
751*61d06d6bSBaptiste Daroussin 	}
752*61d06d6bSBaptiste Daroussin 	*res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage));
753*61d06d6bSBaptiste Daroussin 	page = *res + (*ressz - 1);
754*61d06d6bSBaptiste Daroussin 	page->file = file;
755*61d06d6bSBaptiste Daroussin 	page->names = NULL;
756*61d06d6bSBaptiste Daroussin 	page->output = NULL;
757*61d06d6bSBaptiste Daroussin 	page->ipath = ipath;
758*61d06d6bSBaptiste Daroussin 	page->bits = NAME_FILE & NAME_MASK;
759*61d06d6bSBaptiste Daroussin 	page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10;
760*61d06d6bSBaptiste Daroussin 	page->form = form;
761*61d06d6bSBaptiste Daroussin 	return 1;
762*61d06d6bSBaptiste Daroussin }
763*61d06d6bSBaptiste Daroussin 
764*61d06d6bSBaptiste Daroussin static int
765*61d06d6bSBaptiste Daroussin fs_search(const struct mansearch *cfg, const struct manpaths *paths,
766*61d06d6bSBaptiste Daroussin 	int argc, char **argv, struct manpage **res, size_t *ressz)
767*61d06d6bSBaptiste Daroussin {
768*61d06d6bSBaptiste Daroussin 	const char *const sections[] =
769*61d06d6bSBaptiste Daroussin 	    {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"};
770*61d06d6bSBaptiste Daroussin 	const size_t nsec = sizeof(sections)/sizeof(sections[0]);
771*61d06d6bSBaptiste Daroussin 
772*61d06d6bSBaptiste Daroussin 	size_t		 ipath, isec, lastsz;
773*61d06d6bSBaptiste Daroussin 
774*61d06d6bSBaptiste Daroussin 	assert(cfg->argmode == ARG_NAME);
775*61d06d6bSBaptiste Daroussin 
776*61d06d6bSBaptiste Daroussin 	if (res != NULL)
777*61d06d6bSBaptiste Daroussin 		*res = NULL;
778*61d06d6bSBaptiste Daroussin 	*ressz = lastsz = 0;
779*61d06d6bSBaptiste Daroussin 	while (argc) {
780*61d06d6bSBaptiste Daroussin 		for (ipath = 0; ipath < paths->sz; ipath++) {
781*61d06d6bSBaptiste Daroussin 			if (cfg->sec != NULL) {
782*61d06d6bSBaptiste Daroussin 				if (fs_lookup(paths, ipath, cfg->sec,
783*61d06d6bSBaptiste Daroussin 				    cfg->arch, *argv, res, ressz) &&
784*61d06d6bSBaptiste Daroussin 				    cfg->firstmatch)
785*61d06d6bSBaptiste Daroussin 					return 1;
786*61d06d6bSBaptiste Daroussin 			} else for (isec = 0; isec < nsec; isec++)
787*61d06d6bSBaptiste Daroussin 				if (fs_lookup(paths, ipath, sections[isec],
788*61d06d6bSBaptiste Daroussin 				    cfg->arch, *argv, res, ressz) &&
789*61d06d6bSBaptiste Daroussin 				    cfg->firstmatch)
790*61d06d6bSBaptiste Daroussin 					return 1;
791*61d06d6bSBaptiste Daroussin 		}
792*61d06d6bSBaptiste Daroussin 		if (res != NULL && *ressz == lastsz &&
793*61d06d6bSBaptiste Daroussin 		    strchr(*argv, '/') == NULL)
794*61d06d6bSBaptiste Daroussin 			warnx("No entry for %s in the manual.", *argv);
795*61d06d6bSBaptiste Daroussin 		lastsz = *ressz;
796*61d06d6bSBaptiste Daroussin 		argv++;
797*61d06d6bSBaptiste Daroussin 		argc--;
798*61d06d6bSBaptiste Daroussin 	}
799*61d06d6bSBaptiste Daroussin 	return 0;
800*61d06d6bSBaptiste Daroussin }
801*61d06d6bSBaptiste Daroussin 
802*61d06d6bSBaptiste Daroussin static void
803*61d06d6bSBaptiste Daroussin parse(struct curparse *curp, int fd, const char *file)
804*61d06d6bSBaptiste Daroussin {
805*61d06d6bSBaptiste Daroussin 	enum mandoclevel  rctmp;
806*61d06d6bSBaptiste Daroussin 	struct roff_man	 *man;
807*61d06d6bSBaptiste Daroussin 
808*61d06d6bSBaptiste Daroussin 	/* Begin by parsing the file itself. */
809*61d06d6bSBaptiste Daroussin 
810*61d06d6bSBaptiste Daroussin 	assert(file);
811*61d06d6bSBaptiste Daroussin 	assert(fd >= 0);
812*61d06d6bSBaptiste Daroussin 
813*61d06d6bSBaptiste Daroussin 	rctmp = mparse_readfd(curp->mp, fd, file);
814*61d06d6bSBaptiste Daroussin 	if (fd != STDIN_FILENO)
815*61d06d6bSBaptiste Daroussin 		close(fd);
816*61d06d6bSBaptiste Daroussin 	if (rc < rctmp)
817*61d06d6bSBaptiste Daroussin 		rc = rctmp;
818*61d06d6bSBaptiste Daroussin 
819*61d06d6bSBaptiste Daroussin 	/*
820*61d06d6bSBaptiste Daroussin 	 * With -Wstop and warnings or errors of at least the requested
821*61d06d6bSBaptiste Daroussin 	 * level, do not produce output.
822*61d06d6bSBaptiste Daroussin 	 */
823*61d06d6bSBaptiste Daroussin 
824*61d06d6bSBaptiste Daroussin 	if (rctmp != MANDOCLEVEL_OK && curp->wstop)
825*61d06d6bSBaptiste Daroussin 		return;
826*61d06d6bSBaptiste Daroussin 
827*61d06d6bSBaptiste Daroussin 	if (curp->outdata == NULL)
828*61d06d6bSBaptiste Daroussin 		outdata_alloc(curp);
829*61d06d6bSBaptiste Daroussin 
830*61d06d6bSBaptiste Daroussin 	mparse_result(curp->mp, &man, NULL);
831*61d06d6bSBaptiste Daroussin 
832*61d06d6bSBaptiste Daroussin 	/* Execute the out device, if it exists. */
833*61d06d6bSBaptiste Daroussin 
834*61d06d6bSBaptiste Daroussin 	if (man == NULL)
835*61d06d6bSBaptiste Daroussin 		return;
836*61d06d6bSBaptiste Daroussin 	mandoc_xr_reset();
837*61d06d6bSBaptiste Daroussin 	if (man->macroset == MACROSET_MDOC) {
838*61d06d6bSBaptiste Daroussin 		if (curp->outtype != OUTT_TREE || !curp->outopts->noval)
839*61d06d6bSBaptiste Daroussin 			mdoc_validate(man);
840*61d06d6bSBaptiste Daroussin 		switch (curp->outtype) {
841*61d06d6bSBaptiste Daroussin 		case OUTT_HTML:
842*61d06d6bSBaptiste Daroussin 			html_mdoc(curp->outdata, man);
843*61d06d6bSBaptiste Daroussin 			break;
844*61d06d6bSBaptiste Daroussin 		case OUTT_TREE:
845*61d06d6bSBaptiste Daroussin 			tree_mdoc(curp->outdata, man);
846*61d06d6bSBaptiste Daroussin 			break;
847*61d06d6bSBaptiste Daroussin 		case OUTT_MAN:
848*61d06d6bSBaptiste Daroussin 			man_mdoc(curp->outdata, man);
849*61d06d6bSBaptiste Daroussin 			break;
850*61d06d6bSBaptiste Daroussin 		case OUTT_PDF:
851*61d06d6bSBaptiste Daroussin 		case OUTT_ASCII:
852*61d06d6bSBaptiste Daroussin 		case OUTT_UTF8:
853*61d06d6bSBaptiste Daroussin 		case OUTT_LOCALE:
854*61d06d6bSBaptiste Daroussin 		case OUTT_PS:
855*61d06d6bSBaptiste Daroussin 			terminal_mdoc(curp->outdata, man);
856*61d06d6bSBaptiste Daroussin 			break;
857*61d06d6bSBaptiste Daroussin 		case OUTT_MARKDOWN:
858*61d06d6bSBaptiste Daroussin 			markdown_mdoc(curp->outdata, man);
859*61d06d6bSBaptiste Daroussin 			break;
860*61d06d6bSBaptiste Daroussin 		default:
861*61d06d6bSBaptiste Daroussin 			break;
862*61d06d6bSBaptiste Daroussin 		}
863*61d06d6bSBaptiste Daroussin 	}
864*61d06d6bSBaptiste Daroussin 	if (man->macroset == MACROSET_MAN) {
865*61d06d6bSBaptiste Daroussin 		if (curp->outtype != OUTT_TREE || !curp->outopts->noval)
866*61d06d6bSBaptiste Daroussin 			man_validate(man);
867*61d06d6bSBaptiste Daroussin 		switch (curp->outtype) {
868*61d06d6bSBaptiste Daroussin 		case OUTT_HTML:
869*61d06d6bSBaptiste Daroussin 			html_man(curp->outdata, man);
870*61d06d6bSBaptiste Daroussin 			break;
871*61d06d6bSBaptiste Daroussin 		case OUTT_TREE:
872*61d06d6bSBaptiste Daroussin 			tree_man(curp->outdata, man);
873*61d06d6bSBaptiste Daroussin 			break;
874*61d06d6bSBaptiste Daroussin 		case OUTT_MAN:
875*61d06d6bSBaptiste Daroussin 			man_man(curp->outdata, man);
876*61d06d6bSBaptiste Daroussin 			break;
877*61d06d6bSBaptiste Daroussin 		case OUTT_PDF:
878*61d06d6bSBaptiste Daroussin 		case OUTT_ASCII:
879*61d06d6bSBaptiste Daroussin 		case OUTT_UTF8:
880*61d06d6bSBaptiste Daroussin 		case OUTT_LOCALE:
881*61d06d6bSBaptiste Daroussin 		case OUTT_PS:
882*61d06d6bSBaptiste Daroussin 			terminal_man(curp->outdata, man);
883*61d06d6bSBaptiste Daroussin 			break;
884*61d06d6bSBaptiste Daroussin 		default:
885*61d06d6bSBaptiste Daroussin 			break;
886*61d06d6bSBaptiste Daroussin 		}
887*61d06d6bSBaptiste Daroussin 	}
888*61d06d6bSBaptiste Daroussin 	if (curp->mmin < MANDOCERR_STYLE)
889*61d06d6bSBaptiste Daroussin 		check_xr(file);
890*61d06d6bSBaptiste Daroussin 	mparse_updaterc(curp->mp, &rc);
891*61d06d6bSBaptiste Daroussin }
892*61d06d6bSBaptiste Daroussin 
893*61d06d6bSBaptiste Daroussin static void
894*61d06d6bSBaptiste Daroussin check_xr(const char *file)
895*61d06d6bSBaptiste Daroussin {
896*61d06d6bSBaptiste Daroussin 	static struct manpaths	 paths;
897*61d06d6bSBaptiste Daroussin 	struct mansearch	 search;
898*61d06d6bSBaptiste Daroussin 	struct mandoc_xr	*xr;
899*61d06d6bSBaptiste Daroussin 	char			*cp;
900*61d06d6bSBaptiste Daroussin 	size_t			 sz;
901*61d06d6bSBaptiste Daroussin 
902*61d06d6bSBaptiste Daroussin 	if (paths.sz == 0)
903*61d06d6bSBaptiste Daroussin 		manpath_base(&paths);
904*61d06d6bSBaptiste Daroussin 
905*61d06d6bSBaptiste Daroussin 	for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) {
906*61d06d6bSBaptiste Daroussin 		if (xr->line == -1)
907*61d06d6bSBaptiste Daroussin 			continue;
908*61d06d6bSBaptiste Daroussin 		search.arch = NULL;
909*61d06d6bSBaptiste Daroussin 		search.sec = xr->sec;
910*61d06d6bSBaptiste Daroussin 		search.outkey = NULL;
911*61d06d6bSBaptiste Daroussin 		search.argmode = ARG_NAME;
912*61d06d6bSBaptiste Daroussin 		search.firstmatch = 1;
913*61d06d6bSBaptiste Daroussin 		if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz))
914*61d06d6bSBaptiste Daroussin 			continue;
915*61d06d6bSBaptiste Daroussin 		if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz))
916*61d06d6bSBaptiste Daroussin 			continue;
917*61d06d6bSBaptiste Daroussin 		if (xr->count == 1)
918*61d06d6bSBaptiste Daroussin 			mandoc_asprintf(&cp, "Xr %s %s", xr->name, xr->sec);
919*61d06d6bSBaptiste Daroussin 		else
920*61d06d6bSBaptiste Daroussin 			mandoc_asprintf(&cp, "Xr %s %s (%d times)",
921*61d06d6bSBaptiste Daroussin 			    xr->name, xr->sec, xr->count);
922*61d06d6bSBaptiste Daroussin 		mmsg(MANDOCERR_XR_BAD, MANDOCLEVEL_STYLE,
923*61d06d6bSBaptiste Daroussin 		    file, xr->line, xr->pos + 1, cp);
924*61d06d6bSBaptiste Daroussin 		free(cp);
925*61d06d6bSBaptiste Daroussin 	}
926*61d06d6bSBaptiste Daroussin }
927*61d06d6bSBaptiste Daroussin 
928*61d06d6bSBaptiste Daroussin static void
929*61d06d6bSBaptiste Daroussin outdata_alloc(struct curparse *curp)
930*61d06d6bSBaptiste Daroussin {
931*61d06d6bSBaptiste Daroussin 	switch (curp->outtype) {
932*61d06d6bSBaptiste Daroussin 	case OUTT_HTML:
933*61d06d6bSBaptiste Daroussin 		curp->outdata = html_alloc(curp->outopts);
934*61d06d6bSBaptiste Daroussin 		break;
935*61d06d6bSBaptiste Daroussin 	case OUTT_UTF8:
936*61d06d6bSBaptiste Daroussin 		curp->outdata = utf8_alloc(curp->outopts);
937*61d06d6bSBaptiste Daroussin 		break;
938*61d06d6bSBaptiste Daroussin 	case OUTT_LOCALE:
939*61d06d6bSBaptiste Daroussin 		curp->outdata = locale_alloc(curp->outopts);
940*61d06d6bSBaptiste Daroussin 		break;
941*61d06d6bSBaptiste Daroussin 	case OUTT_ASCII:
942*61d06d6bSBaptiste Daroussin 		curp->outdata = ascii_alloc(curp->outopts);
943*61d06d6bSBaptiste Daroussin 		break;
944*61d06d6bSBaptiste Daroussin 	case OUTT_PDF:
945*61d06d6bSBaptiste Daroussin 		curp->outdata = pdf_alloc(curp->outopts);
946*61d06d6bSBaptiste Daroussin 		break;
947*61d06d6bSBaptiste Daroussin 	case OUTT_PS:
948*61d06d6bSBaptiste Daroussin 		curp->outdata = ps_alloc(curp->outopts);
949*61d06d6bSBaptiste Daroussin 		break;
950*61d06d6bSBaptiste Daroussin 	default:
951*61d06d6bSBaptiste Daroussin 		break;
952*61d06d6bSBaptiste Daroussin 	}
953*61d06d6bSBaptiste Daroussin }
954*61d06d6bSBaptiste Daroussin 
955*61d06d6bSBaptiste Daroussin static void
956*61d06d6bSBaptiste Daroussin passthrough(const char *file, int fd, int synopsis_only)
957*61d06d6bSBaptiste Daroussin {
958*61d06d6bSBaptiste Daroussin 	const char	 synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS";
959*61d06d6bSBaptiste Daroussin 	const char	 synr[] = "SYNOPSIS";
960*61d06d6bSBaptiste Daroussin 
961*61d06d6bSBaptiste Daroussin 	FILE		*stream;
962*61d06d6bSBaptiste Daroussin 	const char	*syscall;
963*61d06d6bSBaptiste Daroussin 	char		*line, *cp;
964*61d06d6bSBaptiste Daroussin 	size_t		 linesz;
965*61d06d6bSBaptiste Daroussin 	ssize_t		 len, written;
966*61d06d6bSBaptiste Daroussin 	int		 print;
967*61d06d6bSBaptiste Daroussin 
968*61d06d6bSBaptiste Daroussin 	line = NULL;
969*61d06d6bSBaptiste Daroussin 	linesz = 0;
970*61d06d6bSBaptiste Daroussin 
971*61d06d6bSBaptiste Daroussin 	if (fflush(stdout) == EOF) {
972*61d06d6bSBaptiste Daroussin 		syscall = "fflush";
973*61d06d6bSBaptiste Daroussin 		goto fail;
974*61d06d6bSBaptiste Daroussin 	}
975*61d06d6bSBaptiste Daroussin 
976*61d06d6bSBaptiste Daroussin 	if ((stream = fdopen(fd, "r")) == NULL) {
977*61d06d6bSBaptiste Daroussin 		close(fd);
978*61d06d6bSBaptiste Daroussin 		syscall = "fdopen";
979*61d06d6bSBaptiste Daroussin 		goto fail;
980*61d06d6bSBaptiste Daroussin 	}
981*61d06d6bSBaptiste Daroussin 
982*61d06d6bSBaptiste Daroussin 	print = 0;
983*61d06d6bSBaptiste Daroussin 	while ((len = getline(&line, &linesz, stream)) != -1) {
984*61d06d6bSBaptiste Daroussin 		cp = line;
985*61d06d6bSBaptiste Daroussin 		if (synopsis_only) {
986*61d06d6bSBaptiste Daroussin 			if (print) {
987*61d06d6bSBaptiste Daroussin 				if ( ! isspace((unsigned char)*cp))
988*61d06d6bSBaptiste Daroussin 					goto done;
989*61d06d6bSBaptiste Daroussin 				while (isspace((unsigned char)*cp)) {
990*61d06d6bSBaptiste Daroussin 					cp++;
991*61d06d6bSBaptiste Daroussin 					len--;
992*61d06d6bSBaptiste Daroussin 				}
993*61d06d6bSBaptiste Daroussin 			} else {
994*61d06d6bSBaptiste Daroussin 				if (strcmp(cp, synb) == 0 ||
995*61d06d6bSBaptiste Daroussin 				    strcmp(cp, synr) == 0)
996*61d06d6bSBaptiste Daroussin 					print = 1;
997*61d06d6bSBaptiste Daroussin 				continue;
998*61d06d6bSBaptiste Daroussin 			}
999*61d06d6bSBaptiste Daroussin 		}
1000*61d06d6bSBaptiste Daroussin 		for (; len > 0; len -= written) {
1001*61d06d6bSBaptiste Daroussin 			if ((written = write(STDOUT_FILENO, cp, len)) != -1)
1002*61d06d6bSBaptiste Daroussin 				continue;
1003*61d06d6bSBaptiste Daroussin 			fclose(stream);
1004*61d06d6bSBaptiste Daroussin 			syscall = "write";
1005*61d06d6bSBaptiste Daroussin 			goto fail;
1006*61d06d6bSBaptiste Daroussin 		}
1007*61d06d6bSBaptiste Daroussin 	}
1008*61d06d6bSBaptiste Daroussin 
1009*61d06d6bSBaptiste Daroussin 	if (ferror(stream)) {
1010*61d06d6bSBaptiste Daroussin 		fclose(stream);
1011*61d06d6bSBaptiste Daroussin 		syscall = "getline";
1012*61d06d6bSBaptiste Daroussin 		goto fail;
1013*61d06d6bSBaptiste Daroussin 	}
1014*61d06d6bSBaptiste Daroussin 
1015*61d06d6bSBaptiste Daroussin done:
1016*61d06d6bSBaptiste Daroussin 	free(line);
1017*61d06d6bSBaptiste Daroussin 	fclose(stream);
1018*61d06d6bSBaptiste Daroussin 	return;
1019*61d06d6bSBaptiste Daroussin 
1020*61d06d6bSBaptiste Daroussin fail:
1021*61d06d6bSBaptiste Daroussin 	free(line);
1022*61d06d6bSBaptiste Daroussin 	warn("%s: SYSERR: %s", file, syscall);
1023*61d06d6bSBaptiste Daroussin 	if (rc < MANDOCLEVEL_SYSERR)
1024*61d06d6bSBaptiste Daroussin 		rc = MANDOCLEVEL_SYSERR;
1025*61d06d6bSBaptiste Daroussin }
1026*61d06d6bSBaptiste Daroussin 
1027*61d06d6bSBaptiste Daroussin static int
1028*61d06d6bSBaptiste Daroussin koptions(int *options, char *arg)
1029*61d06d6bSBaptiste Daroussin {
1030*61d06d6bSBaptiste Daroussin 
1031*61d06d6bSBaptiste Daroussin 	if ( ! strcmp(arg, "utf-8")) {
1032*61d06d6bSBaptiste Daroussin 		*options |=  MPARSE_UTF8;
1033*61d06d6bSBaptiste Daroussin 		*options &= ~MPARSE_LATIN1;
1034*61d06d6bSBaptiste Daroussin 	} else if ( ! strcmp(arg, "iso-8859-1")) {
1035*61d06d6bSBaptiste Daroussin 		*options |=  MPARSE_LATIN1;
1036*61d06d6bSBaptiste Daroussin 		*options &= ~MPARSE_UTF8;
1037*61d06d6bSBaptiste Daroussin 	} else if ( ! strcmp(arg, "us-ascii")) {
1038*61d06d6bSBaptiste Daroussin 		*options &= ~(MPARSE_UTF8 | MPARSE_LATIN1);
1039*61d06d6bSBaptiste Daroussin 	} else {
1040*61d06d6bSBaptiste Daroussin 		warnx("-K %s: Bad argument", arg);
1041*61d06d6bSBaptiste Daroussin 		return 0;
1042*61d06d6bSBaptiste Daroussin 	}
1043*61d06d6bSBaptiste Daroussin 	return 1;
1044*61d06d6bSBaptiste Daroussin }
1045*61d06d6bSBaptiste Daroussin 
1046*61d06d6bSBaptiste Daroussin static void
1047*61d06d6bSBaptiste Daroussin moptions(int *options, char *arg)
1048*61d06d6bSBaptiste Daroussin {
1049*61d06d6bSBaptiste Daroussin 
1050*61d06d6bSBaptiste Daroussin 	if (arg == NULL)
1051*61d06d6bSBaptiste Daroussin 		return;
1052*61d06d6bSBaptiste Daroussin 	if (strcmp(arg, "doc") == 0)
1053*61d06d6bSBaptiste Daroussin 		*options |= MPARSE_MDOC;
1054*61d06d6bSBaptiste Daroussin 	else if (strcmp(arg, "an") == 0)
1055*61d06d6bSBaptiste Daroussin 		*options |= MPARSE_MAN;
1056*61d06d6bSBaptiste Daroussin }
1057*61d06d6bSBaptiste Daroussin 
1058*61d06d6bSBaptiste Daroussin static int
1059*61d06d6bSBaptiste Daroussin toptions(struct curparse *curp, char *arg)
1060*61d06d6bSBaptiste Daroussin {
1061*61d06d6bSBaptiste Daroussin 
1062*61d06d6bSBaptiste Daroussin 	if (0 == strcmp(arg, "ascii"))
1063*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_ASCII;
1064*61d06d6bSBaptiste Daroussin 	else if (0 == strcmp(arg, "lint")) {
1065*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_LINT;
1066*61d06d6bSBaptiste Daroussin 		curp->mmin = MANDOCERR_BASE;
1067*61d06d6bSBaptiste Daroussin 		mmsg_stream = stdout;
1068*61d06d6bSBaptiste Daroussin 	} else if (0 == strcmp(arg, "tree"))
1069*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_TREE;
1070*61d06d6bSBaptiste Daroussin 	else if (0 == strcmp(arg, "man"))
1071*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_MAN;
1072*61d06d6bSBaptiste Daroussin 	else if (0 == strcmp(arg, "html"))
1073*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_HTML;
1074*61d06d6bSBaptiste Daroussin 	else if (0 == strcmp(arg, "markdown"))
1075*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_MARKDOWN;
1076*61d06d6bSBaptiste Daroussin 	else if (0 == strcmp(arg, "utf8"))
1077*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_UTF8;
1078*61d06d6bSBaptiste Daroussin 	else if (0 == strcmp(arg, "locale"))
1079*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_LOCALE;
1080*61d06d6bSBaptiste Daroussin 	else if (0 == strcmp(arg, "ps"))
1081*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_PS;
1082*61d06d6bSBaptiste Daroussin 	else if (0 == strcmp(arg, "pdf"))
1083*61d06d6bSBaptiste Daroussin 		curp->outtype = OUTT_PDF;
1084*61d06d6bSBaptiste Daroussin 	else {
1085*61d06d6bSBaptiste Daroussin 		warnx("-T %s: Bad argument", arg);
1086*61d06d6bSBaptiste Daroussin 		return 0;
1087*61d06d6bSBaptiste Daroussin 	}
1088*61d06d6bSBaptiste Daroussin 
1089*61d06d6bSBaptiste Daroussin 	return 1;
1090*61d06d6bSBaptiste Daroussin }
1091*61d06d6bSBaptiste Daroussin 
1092*61d06d6bSBaptiste Daroussin static int
1093*61d06d6bSBaptiste Daroussin woptions(struct curparse *curp, char *arg)
1094*61d06d6bSBaptiste Daroussin {
1095*61d06d6bSBaptiste Daroussin 	char		*v, *o;
1096*61d06d6bSBaptiste Daroussin 	const char	*toks[11];
1097*61d06d6bSBaptiste Daroussin 
1098*61d06d6bSBaptiste Daroussin 	toks[0] = "stop";
1099*61d06d6bSBaptiste Daroussin 	toks[1] = "all";
1100*61d06d6bSBaptiste Daroussin 	toks[2] = "base";
1101*61d06d6bSBaptiste Daroussin 	toks[3] = "style";
1102*61d06d6bSBaptiste Daroussin 	toks[4] = "warning";
1103*61d06d6bSBaptiste Daroussin 	toks[5] = "error";
1104*61d06d6bSBaptiste Daroussin 	toks[6] = "unsupp";
1105*61d06d6bSBaptiste Daroussin 	toks[7] = "fatal";
1106*61d06d6bSBaptiste Daroussin 	toks[8] = "openbsd";
1107*61d06d6bSBaptiste Daroussin 	toks[9] = "netbsd";
1108*61d06d6bSBaptiste Daroussin 	toks[10] = NULL;
1109*61d06d6bSBaptiste Daroussin 
1110*61d06d6bSBaptiste Daroussin 	while (*arg) {
1111*61d06d6bSBaptiste Daroussin 		o = arg;
1112*61d06d6bSBaptiste Daroussin 		switch (getsubopt(&arg, (char * const *)toks, &v)) {
1113*61d06d6bSBaptiste Daroussin 		case 0:
1114*61d06d6bSBaptiste Daroussin 			curp->wstop = 1;
1115*61d06d6bSBaptiste Daroussin 			break;
1116*61d06d6bSBaptiste Daroussin 		case 1:
1117*61d06d6bSBaptiste Daroussin 		case 2:
1118*61d06d6bSBaptiste Daroussin 			curp->mmin = MANDOCERR_BASE;
1119*61d06d6bSBaptiste Daroussin 			break;
1120*61d06d6bSBaptiste Daroussin 		case 3:
1121*61d06d6bSBaptiste Daroussin 			curp->mmin = MANDOCERR_STYLE;
1122*61d06d6bSBaptiste Daroussin 			break;
1123*61d06d6bSBaptiste Daroussin 		case 4:
1124*61d06d6bSBaptiste Daroussin 			curp->mmin = MANDOCERR_WARNING;
1125*61d06d6bSBaptiste Daroussin 			break;
1126*61d06d6bSBaptiste Daroussin 		case 5:
1127*61d06d6bSBaptiste Daroussin 			curp->mmin = MANDOCERR_ERROR;
1128*61d06d6bSBaptiste Daroussin 			break;
1129*61d06d6bSBaptiste Daroussin 		case 6:
1130*61d06d6bSBaptiste Daroussin 			curp->mmin = MANDOCERR_UNSUPP;
1131*61d06d6bSBaptiste Daroussin 			break;
1132*61d06d6bSBaptiste Daroussin 		case 7:
1133*61d06d6bSBaptiste Daroussin 			curp->mmin = MANDOCERR_MAX;
1134*61d06d6bSBaptiste Daroussin 			break;
1135*61d06d6bSBaptiste Daroussin 		case 8:
1136*61d06d6bSBaptiste Daroussin 			curp->mmin = MANDOCERR_BASE;
1137*61d06d6bSBaptiste Daroussin 			curp->os_e = MANDOC_OS_OPENBSD;
1138*61d06d6bSBaptiste Daroussin 			break;
1139*61d06d6bSBaptiste Daroussin 		case 9:
1140*61d06d6bSBaptiste Daroussin 			curp->mmin = MANDOCERR_BASE;
1141*61d06d6bSBaptiste Daroussin 			curp->os_e = MANDOC_OS_NETBSD;
1142*61d06d6bSBaptiste Daroussin 			break;
1143*61d06d6bSBaptiste Daroussin 		default:
1144*61d06d6bSBaptiste Daroussin 			warnx("-W %s: Bad argument", o);
1145*61d06d6bSBaptiste Daroussin 			return 0;
1146*61d06d6bSBaptiste Daroussin 		}
1147*61d06d6bSBaptiste Daroussin 	}
1148*61d06d6bSBaptiste Daroussin 	return 1;
1149*61d06d6bSBaptiste Daroussin }
1150*61d06d6bSBaptiste Daroussin 
1151*61d06d6bSBaptiste Daroussin static void
1152*61d06d6bSBaptiste Daroussin mmsg(enum mandocerr t, enum mandoclevel lvl,
1153*61d06d6bSBaptiste Daroussin 		const char *file, int line, int col, const char *msg)
1154*61d06d6bSBaptiste Daroussin {
1155*61d06d6bSBaptiste Daroussin 	const char	*mparse_msg;
1156*61d06d6bSBaptiste Daroussin 
1157*61d06d6bSBaptiste Daroussin 	fprintf(mmsg_stream, "%s: %s:", getprogname(),
1158*61d06d6bSBaptiste Daroussin 	    file == NULL ? "<stdin>" : file);
1159*61d06d6bSBaptiste Daroussin 
1160*61d06d6bSBaptiste Daroussin 	if (line)
1161*61d06d6bSBaptiste Daroussin 		fprintf(mmsg_stream, "%d:%d:", line, col + 1);
1162*61d06d6bSBaptiste Daroussin 
1163*61d06d6bSBaptiste Daroussin 	fprintf(mmsg_stream, " %s", mparse_strlevel(lvl));
1164*61d06d6bSBaptiste Daroussin 
1165*61d06d6bSBaptiste Daroussin 	if ((mparse_msg = mparse_strerror(t)) != NULL)
1166*61d06d6bSBaptiste Daroussin 		fprintf(mmsg_stream, ": %s", mparse_msg);
1167*61d06d6bSBaptiste Daroussin 
1168*61d06d6bSBaptiste Daroussin 	if (msg)
1169*61d06d6bSBaptiste Daroussin 		fprintf(mmsg_stream, ": %s", msg);
1170*61d06d6bSBaptiste Daroussin 
1171*61d06d6bSBaptiste Daroussin 	fputc('\n', mmsg_stream);
1172*61d06d6bSBaptiste Daroussin }
1173*61d06d6bSBaptiste Daroussin 
1174*61d06d6bSBaptiste Daroussin static pid_t
1175*61d06d6bSBaptiste Daroussin spawn_pager(struct tag_files *tag_files)
1176*61d06d6bSBaptiste Daroussin {
1177*61d06d6bSBaptiste Daroussin 	const struct timespec timeout = { 0, 100000000 };  /* 0.1s */
1178*61d06d6bSBaptiste Daroussin #define MAX_PAGER_ARGS 16
1179*61d06d6bSBaptiste Daroussin 	char		*argv[MAX_PAGER_ARGS];
1180*61d06d6bSBaptiste Daroussin 	const char	*pager;
1181*61d06d6bSBaptiste Daroussin 	char		*cp;
1182*61d06d6bSBaptiste Daroussin 	size_t		 cmdlen;
1183*61d06d6bSBaptiste Daroussin 	int		 argc;
1184*61d06d6bSBaptiste Daroussin 	pid_t		 pager_pid;
1185*61d06d6bSBaptiste Daroussin 
1186*61d06d6bSBaptiste Daroussin 	pager = getenv("MANPAGER");
1187*61d06d6bSBaptiste Daroussin 	if (pager == NULL || *pager == '\0')
1188*61d06d6bSBaptiste Daroussin 		pager = getenv("PAGER");
1189*61d06d6bSBaptiste Daroussin 	if (pager == NULL || *pager == '\0')
1190*61d06d6bSBaptiste Daroussin 		pager = "less -s";
1191*61d06d6bSBaptiste Daroussin 	cp = mandoc_strdup(pager);
1192*61d06d6bSBaptiste Daroussin 
1193*61d06d6bSBaptiste Daroussin 	/*
1194*61d06d6bSBaptiste Daroussin 	 * Parse the pager command into words.
1195*61d06d6bSBaptiste Daroussin 	 * Intentionally do not do anything fancy here.
1196*61d06d6bSBaptiste Daroussin 	 */
1197*61d06d6bSBaptiste Daroussin 
1198*61d06d6bSBaptiste Daroussin 	argc = 0;
1199*61d06d6bSBaptiste Daroussin 	while (argc + 4 < MAX_PAGER_ARGS) {
1200*61d06d6bSBaptiste Daroussin 		argv[argc++] = cp;
1201*61d06d6bSBaptiste Daroussin 		cp = strchr(cp, ' ');
1202*61d06d6bSBaptiste Daroussin 		if (cp == NULL)
1203*61d06d6bSBaptiste Daroussin 			break;
1204*61d06d6bSBaptiste Daroussin 		*cp++ = '\0';
1205*61d06d6bSBaptiste Daroussin 		while (*cp == ' ')
1206*61d06d6bSBaptiste Daroussin 			cp++;
1207*61d06d6bSBaptiste Daroussin 		if (*cp == '\0')
1208*61d06d6bSBaptiste Daroussin 			break;
1209*61d06d6bSBaptiste Daroussin 	}
1210*61d06d6bSBaptiste Daroussin 
1211*61d06d6bSBaptiste Daroussin 	/* For less(1), use the tag file. */
1212*61d06d6bSBaptiste Daroussin 
1213*61d06d6bSBaptiste Daroussin 	if ((cmdlen = strlen(argv[0])) >= 4) {
1214*61d06d6bSBaptiste Daroussin 		cp = argv[0] + cmdlen - 4;
1215*61d06d6bSBaptiste Daroussin 		if (strcmp(cp, "less") == 0) {
1216*61d06d6bSBaptiste Daroussin 			argv[argc++] = mandoc_strdup("-T");
1217*61d06d6bSBaptiste Daroussin 			argv[argc++] = tag_files->tfn;
1218*61d06d6bSBaptiste Daroussin 		}
1219*61d06d6bSBaptiste Daroussin 	}
1220*61d06d6bSBaptiste Daroussin 	argv[argc++] = tag_files->ofn;
1221*61d06d6bSBaptiste Daroussin 	argv[argc] = NULL;
1222*61d06d6bSBaptiste Daroussin 
1223*61d06d6bSBaptiste Daroussin 	switch (pager_pid = fork()) {
1224*61d06d6bSBaptiste Daroussin 	case -1:
1225*61d06d6bSBaptiste Daroussin 		err((int)MANDOCLEVEL_SYSERR, "fork");
1226*61d06d6bSBaptiste Daroussin 	case 0:
1227*61d06d6bSBaptiste Daroussin 		break;
1228*61d06d6bSBaptiste Daroussin 	default:
1229*61d06d6bSBaptiste Daroussin 		(void)setpgid(pager_pid, 0);
1230*61d06d6bSBaptiste Daroussin 		(void)tcsetpgrp(tag_files->ofd, pager_pid);
1231*61d06d6bSBaptiste Daroussin #if HAVE_PLEDGE
1232*61d06d6bSBaptiste Daroussin 		if (pledge("stdio rpath tmppath tty proc", NULL) == -1)
1233*61d06d6bSBaptiste Daroussin 			err((int)MANDOCLEVEL_SYSERR, "pledge");
1234*61d06d6bSBaptiste Daroussin #endif
1235*61d06d6bSBaptiste Daroussin 		tag_files->pager_pid = pager_pid;
1236*61d06d6bSBaptiste Daroussin 		return pager_pid;
1237*61d06d6bSBaptiste Daroussin 	}
1238*61d06d6bSBaptiste Daroussin 
1239*61d06d6bSBaptiste Daroussin 	/* The child process becomes the pager. */
1240*61d06d6bSBaptiste Daroussin 
1241*61d06d6bSBaptiste Daroussin 	if (dup2(tag_files->ofd, STDOUT_FILENO) == -1)
1242*61d06d6bSBaptiste Daroussin 		err((int)MANDOCLEVEL_SYSERR, "pager stdout");
1243*61d06d6bSBaptiste Daroussin 	close(tag_files->ofd);
1244*61d06d6bSBaptiste Daroussin 	assert(tag_files->tfd == -1);
1245*61d06d6bSBaptiste Daroussin 
1246*61d06d6bSBaptiste Daroussin 	/* Do not start the pager before controlling the terminal. */
1247*61d06d6bSBaptiste Daroussin 
1248*61d06d6bSBaptiste Daroussin 	while (tcgetpgrp(STDOUT_FILENO) != getpid())
1249*61d06d6bSBaptiste Daroussin 		nanosleep(&timeout, NULL);
1250*61d06d6bSBaptiste Daroussin 
1251*61d06d6bSBaptiste Daroussin 	execvp(argv[0], argv);
1252*61d06d6bSBaptiste Daroussin 	err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]);
1253*61d06d6bSBaptiste Daroussin }
1254