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 */
8095c635efSGarrett D'Amore enum outt outtype; /* which output to use */
8195c635efSGarrett D'Amore out_mdoc outmdoc; /* mdoc output ptr */
8295c635efSGarrett D'Amore 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 *);
10295c635efSGarrett D'Amore 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
main(int argc,char * argv[])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
14995c635efSGarrett D'Amore
150*260e9a87SYuri Pankov /* Search options. */
151*260e9a87SYuri Pankov
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;
264*260e9a87SYuri Pankov }
26595c635efSGarrett D'Amore }
26695c635efSGarrett D'Amore
267*260e9a87SYuri Pankov if (show_usage)
268*260e9a87SYuri Pankov usage(search.argmode);
269*260e9a87SYuri Pankov
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();
42395c635efSGarrett D'Amore 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 }
448*260e9a87SYuri Pankov
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 }
456*260e9a87SYuri Pankov
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);
47095c635efSGarrett D'Amore 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
usage(enum argmode argmode)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;
52595c635efSGarrett D'Amore }
52695c635efSGarrett D'Amore exit((int)MANDOCLEVEL_BADARG);
52795c635efSGarrett D'Amore }
52895c635efSGarrett D'Amore
529*260e9a87SYuri Pankov static int
fs_lookup(const struct manpaths * paths,size_t ipath,const char * sec,const char * arch,const char * name,struct manpage ** res,size_t * ressz)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)
533*260e9a87SYuri Pankov {
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);
574*260e9a87SYuri Pankov
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
581*260e9a87SYuri Pankov
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
59495c635efSGarrett D'Amore static void
fs_search(const struct mansearch * cfg,const struct manpaths * paths,int argc,char ** argv,struct manpage ** res,size_t * ressz)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 }
629*260e9a87SYuri Pankov }
630*260e9a87SYuri Pankov
631*260e9a87SYuri Pankov static void
parse(struct curparse * curp,int fd,const char * file,enum mandoclevel * level)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
73195c635efSGarrett D'Amore cleanup:
73295c635efSGarrett D'Amore if (*level < rc)
73395c635efSGarrett D'Amore *level = rc;
73495c635efSGarrett D'Amore }
73595c635efSGarrett D'Amore
736*260e9a87SYuri Pankov static enum mandoclevel
passthrough(const char * file,int fd,int synopsis_only)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
80295c635efSGarrett D'Amore static int
koptions(int * options,char * arg)803*260e9a87SYuri Pankov koptions(int *options, char *arg)
80495c635efSGarrett D'Amore {
80595c635efSGarrett D'Amore
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
822*260e9a87SYuri Pankov static int
moptions(int * options,char * arg)823*260e9a87SYuri Pankov moptions(int *options, char *arg)
824*260e9a87SYuri Pankov {
825*260e9a87SYuri Pankov
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
toptions(struct curparse * curp,char * arg)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
woptions(struct curparse * curp,char * arg)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
mmsg(enum mandocerr t,enum mandoclevel lvl,const char * file,int line,int col,const char * msg)92295c635efSGarrett D'Amore 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;
92695c635efSGarrett D'Amore
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);
931*260e9a87SYuri Pankov
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
spawn_pager(void)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