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 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 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 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 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 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 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 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 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 84495c635efSGarrett D'Amore toptions(struct curparse *curp, char *arg) 84595c635efSGarrett D'Amore { 84695c635efSGarrett D'Amore 84795c635efSGarrett D'Amore if (0 == strcmp(arg, "ascii")) 84895c635efSGarrett D'Amore curp->outtype = OUTT_ASCII; 84995c635efSGarrett D'Amore else if (0 == strcmp(arg, "lint")) { 85095c635efSGarrett D'Amore curp->outtype = OUTT_LINT; 85195c635efSGarrett D'Amore curp->wlevel = MANDOCLEVEL_WARNING; 85295c635efSGarrett D'Amore } else if (0 == strcmp(arg, "tree")) 85395c635efSGarrett D'Amore curp->outtype = OUTT_TREE; 85495c635efSGarrett D'Amore else if (0 == strcmp(arg, "man")) 85595c635efSGarrett D'Amore curp->outtype = OUTT_MAN; 85695c635efSGarrett D'Amore else if (0 == strcmp(arg, "html")) 85795c635efSGarrett D'Amore curp->outtype = OUTT_HTML; 85895c635efSGarrett D'Amore else if (0 == strcmp(arg, "utf8")) 85995c635efSGarrett D'Amore curp->outtype = OUTT_UTF8; 86095c635efSGarrett D'Amore else if (0 == strcmp(arg, "locale")) 86195c635efSGarrett D'Amore curp->outtype = OUTT_LOCALE; 86295c635efSGarrett D'Amore else if (0 == strcmp(arg, "xhtml")) 863*260e9a87SYuri Pankov curp->outtype = OUTT_HTML; 86495c635efSGarrett D'Amore else if (0 == strcmp(arg, "ps")) 86595c635efSGarrett D'Amore curp->outtype = OUTT_PS; 86695c635efSGarrett D'Amore else if (0 == strcmp(arg, "pdf")) 86795c635efSGarrett D'Amore curp->outtype = OUTT_PDF; 86895c635efSGarrett D'Amore else { 869*260e9a87SYuri Pankov fprintf(stderr, "%s: -T %s: Bad argument\n", 870*260e9a87SYuri Pankov progname, arg); 87195c635efSGarrett D'Amore return(0); 87295c635efSGarrett D'Amore } 87395c635efSGarrett D'Amore 87495c635efSGarrett D'Amore return(1); 87595c635efSGarrett D'Amore } 87695c635efSGarrett D'Amore 87795c635efSGarrett D'Amore static int 87895c635efSGarrett D'Amore woptions(struct curparse *curp, char *arg) 87995c635efSGarrett D'Amore { 88095c635efSGarrett D'Amore char *v, *o; 881*260e9a87SYuri Pankov const char *toks[7]; 88295c635efSGarrett D'Amore 88395c635efSGarrett D'Amore toks[0] = "stop"; 88495c635efSGarrett D'Amore toks[1] = "all"; 88595c635efSGarrett D'Amore toks[2] = "warning"; 88695c635efSGarrett D'Amore toks[3] = "error"; 887*260e9a87SYuri Pankov toks[4] = "unsupp"; 888*260e9a87SYuri Pankov toks[5] = "fatal"; 889*260e9a87SYuri Pankov toks[6] = NULL; 89095c635efSGarrett D'Amore 89195c635efSGarrett D'Amore while (*arg) { 89295c635efSGarrett D'Amore o = arg; 89395c635efSGarrett D'Amore switch (getsubopt(&arg, UNCONST(toks), &v)) { 894*260e9a87SYuri Pankov case 0: 89595c635efSGarrett D'Amore curp->wstop = 1; 89695c635efSGarrett D'Amore break; 897*260e9a87SYuri Pankov case 1: 89895c635efSGarrett D'Amore /* FALLTHROUGH */ 899*260e9a87SYuri Pankov case 2: 90095c635efSGarrett D'Amore curp->wlevel = MANDOCLEVEL_WARNING; 90195c635efSGarrett D'Amore break; 902*260e9a87SYuri Pankov case 3: 90395c635efSGarrett D'Amore curp->wlevel = MANDOCLEVEL_ERROR; 90495c635efSGarrett D'Amore break; 905*260e9a87SYuri Pankov case 4: 906*260e9a87SYuri Pankov curp->wlevel = MANDOCLEVEL_UNSUPP; 907*260e9a87SYuri Pankov break; 908*260e9a87SYuri Pankov case 5: 909*260e9a87SYuri Pankov curp->wlevel = MANDOCLEVEL_BADARG; 91095c635efSGarrett D'Amore break; 91195c635efSGarrett D'Amore default: 912*260e9a87SYuri Pankov fprintf(stderr, "%s: -W %s: Bad argument\n", 913*260e9a87SYuri Pankov progname, o); 91495c635efSGarrett D'Amore return(0); 91595c635efSGarrett D'Amore } 91695c635efSGarrett D'Amore } 91795c635efSGarrett D'Amore 91895c635efSGarrett D'Amore return(1); 91995c635efSGarrett D'Amore } 92095c635efSGarrett D'Amore 92195c635efSGarrett D'Amore static void 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 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