1*a40ea1a7SYuri Pankov /* $Id: main.c,v 1.283 2017/02/17 14:31:52 schwarze Exp $ */ 295c635efSGarrett D'Amore /* 3260e9a87SYuri Pankov * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4a5934736SYuri Pankov * Copyright (c) 2010-2012, 2014-2017 Ingo Schwarze <schwarze@openbsd.org> 5260e9a87SYuri 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 * 11371584c2SYuri Pankov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1295c635efSGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13371584c2SYuri Pankov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS 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" 20260e9a87SYuri Pankov 21260e9a87SYuri Pankov #include <sys/types.h> 22260e9a87SYuri Pankov #include <sys/param.h> /* MACHINE */ 23260e9a87SYuri Pankov #include <sys/wait.h> 2495c635efSGarrett D'Amore 2595c635efSGarrett D'Amore #include <assert.h> 26260e9a87SYuri Pankov #include <ctype.h> 27371584c2SYuri Pankov #if HAVE_ERR 28371584c2SYuri Pankov #include <err.h> 29371584c2SYuri Pankov #endif 30260e9a87SYuri Pankov #include <errno.h> 31260e9a87SYuri Pankov #include <fcntl.h> 32260e9a87SYuri Pankov #include <glob.h> 33371584c2SYuri Pankov #if HAVE_SANDBOX_INIT 34371584c2SYuri Pankov #include <sandbox.h> 35371584c2SYuri Pankov #endif 36371584c2SYuri Pankov #include <signal.h> 3795c635efSGarrett D'Amore #include <stdio.h> 3895c635efSGarrett D'Amore #include <stdint.h> 3995c635efSGarrett D'Amore #include <stdlib.h> 4095c635efSGarrett D'Amore #include <string.h> 41371584c2SYuri Pankov #include <time.h> 4295c635efSGarrett D'Amore #include <unistd.h> 4395c635efSGarrett D'Amore 44260e9a87SYuri Pankov #include "mandoc_aux.h" 45371584c2SYuri Pankov #include "mandoc.h" 46371584c2SYuri Pankov #include "roff.h" 4795c635efSGarrett D'Amore #include "mdoc.h" 4895c635efSGarrett D'Amore #include "man.h" 49371584c2SYuri Pankov #include "tag.h" 50371584c2SYuri Pankov #include "main.h" 51371584c2SYuri Pankov #include "manconf.h" 52260e9a87SYuri Pankov #include "mansearch.h" 5395c635efSGarrett D'Amore 54260e9a87SYuri Pankov enum outmode { 55260e9a87SYuri Pankov OUTMODE_DEF = 0, 56260e9a87SYuri Pankov OUTMODE_FLN, 57260e9a87SYuri Pankov OUTMODE_LST, 58260e9a87SYuri Pankov OUTMODE_ALL, 59260e9a87SYuri Pankov OUTMODE_INT, 60260e9a87SYuri Pankov OUTMODE_ONE 61260e9a87SYuri Pankov }; 62260e9a87SYuri Pankov 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; 7795c635efSGarrett D'Amore enum mandoclevel wlevel; /* ignore messages below this */ 7895c635efSGarrett D'Amore int wstop; /* stop after a file with a warning */ 79260e9a87SYuri Pankov enum outt outtype; /* which output to use */ 8095c635efSGarrett D'Amore void *outdata; /* data for output */ 81371584c2SYuri Pankov struct manoutput *outopts; /* output options */ 8295c635efSGarrett D'Amore }; 8395c635efSGarrett D'Amore 84a5934736SYuri Pankov 85a5934736SYuri Pankov int mandocdb(int, char *[]); 86a5934736SYuri Pankov 87260e9a87SYuri Pankov static int fs_lookup(const struct manpaths *, 88260e9a87SYuri Pankov size_t ipath, const char *, 89260e9a87SYuri Pankov const char *, const char *, 90260e9a87SYuri Pankov struct manpage **, size_t *); 91260e9a87SYuri Pankov static void fs_search(const struct mansearch *, 92260e9a87SYuri Pankov const struct manpaths *, int, char**, 93260e9a87SYuri Pankov struct manpage **, size_t *); 94260e9a87SYuri Pankov static int koptions(int *, char *); 95260e9a87SYuri Pankov static int moptions(int *, char *); 9695c635efSGarrett D'Amore static void mmsg(enum mandocerr, enum mandoclevel, 9795c635efSGarrett D'Amore const char *, int, int, const char *); 98a5934736SYuri Pankov static void outdata_alloc(struct curparse *); 99371584c2SYuri Pankov static void parse(struct curparse *, int, const char *); 100371584c2SYuri Pankov static void passthrough(const char *, int, int); 101371584c2SYuri Pankov static pid_t spawn_pager(struct tag_files *); 10295c635efSGarrett D'Amore static int toptions(struct curparse *, char *); 103*a40ea1a7SYuri Pankov static void usage(enum argmode) __attribute__((__noreturn__)); 10495c635efSGarrett D'Amore static int woptions(struct curparse *, char *); 10595c635efSGarrett D'Amore 106260e9a87SYuri Pankov static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; 107260e9a87SYuri Pankov static char help_arg[] = "help"; 108260e9a87SYuri Pankov static char *help_argv[] = {help_arg, NULL}; 109371584c2SYuri Pankov static enum mandoclevel rc; 11095c635efSGarrett D'Amore 111260e9a87SYuri Pankov 11295c635efSGarrett D'Amore int 11395c635efSGarrett D'Amore main(int argc, char *argv[]) 11495c635efSGarrett D'Amore { 115371584c2SYuri Pankov struct manconf conf; 116260e9a87SYuri Pankov struct mansearch search; 117*a40ea1a7SYuri Pankov struct curparse curp; 118371584c2SYuri Pankov struct tag_files *tag_files; 119260e9a87SYuri Pankov struct manpage *res, *resp; 120*a40ea1a7SYuri Pankov const char *progname, *sec, *thisarg; 121*a40ea1a7SYuri Pankov char *conf_file, *defpaths, *auxpaths; 122*a40ea1a7SYuri Pankov char *defos, *oarg; 123*a40ea1a7SYuri Pankov unsigned char *uc; 124371584c2SYuri Pankov size_t i, sz; 125371584c2SYuri Pankov int prio, best_prio; 126260e9a87SYuri Pankov enum outmode outmode; 127260e9a87SYuri Pankov int fd; 128260e9a87SYuri Pankov int show_usage; 129260e9a87SYuri Pankov int options; 130371584c2SYuri Pankov int use_pager; 131371584c2SYuri Pankov int status, signum; 132260e9a87SYuri Pankov int c; 133371584c2SYuri Pankov pid_t pager_pid, tc_pgid, man_pgid, pid; 13495c635efSGarrett D'Amore 135371584c2SYuri Pankov #if HAVE_PROGNAME 136371584c2SYuri Pankov progname = getprogname(); 137371584c2SYuri Pankov #else 138260e9a87SYuri Pankov if (argc < 1) 139371584c2SYuri Pankov progname = mandoc_strdup("mandoc"); 140260e9a87SYuri Pankov else if ((progname = strrchr(argv[0], '/')) == NULL) 14195c635efSGarrett D'Amore progname = argv[0]; 14295c635efSGarrett D'Amore else 14395c635efSGarrett D'Amore ++progname; 144371584c2SYuri Pankov setprogname(progname); 145371584c2SYuri Pankov #endif 14695c635efSGarrett D'Amore 147371584c2SYuri Pankov if (strncmp(progname, "mandocdb", 8) == 0 || 148371584c2SYuri Pankov strcmp(progname, BINM_MAKEWHATIS) == 0) 149371584c2SYuri Pankov return mandocdb(argc, argv); 150371584c2SYuri Pankov 151371584c2SYuri Pankov #if HAVE_PLEDGE 152371584c2SYuri Pankov if (pledge("stdio rpath tmppath tty proc exec flock", NULL) == -1) 153371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 154371584c2SYuri Pankov #endif 155371584c2SYuri Pankov 156371584c2SYuri Pankov #if HAVE_SANDBOX_INIT 157371584c2SYuri Pankov if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) 158371584c2SYuri Pankov errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); 159260e9a87SYuri Pankov #endif 160260e9a87SYuri Pankov 161260e9a87SYuri Pankov /* Search options. */ 16295c635efSGarrett D'Amore 163371584c2SYuri Pankov memset(&conf, 0, sizeof(conf)); 164260e9a87SYuri Pankov conf_file = defpaths = NULL; 165260e9a87SYuri Pankov auxpaths = NULL; 166260e9a87SYuri Pankov 167260e9a87SYuri Pankov memset(&search, 0, sizeof(struct mansearch)); 168260e9a87SYuri Pankov search.outkey = "Nd"; 169*a40ea1a7SYuri Pankov oarg = NULL; 170260e9a87SYuri Pankov 171260e9a87SYuri Pankov if (strcmp(progname, BINM_MAN) == 0) 172260e9a87SYuri Pankov search.argmode = ARG_NAME; 173260e9a87SYuri Pankov else if (strcmp(progname, BINM_APROPOS) == 0) 174260e9a87SYuri Pankov search.argmode = ARG_EXPR; 175260e9a87SYuri Pankov else if (strcmp(progname, BINM_WHATIS) == 0) 176260e9a87SYuri Pankov search.argmode = ARG_WORD; 177260e9a87SYuri Pankov else if (strncmp(progname, "help", 4) == 0) 178260e9a87SYuri Pankov search.argmode = ARG_NAME; 179260e9a87SYuri Pankov else 180260e9a87SYuri Pankov search.argmode = ARG_FILE; 181260e9a87SYuri Pankov 182260e9a87SYuri Pankov /* Parser and formatter options. */ 183260e9a87SYuri Pankov 184260e9a87SYuri Pankov memset(&curp, 0, sizeof(struct curparse)); 185260e9a87SYuri Pankov curp.outtype = OUTT_LOCALE; 186260e9a87SYuri Pankov curp.wlevel = MANDOCLEVEL_BADARG; 187371584c2SYuri Pankov curp.outopts = &conf.output; 188260e9a87SYuri Pankov options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; 189698f87a4SGarrett D'Amore defos = NULL; 19095c635efSGarrett D'Amore 191371584c2SYuri Pankov use_pager = 1; 192371584c2SYuri Pankov tag_files = NULL; 193260e9a87SYuri Pankov show_usage = 0; 194260e9a87SYuri Pankov outmode = OUTMODE_DEF; 195260e9a87SYuri Pankov 196260e9a87SYuri Pankov while (-1 != (c = getopt(argc, argv, 197260e9a87SYuri Pankov "aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) { 19895c635efSGarrett D'Amore switch (c) { 199260e9a87SYuri Pankov case 'a': 200260e9a87SYuri Pankov outmode = OUTMODE_ALL; 201260e9a87SYuri Pankov break; 202260e9a87SYuri Pankov case 'C': 203260e9a87SYuri Pankov conf_file = optarg; 204260e9a87SYuri Pankov break; 205260e9a87SYuri Pankov case 'c': 206371584c2SYuri Pankov use_pager = 0; 207260e9a87SYuri Pankov break; 208260e9a87SYuri Pankov case 'f': 209260e9a87SYuri Pankov search.argmode = ARG_WORD; 210260e9a87SYuri Pankov break; 211260e9a87SYuri Pankov case 'h': 212371584c2SYuri Pankov conf.output.synopsisonly = 1; 213371584c2SYuri Pankov use_pager = 0; 214260e9a87SYuri Pankov outmode = OUTMODE_ALL; 215260e9a87SYuri Pankov break; 216260e9a87SYuri Pankov case 'I': 217698f87a4SGarrett D'Amore if (strncmp(optarg, "os=", 3)) { 218371584c2SYuri Pankov warnx("-I %s: Bad argument", optarg); 219371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 220698f87a4SGarrett D'Amore } 221698f87a4SGarrett D'Amore if (defos) { 222371584c2SYuri Pankov warnx("-I %s: Duplicate argument", optarg); 223371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 224698f87a4SGarrett D'Amore } 225698f87a4SGarrett D'Amore defos = mandoc_strdup(optarg + 3); 226698f87a4SGarrett D'Amore break; 227260e9a87SYuri Pankov case 'i': 228260e9a87SYuri Pankov outmode = OUTMODE_INT; 229260e9a87SYuri Pankov break; 230260e9a87SYuri Pankov case 'K': 231260e9a87SYuri Pankov if ( ! koptions(&options, optarg)) 232371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 23395c635efSGarrett D'Amore break; 234260e9a87SYuri Pankov case 'k': 235260e9a87SYuri Pankov search.argmode = ARG_EXPR; 236260e9a87SYuri Pankov break; 237260e9a87SYuri Pankov case 'l': 238260e9a87SYuri Pankov search.argmode = ARG_FILE; 239260e9a87SYuri Pankov outmode = OUTMODE_ALL; 240260e9a87SYuri Pankov break; 241260e9a87SYuri Pankov case 'M': 242260e9a87SYuri Pankov defpaths = optarg; 243260e9a87SYuri Pankov break; 244260e9a87SYuri Pankov case 'm': 245260e9a87SYuri Pankov auxpaths = optarg; 246260e9a87SYuri Pankov break; 247260e9a87SYuri Pankov case 'O': 248*a40ea1a7SYuri Pankov oarg = optarg; 24995c635efSGarrett D'Amore break; 250260e9a87SYuri Pankov case 'S': 251260e9a87SYuri Pankov search.arch = optarg; 252260e9a87SYuri Pankov break; 253260e9a87SYuri Pankov case 's': 254260e9a87SYuri Pankov search.sec = optarg; 255260e9a87SYuri Pankov break; 256260e9a87SYuri Pankov case 'T': 25795c635efSGarrett D'Amore if ( ! toptions(&curp, optarg)) 258371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 25995c635efSGarrett D'Amore break; 260260e9a87SYuri Pankov case 'W': 26195c635efSGarrett D'Amore if ( ! woptions(&curp, optarg)) 262371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 26395c635efSGarrett D'Amore break; 264260e9a87SYuri Pankov case 'w': 265260e9a87SYuri Pankov outmode = OUTMODE_FLN; 266260e9a87SYuri Pankov break; 26795c635efSGarrett D'Amore default: 268260e9a87SYuri Pankov show_usage = 1; 269260e9a87SYuri Pankov break; 27095c635efSGarrett D'Amore } 271260e9a87SYuri Pankov } 272260e9a87SYuri Pankov 273260e9a87SYuri Pankov if (show_usage) 274260e9a87SYuri Pankov usage(search.argmode); 27595c635efSGarrett D'Amore 276260e9a87SYuri Pankov /* Postprocess options. */ 277260e9a87SYuri Pankov 278260e9a87SYuri Pankov if (outmode == OUTMODE_DEF) { 279260e9a87SYuri Pankov switch (search.argmode) { 280260e9a87SYuri Pankov case ARG_FILE: 281260e9a87SYuri Pankov outmode = OUTMODE_ALL; 282371584c2SYuri Pankov use_pager = 0; 283260e9a87SYuri Pankov break; 284260e9a87SYuri Pankov case ARG_NAME: 285260e9a87SYuri Pankov outmode = OUTMODE_ONE; 286260e9a87SYuri Pankov break; 287260e9a87SYuri Pankov default: 288260e9a87SYuri Pankov outmode = OUTMODE_LST; 289260e9a87SYuri Pankov break; 290260e9a87SYuri Pankov } 291260e9a87SYuri Pankov } 292260e9a87SYuri Pankov 293*a40ea1a7SYuri Pankov if (oarg != NULL) { 294*a40ea1a7SYuri Pankov if (outmode == OUTMODE_LST) 295*a40ea1a7SYuri Pankov search.outkey = oarg; 296*a40ea1a7SYuri Pankov else { 297*a40ea1a7SYuri Pankov while (oarg != NULL) { 298*a40ea1a7SYuri Pankov thisarg = oarg; 299*a40ea1a7SYuri Pankov if (manconf_output(&conf.output, 300*a40ea1a7SYuri Pankov strsep(&oarg, ","), 0) == 0) 301*a40ea1a7SYuri Pankov continue; 302*a40ea1a7SYuri Pankov warnx("-O %s: Bad argument", thisarg); 303*a40ea1a7SYuri Pankov return (int)MANDOCLEVEL_BADARG; 304*a40ea1a7SYuri Pankov } 305*a40ea1a7SYuri Pankov } 306*a40ea1a7SYuri Pankov } 307*a40ea1a7SYuri Pankov 308371584c2SYuri Pankov if (outmode == OUTMODE_FLN || 309371584c2SYuri Pankov outmode == OUTMODE_LST || 310371584c2SYuri Pankov !isatty(STDOUT_FILENO)) 311371584c2SYuri Pankov use_pager = 0; 312371584c2SYuri Pankov 313371584c2SYuri Pankov #if HAVE_PLEDGE 314371584c2SYuri Pankov if (!use_pager) 315371584c2SYuri Pankov if (pledge("stdio rpath flock", NULL) == -1) 316371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 317371584c2SYuri Pankov #endif 318371584c2SYuri Pankov 319260e9a87SYuri Pankov /* Parse arguments. */ 320260e9a87SYuri Pankov 321260e9a87SYuri Pankov if (argc > 0) { 322260e9a87SYuri Pankov argc -= optind; 323260e9a87SYuri Pankov argv += optind; 324260e9a87SYuri Pankov } 325260e9a87SYuri Pankov resp = NULL; 326260e9a87SYuri Pankov 327260e9a87SYuri Pankov /* 328260e9a87SYuri Pankov * Quirks for help(1) 329260e9a87SYuri Pankov * and for a man(1) section argument without -s. 330260e9a87SYuri Pankov */ 331260e9a87SYuri Pankov 332260e9a87SYuri Pankov if (search.argmode == ARG_NAME) { 333260e9a87SYuri Pankov if (*progname == 'h') { 334260e9a87SYuri Pankov if (argc == 0) { 335260e9a87SYuri Pankov argv = help_argv; 336260e9a87SYuri Pankov argc = 1; 337260e9a87SYuri Pankov } 338260e9a87SYuri Pankov } else if (argc > 1 && 339260e9a87SYuri Pankov ((uc = (unsigned char *)argv[0]) != NULL) && 340260e9a87SYuri Pankov ((isdigit(uc[0]) && (uc[1] == '\0' || 341260e9a87SYuri Pankov (isalpha(uc[1]) && uc[2] == '\0'))) || 342260e9a87SYuri Pankov (uc[0] == 'n' && uc[1] == '\0'))) { 343260e9a87SYuri Pankov search.sec = (char *)uc; 344260e9a87SYuri Pankov argv++; 345260e9a87SYuri Pankov argc--; 346260e9a87SYuri Pankov } 347260e9a87SYuri Pankov if (search.arch == NULL) 348260e9a87SYuri Pankov search.arch = getenv("MACHINE"); 349260e9a87SYuri Pankov #ifdef MACHINE 350260e9a87SYuri Pankov if (search.arch == NULL) 351260e9a87SYuri Pankov search.arch = MACHINE; 352260e9a87SYuri Pankov #endif 353260e9a87SYuri Pankov } 354260e9a87SYuri Pankov 355260e9a87SYuri Pankov rc = MANDOCLEVEL_OK; 356260e9a87SYuri Pankov 357260e9a87SYuri Pankov /* man(1), whatis(1), apropos(1) */ 358260e9a87SYuri Pankov 359260e9a87SYuri Pankov if (search.argmode != ARG_FILE) { 360260e9a87SYuri Pankov if (search.argmode == ARG_NAME && 361260e9a87SYuri Pankov outmode == OUTMODE_ONE) 362260e9a87SYuri Pankov search.firstmatch = 1; 363260e9a87SYuri Pankov 364260e9a87SYuri Pankov /* Access the mandoc database. */ 365260e9a87SYuri Pankov 366371584c2SYuri Pankov manconf_parse(&conf, conf_file, defpaths, auxpaths); 367371584c2SYuri Pankov if ( ! mansearch(&search, &conf.manpath, 368371584c2SYuri Pankov argc, argv, &res, &sz)) 369260e9a87SYuri Pankov usage(search.argmode); 370260e9a87SYuri Pankov 371371584c2SYuri Pankov if (sz == 0) { 372371584c2SYuri Pankov if (search.argmode == ARG_NAME) 373371584c2SYuri Pankov fs_search(&search, &conf.manpath, 374371584c2SYuri Pankov argc, argv, &res, &sz); 375371584c2SYuri Pankov else 376371584c2SYuri Pankov warnx("nothing appropriate"); 377371584c2SYuri Pankov } 378260e9a87SYuri Pankov 379260e9a87SYuri Pankov if (sz == 0) { 380260e9a87SYuri Pankov rc = MANDOCLEVEL_BADARG; 381260e9a87SYuri Pankov goto out; 382260e9a87SYuri Pankov } 383260e9a87SYuri Pankov 384260e9a87SYuri Pankov /* 385260e9a87SYuri Pankov * For standard man(1) and -a output mode, 386260e9a87SYuri Pankov * prepare for copying filename pointers 387260e9a87SYuri Pankov * into the program parameter array. 388260e9a87SYuri Pankov */ 389260e9a87SYuri Pankov 390260e9a87SYuri Pankov if (outmode == OUTMODE_ONE) { 391260e9a87SYuri Pankov argc = 1; 392371584c2SYuri Pankov best_prio = 20; 393260e9a87SYuri Pankov } else if (outmode == OUTMODE_ALL) 394260e9a87SYuri Pankov argc = (int)sz; 395260e9a87SYuri Pankov 396260e9a87SYuri Pankov /* Iterate all matching manuals. */ 397260e9a87SYuri Pankov 398260e9a87SYuri Pankov resp = res; 399260e9a87SYuri Pankov for (i = 0; i < sz; i++) { 400260e9a87SYuri Pankov if (outmode == OUTMODE_FLN) 401260e9a87SYuri Pankov puts(res[i].file); 402260e9a87SYuri Pankov else if (outmode == OUTMODE_LST) 403260e9a87SYuri Pankov printf("%s - %s\n", res[i].names, 404260e9a87SYuri Pankov res[i].output == NULL ? "" : 405260e9a87SYuri Pankov res[i].output); 406260e9a87SYuri Pankov else if (outmode == OUTMODE_ONE) { 407260e9a87SYuri Pankov /* Search for the best section. */ 408371584c2SYuri Pankov sec = res[i].file; 409371584c2SYuri Pankov sec += strcspn(sec, "123456789"); 410371584c2SYuri Pankov if (sec[0] == '\0') 411260e9a87SYuri Pankov continue; 412371584c2SYuri Pankov prio = sec_prios[sec[0] - '1']; 413371584c2SYuri Pankov if (sec[1] != '/') 414371584c2SYuri Pankov prio += 10; 415260e9a87SYuri Pankov if (prio >= best_prio) 416260e9a87SYuri Pankov continue; 417260e9a87SYuri Pankov best_prio = prio; 418260e9a87SYuri Pankov resp = res + i; 419260e9a87SYuri Pankov } 420260e9a87SYuri Pankov } 421260e9a87SYuri Pankov 422260e9a87SYuri Pankov /* 423260e9a87SYuri Pankov * For man(1), -a and -i output mode, fall through 424260e9a87SYuri Pankov * to the main mandoc(1) code iterating files 425260e9a87SYuri Pankov * and running the parsers on each of them. 426260e9a87SYuri Pankov */ 427260e9a87SYuri Pankov 428260e9a87SYuri Pankov if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) 429260e9a87SYuri Pankov goto out; 430260e9a87SYuri Pankov } 431260e9a87SYuri Pankov 432260e9a87SYuri Pankov /* mandoc(1) */ 433260e9a87SYuri Pankov 434371584c2SYuri Pankov #if HAVE_PLEDGE 435371584c2SYuri Pankov if (use_pager) { 436371584c2SYuri Pankov if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) 437371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 438371584c2SYuri Pankov } else { 439371584c2SYuri Pankov if (pledge("stdio rpath", NULL) == -1) 440371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 441371584c2SYuri Pankov } 442371584c2SYuri Pankov #endif 443371584c2SYuri Pankov 444260e9a87SYuri Pankov if (search.argmode == ARG_FILE && ! moptions(&options, auxpaths)) 445371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 446260e9a87SYuri Pankov 447371584c2SYuri Pankov mchars_alloc(); 448371584c2SYuri Pankov curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos); 44995c635efSGarrett D'Amore 45095c635efSGarrett D'Amore /* 45195c635efSGarrett D'Amore * Conditionally start up the lookaside buffer before parsing. 45295c635efSGarrett D'Amore */ 45395c635efSGarrett D'Amore if (OUTT_MAN == curp.outtype) 45495c635efSGarrett D'Amore mparse_keep(curp.mp); 45595c635efSGarrett D'Amore 456260e9a87SYuri Pankov if (argc < 1) { 457371584c2SYuri Pankov if (use_pager) 458371584c2SYuri Pankov tag_files = tag_init(); 459371584c2SYuri Pankov parse(&curp, STDIN_FILENO, "<stdin>"); 460260e9a87SYuri Pankov } 46195c635efSGarrett D'Amore 462260e9a87SYuri Pankov while (argc > 0) { 463371584c2SYuri Pankov fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv); 464260e9a87SYuri Pankov if (fd != -1) { 465371584c2SYuri Pankov if (use_pager) { 466371584c2SYuri Pankov tag_files = tag_init(); 467371584c2SYuri Pankov use_pager = 0; 468371584c2SYuri Pankov } 469260e9a87SYuri Pankov 470260e9a87SYuri Pankov if (resp == NULL) 471371584c2SYuri Pankov parse(&curp, fd, *argv); 472*a40ea1a7SYuri Pankov else if (resp->form == FORM_SRC) { 473260e9a87SYuri Pankov /* For .so only; ignore failure. */ 474371584c2SYuri Pankov chdir(conf.manpath.paths[resp->ipath]); 475371584c2SYuri Pankov parse(&curp, fd, resp->file); 476371584c2SYuri Pankov } else 477371584c2SYuri Pankov passthrough(resp->file, fd, 478371584c2SYuri Pankov conf.output.synopsisonly); 479260e9a87SYuri Pankov 480a5934736SYuri Pankov if (argc > 1 && curp.outtype <= OUTT_UTF8) { 481a5934736SYuri Pankov if (curp.outdata == NULL) 482a5934736SYuri Pankov outdata_alloc(&curp); 483371584c2SYuri Pankov terminal_sepline(curp.outdata); 484a5934736SYuri Pankov } 485371584c2SYuri Pankov } else if (rc < MANDOCLEVEL_ERROR) 486371584c2SYuri Pankov rc = MANDOCLEVEL_ERROR; 48795c635efSGarrett D'Amore 48895c635efSGarrett D'Amore if (MANDOCLEVEL_OK != rc && curp.wstop) 48995c635efSGarrett D'Amore break; 490260e9a87SYuri Pankov 491260e9a87SYuri Pankov if (resp != NULL) 492260e9a87SYuri Pankov resp++; 493260e9a87SYuri Pankov else 494260e9a87SYuri Pankov argv++; 495260e9a87SYuri Pankov if (--argc) 496260e9a87SYuri Pankov mparse_reset(curp.mp); 49795c635efSGarrett D'Amore } 49895c635efSGarrett D'Amore 499371584c2SYuri Pankov if (curp.outdata != NULL) { 500371584c2SYuri Pankov switch (curp.outtype) { 501371584c2SYuri Pankov case OUTT_HTML: 502371584c2SYuri Pankov html_free(curp.outdata); 503371584c2SYuri Pankov break; 504371584c2SYuri Pankov case OUTT_UTF8: 505371584c2SYuri Pankov case OUTT_LOCALE: 506371584c2SYuri Pankov case OUTT_ASCII: 507371584c2SYuri Pankov ascii_free(curp.outdata); 508371584c2SYuri Pankov break; 509371584c2SYuri Pankov case OUTT_PDF: 510371584c2SYuri Pankov case OUTT_PS: 511371584c2SYuri Pankov pspdf_free(curp.outdata); 512371584c2SYuri Pankov break; 513371584c2SYuri Pankov default: 514371584c2SYuri Pankov break; 515371584c2SYuri Pankov } 516371584c2SYuri Pankov } 517260e9a87SYuri Pankov mparse_free(curp.mp); 518371584c2SYuri Pankov mchars_free(); 519260e9a87SYuri Pankov 520260e9a87SYuri Pankov out: 521260e9a87SYuri Pankov if (search.argmode != ARG_FILE) { 522371584c2SYuri Pankov manconf_free(&conf); 523260e9a87SYuri Pankov mansearch_free(res, sz); 524260e9a87SYuri Pankov } 525260e9a87SYuri Pankov 526698f87a4SGarrett D'Amore free(defos); 52795c635efSGarrett D'Amore 528260e9a87SYuri Pankov /* 529371584c2SYuri Pankov * When using a pager, finish writing both temporary files, 530371584c2SYuri Pankov * fork it, wait for the user to close it, and clean up. 531260e9a87SYuri Pankov */ 532260e9a87SYuri Pankov 533371584c2SYuri Pankov if (tag_files != NULL) { 534260e9a87SYuri Pankov fclose(stdout); 535371584c2SYuri Pankov tag_write(); 536371584c2SYuri Pankov man_pgid = getpgid(0); 537371584c2SYuri Pankov tag_files->tcpgid = man_pgid == getpid() ? 538371584c2SYuri Pankov getpgid(getppid()) : man_pgid; 539371584c2SYuri Pankov pager_pid = 0; 540371584c2SYuri Pankov signum = SIGSTOP; 541371584c2SYuri Pankov for (;;) { 542371584c2SYuri Pankov 543371584c2SYuri Pankov /* Stop here until moved to the foreground. */ 544371584c2SYuri Pankov 545*a40ea1a7SYuri Pankov tc_pgid = tcgetpgrp(tag_files->ofd); 546371584c2SYuri Pankov if (tc_pgid != man_pgid) { 547371584c2SYuri Pankov if (tc_pgid == pager_pid) { 548*a40ea1a7SYuri Pankov (void)tcsetpgrp(tag_files->ofd, 549371584c2SYuri Pankov man_pgid); 550371584c2SYuri Pankov if (signum == SIGTTIN) 551371584c2SYuri Pankov continue; 552371584c2SYuri Pankov } else 553371584c2SYuri Pankov tag_files->tcpgid = tc_pgid; 554371584c2SYuri Pankov kill(0, signum); 555371584c2SYuri Pankov continue; 556371584c2SYuri Pankov } 557371584c2SYuri Pankov 558371584c2SYuri Pankov /* Once in the foreground, activate the pager. */ 559371584c2SYuri Pankov 560371584c2SYuri Pankov if (pager_pid) { 561*a40ea1a7SYuri Pankov (void)tcsetpgrp(tag_files->ofd, pager_pid); 562371584c2SYuri Pankov kill(pager_pid, SIGCONT); 563371584c2SYuri Pankov } else 564371584c2SYuri Pankov pager_pid = spawn_pager(tag_files); 565371584c2SYuri Pankov 566371584c2SYuri Pankov /* Wait for the pager to stop or exit. */ 567371584c2SYuri Pankov 568371584c2SYuri Pankov while ((pid = waitpid(pager_pid, &status, 569371584c2SYuri Pankov WUNTRACED)) == -1 && errno == EINTR) 570371584c2SYuri Pankov continue; 571371584c2SYuri Pankov 572371584c2SYuri Pankov if (pid == -1) { 573371584c2SYuri Pankov warn("wait"); 574371584c2SYuri Pankov rc = MANDOCLEVEL_SYSERR; 575371584c2SYuri Pankov break; 576371584c2SYuri Pankov } 577371584c2SYuri Pankov if (!WIFSTOPPED(status)) 578371584c2SYuri Pankov break; 579371584c2SYuri Pankov 580371584c2SYuri Pankov signum = WSTOPSIG(status); 581371584c2SYuri Pankov } 582371584c2SYuri Pankov tag_unlink(); 583260e9a87SYuri Pankov } 584260e9a87SYuri Pankov 585371584c2SYuri Pankov return (int)rc; 58695c635efSGarrett D'Amore } 58795c635efSGarrett D'Amore 58895c635efSGarrett D'Amore static void 589260e9a87SYuri Pankov usage(enum argmode argmode) 59095c635efSGarrett D'Amore { 59195c635efSGarrett D'Amore 592260e9a87SYuri Pankov switch (argmode) { 593260e9a87SYuri Pankov case ARG_FILE: 594371584c2SYuri Pankov fputs("usage: mandoc [-acfhkl] [-I os=name] " 595371584c2SYuri Pankov "[-K encoding] [-mformat] [-O option]\n" 596371584c2SYuri Pankov "\t [-T output] [-W level] [file ...]\n", stderr); 597260e9a87SYuri Pankov break; 598260e9a87SYuri Pankov case ARG_NAME: 599260e9a87SYuri Pankov fputs("usage: man [-acfhklw] [-C file] [-I os=name] " 600260e9a87SYuri Pankov "[-K encoding] [-M path] [-m path]\n" 601260e9a87SYuri Pankov "\t [-O option=value] [-S subsection] [-s section] " 602260e9a87SYuri Pankov "[-T output] [-W level]\n" 603260e9a87SYuri Pankov "\t [section] name ...\n", stderr); 604260e9a87SYuri Pankov break; 605260e9a87SYuri Pankov case ARG_WORD: 606260e9a87SYuri Pankov fputs("usage: whatis [-acfhklw] [-C file] " 607260e9a87SYuri Pankov "[-M path] [-m path] [-O outkey] [-S arch]\n" 608260e9a87SYuri Pankov "\t [-s section] name ...\n", stderr); 609260e9a87SYuri Pankov break; 610260e9a87SYuri Pankov case ARG_EXPR: 611260e9a87SYuri Pankov fputs("usage: apropos [-acfhklw] [-C file] " 612260e9a87SYuri Pankov "[-M path] [-m path] [-O outkey] [-S arch]\n" 613260e9a87SYuri Pankov "\t [-s section] expression ...\n", stderr); 614260e9a87SYuri Pankov break; 615260e9a87SYuri Pankov } 616260e9a87SYuri Pankov exit((int)MANDOCLEVEL_BADARG); 61795c635efSGarrett D'Amore } 61895c635efSGarrett D'Amore 619260e9a87SYuri Pankov static int 620260e9a87SYuri Pankov fs_lookup(const struct manpaths *paths, size_t ipath, 621260e9a87SYuri Pankov const char *sec, const char *arch, const char *name, 622260e9a87SYuri Pankov struct manpage **res, size_t *ressz) 62395c635efSGarrett D'Amore { 624260e9a87SYuri Pankov glob_t globinfo; 625260e9a87SYuri Pankov struct manpage *page; 626260e9a87SYuri Pankov char *file; 627*a40ea1a7SYuri Pankov int globres; 628*a40ea1a7SYuri Pankov enum form form; 629260e9a87SYuri Pankov 630260e9a87SYuri Pankov form = FORM_SRC; 631260e9a87SYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s.%s", 632260e9a87SYuri Pankov paths->paths[ipath], sec, name, sec); 633260e9a87SYuri Pankov if (access(file, R_OK) != -1) 634260e9a87SYuri Pankov goto found; 635260e9a87SYuri Pankov free(file); 636260e9a87SYuri Pankov 637260e9a87SYuri Pankov mandoc_asprintf(&file, "%s/cat%s/%s.0", 638260e9a87SYuri Pankov paths->paths[ipath], sec, name); 639260e9a87SYuri Pankov if (access(file, R_OK) != -1) { 640260e9a87SYuri Pankov form = FORM_CAT; 641260e9a87SYuri Pankov goto found; 642260e9a87SYuri Pankov } 643260e9a87SYuri Pankov free(file); 644260e9a87SYuri Pankov 645260e9a87SYuri Pankov if (arch != NULL) { 646260e9a87SYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", 647260e9a87SYuri Pankov paths->paths[ipath], sec, arch, name, sec); 648260e9a87SYuri Pankov if (access(file, R_OK) != -1) 649260e9a87SYuri Pankov goto found; 650260e9a87SYuri Pankov free(file); 651260e9a87SYuri Pankov } 652260e9a87SYuri Pankov 653371584c2SYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", 654260e9a87SYuri Pankov paths->paths[ipath], sec, name); 655260e9a87SYuri Pankov globres = glob(file, 0, NULL, &globinfo); 656260e9a87SYuri Pankov if (globres != 0 && globres != GLOB_NOMATCH) 657371584c2SYuri Pankov warn("%s: glob", file); 658260e9a87SYuri Pankov free(file); 659260e9a87SYuri Pankov if (globres == 0) 660260e9a87SYuri Pankov file = mandoc_strdup(*globinfo.gl_pathv); 661260e9a87SYuri Pankov globfree(&globinfo); 662260e9a87SYuri Pankov if (globres != 0) 663371584c2SYuri Pankov return 0; 66495c635efSGarrett D'Amore 665260e9a87SYuri Pankov found: 666371584c2SYuri Pankov warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", 667371584c2SYuri Pankov name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); 668260e9a87SYuri Pankov *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); 669260e9a87SYuri Pankov page = *res + (*ressz - 1); 670260e9a87SYuri Pankov page->file = file; 671260e9a87SYuri Pankov page->names = NULL; 672260e9a87SYuri Pankov page->output = NULL; 673260e9a87SYuri Pankov page->ipath = ipath; 674260e9a87SYuri Pankov page->bits = NAME_FILE & NAME_MASK; 675260e9a87SYuri Pankov page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; 676260e9a87SYuri Pankov page->form = form; 677371584c2SYuri Pankov return 1; 678260e9a87SYuri Pankov } 679260e9a87SYuri Pankov 680260e9a87SYuri Pankov static void 681260e9a87SYuri Pankov fs_search(const struct mansearch *cfg, const struct manpaths *paths, 682260e9a87SYuri Pankov int argc, char **argv, struct manpage **res, size_t *ressz) 683260e9a87SYuri Pankov { 684260e9a87SYuri Pankov const char *const sections[] = 685371584c2SYuri Pankov {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; 686260e9a87SYuri Pankov const size_t nsec = sizeof(sections)/sizeof(sections[0]); 687260e9a87SYuri Pankov 688260e9a87SYuri Pankov size_t ipath, isec, lastsz; 689260e9a87SYuri Pankov 690260e9a87SYuri Pankov assert(cfg->argmode == ARG_NAME); 691260e9a87SYuri Pankov 692260e9a87SYuri Pankov *res = NULL; 693260e9a87SYuri Pankov *ressz = lastsz = 0; 694260e9a87SYuri Pankov while (argc) { 695260e9a87SYuri Pankov for (ipath = 0; ipath < paths->sz; ipath++) { 696260e9a87SYuri Pankov if (cfg->sec != NULL) { 697260e9a87SYuri Pankov if (fs_lookup(paths, ipath, cfg->sec, 698260e9a87SYuri Pankov cfg->arch, *argv, res, ressz) && 699260e9a87SYuri Pankov cfg->firstmatch) 700260e9a87SYuri Pankov return; 701260e9a87SYuri Pankov } else for (isec = 0; isec < nsec; isec++) 702260e9a87SYuri Pankov if (fs_lookup(paths, ipath, sections[isec], 703260e9a87SYuri Pankov cfg->arch, *argv, res, ressz) && 704260e9a87SYuri Pankov cfg->firstmatch) 705260e9a87SYuri Pankov return; 706260e9a87SYuri Pankov } 707260e9a87SYuri Pankov if (*ressz == lastsz) 708371584c2SYuri Pankov warnx("No entry for %s in the manual.", *argv); 709260e9a87SYuri Pankov lastsz = *ressz; 710260e9a87SYuri Pankov argv++; 711260e9a87SYuri Pankov argc--; 712260e9a87SYuri Pankov } 71395c635efSGarrett D'Amore } 71495c635efSGarrett D'Amore 71595c635efSGarrett D'Amore static void 716371584c2SYuri Pankov parse(struct curparse *curp, int fd, const char *file) 71795c635efSGarrett D'Amore { 718371584c2SYuri Pankov enum mandoclevel rctmp; 719371584c2SYuri Pankov struct roff_man *man; 72095c635efSGarrett D'Amore 72195c635efSGarrett D'Amore /* Begin by parsing the file itself. */ 72295c635efSGarrett D'Amore 72395c635efSGarrett D'Amore assert(file); 724371584c2SYuri Pankov assert(fd >= 0); 72595c635efSGarrett D'Amore 726371584c2SYuri Pankov rctmp = mparse_readfd(curp->mp, fd, file); 727371584c2SYuri Pankov if (fd != STDIN_FILENO) 728371584c2SYuri Pankov close(fd); 729371584c2SYuri Pankov if (rc < rctmp) 730371584c2SYuri Pankov rc = rctmp; 73195c635efSGarrett D'Amore 73295c635efSGarrett D'Amore /* 73395c635efSGarrett D'Amore * With -Wstop and warnings or errors of at least the requested 73495c635efSGarrett D'Amore * level, do not produce output. 73595c635efSGarrett D'Amore */ 73695c635efSGarrett D'Amore 737371584c2SYuri Pankov if (rctmp != MANDOCLEVEL_OK && curp->wstop) 738371584c2SYuri Pankov return; 73995c635efSGarrett D'Amore 740a5934736SYuri Pankov if (curp->outdata == NULL) 741a5934736SYuri Pankov outdata_alloc(curp); 742371584c2SYuri Pankov 743371584c2SYuri Pankov mparse_result(curp->mp, &man, NULL); 74495c635efSGarrett D'Amore 745371584c2SYuri Pankov /* Execute the out device, if it exists. */ 746371584c2SYuri Pankov 747371584c2SYuri Pankov if (man == NULL) 748371584c2SYuri Pankov return; 749371584c2SYuri Pankov if (man->macroset == MACROSET_MDOC) { 750*a40ea1a7SYuri Pankov if (curp->outtype != OUTT_TREE || !curp->outopts->noval) 751*a40ea1a7SYuri Pankov mdoc_validate(man); 75295c635efSGarrett D'Amore switch (curp->outtype) { 753260e9a87SYuri Pankov case OUTT_HTML: 754371584c2SYuri Pankov html_mdoc(curp->outdata, man); 75595c635efSGarrett D'Amore break; 756260e9a87SYuri Pankov case OUTT_TREE: 757371584c2SYuri Pankov tree_mdoc(curp->outdata, man); 75895c635efSGarrett D'Amore break; 759260e9a87SYuri Pankov case OUTT_MAN: 760371584c2SYuri Pankov man_mdoc(curp->outdata, man); 76195c635efSGarrett D'Amore break; 762260e9a87SYuri Pankov case OUTT_PDF: 763260e9a87SYuri Pankov case OUTT_ASCII: 764260e9a87SYuri Pankov case OUTT_UTF8: 765260e9a87SYuri Pankov case OUTT_LOCALE: 766260e9a87SYuri Pankov case OUTT_PS: 767371584c2SYuri Pankov terminal_mdoc(curp->outdata, man); 768371584c2SYuri Pankov break; 769371584c2SYuri Pankov default: 770371584c2SYuri Pankov break; 771371584c2SYuri Pankov } 772371584c2SYuri Pankov } 773371584c2SYuri Pankov if (man->macroset == MACROSET_MAN) { 774*a40ea1a7SYuri Pankov if (curp->outtype != OUTT_TREE || !curp->outopts->noval) 775*a40ea1a7SYuri Pankov man_validate(man); 776371584c2SYuri Pankov switch (curp->outtype) { 777371584c2SYuri Pankov case OUTT_HTML: 778371584c2SYuri Pankov html_man(curp->outdata, man); 779371584c2SYuri Pankov break; 780371584c2SYuri Pankov case OUTT_TREE: 781371584c2SYuri Pankov tree_man(curp->outdata, man); 782371584c2SYuri Pankov break; 783371584c2SYuri Pankov case OUTT_MAN: 784371584c2SYuri Pankov man_man(curp->outdata, man); 785371584c2SYuri Pankov break; 786371584c2SYuri Pankov case OUTT_PDF: 787371584c2SYuri Pankov case OUTT_ASCII: 788371584c2SYuri Pankov case OUTT_UTF8: 789371584c2SYuri Pankov case OUTT_LOCALE: 790371584c2SYuri Pankov case OUTT_PS: 791371584c2SYuri Pankov terminal_man(curp->outdata, man); 79295c635efSGarrett D'Amore break; 79395c635efSGarrett D'Amore default: 79495c635efSGarrett D'Amore break; 79595c635efSGarrett D'Amore } 79695c635efSGarrett D'Amore } 797a5934736SYuri Pankov mparse_updaterc(curp->mp, &rc); 798a5934736SYuri Pankov } 799a5934736SYuri Pankov 800a5934736SYuri Pankov static void 801a5934736SYuri Pankov outdata_alloc(struct curparse *curp) 802a5934736SYuri Pankov { 803a5934736SYuri Pankov switch (curp->outtype) { 804a5934736SYuri Pankov case OUTT_HTML: 805a5934736SYuri Pankov curp->outdata = html_alloc(curp->outopts); 806a5934736SYuri Pankov break; 807a5934736SYuri Pankov case OUTT_UTF8: 808a5934736SYuri Pankov curp->outdata = utf8_alloc(curp->outopts); 809a5934736SYuri Pankov break; 810a5934736SYuri Pankov case OUTT_LOCALE: 811a5934736SYuri Pankov curp->outdata = locale_alloc(curp->outopts); 812a5934736SYuri Pankov break; 813a5934736SYuri Pankov case OUTT_ASCII: 814a5934736SYuri Pankov curp->outdata = ascii_alloc(curp->outopts); 815a5934736SYuri Pankov break; 816a5934736SYuri Pankov case OUTT_PDF: 817a5934736SYuri Pankov curp->outdata = pdf_alloc(curp->outopts); 818a5934736SYuri Pankov break; 819a5934736SYuri Pankov case OUTT_PS: 820a5934736SYuri Pankov curp->outdata = ps_alloc(curp->outopts); 821a5934736SYuri Pankov break; 822a5934736SYuri Pankov default: 823a5934736SYuri Pankov break; 824a5934736SYuri Pankov } 82595c635efSGarrett D'Amore } 82695c635efSGarrett D'Amore 827371584c2SYuri Pankov static void 828260e9a87SYuri Pankov passthrough(const char *file, int fd, int synopsis_only) 829260e9a87SYuri Pankov { 830260e9a87SYuri Pankov const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; 831260e9a87SYuri Pankov const char synr[] = "SYNOPSIS"; 832260e9a87SYuri Pankov 833260e9a87SYuri Pankov FILE *stream; 834260e9a87SYuri Pankov const char *syscall; 835371584c2SYuri Pankov char *line, *cp; 836371584c2SYuri Pankov size_t linesz; 837a5934736SYuri Pankov ssize_t len, written; 838260e9a87SYuri Pankov int print; 839260e9a87SYuri Pankov 840371584c2SYuri Pankov line = NULL; 841371584c2SYuri Pankov linesz = 0; 842260e9a87SYuri Pankov 843a5934736SYuri Pankov if (fflush(stdout) == EOF) { 844a5934736SYuri Pankov syscall = "fflush"; 845a5934736SYuri Pankov goto fail; 846a5934736SYuri Pankov } 847a5934736SYuri Pankov 848260e9a87SYuri Pankov if ((stream = fdopen(fd, "r")) == NULL) { 849260e9a87SYuri Pankov close(fd); 850260e9a87SYuri Pankov syscall = "fdopen"; 851260e9a87SYuri Pankov goto fail; 852260e9a87SYuri Pankov } 853260e9a87SYuri Pankov 854260e9a87SYuri Pankov print = 0; 855a5934736SYuri Pankov while ((len = getline(&line, &linesz, stream)) != -1) { 856371584c2SYuri Pankov cp = line; 857260e9a87SYuri Pankov if (synopsis_only) { 858260e9a87SYuri Pankov if (print) { 859371584c2SYuri Pankov if ( ! isspace((unsigned char)*cp)) 860260e9a87SYuri Pankov goto done; 861a5934736SYuri Pankov while (isspace((unsigned char)*cp)) { 862371584c2SYuri Pankov cp++; 863a5934736SYuri Pankov len--; 864a5934736SYuri Pankov } 865260e9a87SYuri Pankov } else { 866371584c2SYuri Pankov if (strcmp(cp, synb) == 0 || 867371584c2SYuri Pankov strcmp(cp, synr) == 0) 868260e9a87SYuri Pankov print = 1; 869260e9a87SYuri Pankov continue; 870260e9a87SYuri Pankov } 871260e9a87SYuri Pankov } 872a5934736SYuri Pankov for (; len > 0; len -= written) { 873a5934736SYuri Pankov if ((written = write(STDOUT_FILENO, cp, len)) != -1) 874a5934736SYuri Pankov continue; 875371584c2SYuri Pankov fclose(stream); 876a5934736SYuri Pankov syscall = "write"; 877371584c2SYuri Pankov goto fail; 878371584c2SYuri Pankov } 879260e9a87SYuri Pankov } 880260e9a87SYuri Pankov 881260e9a87SYuri Pankov if (ferror(stream)) { 882260e9a87SYuri Pankov fclose(stream); 883371584c2SYuri Pankov syscall = "getline"; 884260e9a87SYuri Pankov goto fail; 885260e9a87SYuri Pankov } 886260e9a87SYuri Pankov 887260e9a87SYuri Pankov done: 888371584c2SYuri Pankov free(line); 889260e9a87SYuri Pankov fclose(stream); 890371584c2SYuri Pankov return; 891260e9a87SYuri Pankov 892260e9a87SYuri Pankov fail: 893371584c2SYuri Pankov free(line); 894371584c2SYuri Pankov warn("%s: SYSERR: %s", file, syscall); 895371584c2SYuri Pankov if (rc < MANDOCLEVEL_SYSERR) 896371584c2SYuri Pankov rc = MANDOCLEVEL_SYSERR; 897260e9a87SYuri Pankov } 898260e9a87SYuri Pankov 899260e9a87SYuri Pankov static int 900260e9a87SYuri Pankov koptions(int *options, char *arg) 901260e9a87SYuri Pankov { 902260e9a87SYuri Pankov 903260e9a87SYuri Pankov if ( ! strcmp(arg, "utf-8")) { 904260e9a87SYuri Pankov *options |= MPARSE_UTF8; 905260e9a87SYuri Pankov *options &= ~MPARSE_LATIN1; 906260e9a87SYuri Pankov } else if ( ! strcmp(arg, "iso-8859-1")) { 907260e9a87SYuri Pankov *options |= MPARSE_LATIN1; 908260e9a87SYuri Pankov *options &= ~MPARSE_UTF8; 909260e9a87SYuri Pankov } else if ( ! strcmp(arg, "us-ascii")) { 910260e9a87SYuri Pankov *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); 911260e9a87SYuri Pankov } else { 912371584c2SYuri Pankov warnx("-K %s: Bad argument", arg); 913371584c2SYuri Pankov return 0; 914260e9a87SYuri Pankov } 915371584c2SYuri Pankov return 1; 916260e9a87SYuri Pankov } 917260e9a87SYuri Pankov 91895c635efSGarrett D'Amore static int 919260e9a87SYuri Pankov moptions(int *options, char *arg) 92095c635efSGarrett D'Amore { 92195c635efSGarrett D'Amore 922260e9a87SYuri Pankov if (arg == NULL) 923260e9a87SYuri Pankov /* nothing to do */; 924260e9a87SYuri Pankov else if (0 == strcmp(arg, "doc")) 925260e9a87SYuri Pankov *options |= MPARSE_MDOC; 92695c635efSGarrett D'Amore else if (0 == strcmp(arg, "andoc")) 927260e9a87SYuri Pankov /* nothing to do */; 92895c635efSGarrett D'Amore else if (0 == strcmp(arg, "an")) 929260e9a87SYuri Pankov *options |= MPARSE_MAN; 93095c635efSGarrett D'Amore else { 931371584c2SYuri Pankov warnx("-m %s: Bad argument", arg); 932371584c2SYuri Pankov return 0; 93395c635efSGarrett D'Amore } 93495c635efSGarrett D'Amore 935371584c2SYuri Pankov return 1; 93695c635efSGarrett D'Amore } 93795c635efSGarrett D'Amore 93895c635efSGarrett D'Amore static int 93995c635efSGarrett D'Amore toptions(struct curparse *curp, char *arg) 94095c635efSGarrett D'Amore { 94195c635efSGarrett D'Amore 94295c635efSGarrett D'Amore if (0 == strcmp(arg, "ascii")) 94395c635efSGarrett D'Amore curp->outtype = OUTT_ASCII; 94495c635efSGarrett D'Amore else if (0 == strcmp(arg, "lint")) { 94595c635efSGarrett D'Amore curp->outtype = OUTT_LINT; 94695c635efSGarrett D'Amore curp->wlevel = MANDOCLEVEL_WARNING; 94795c635efSGarrett D'Amore } else if (0 == strcmp(arg, "tree")) 94895c635efSGarrett D'Amore curp->outtype = OUTT_TREE; 94995c635efSGarrett D'Amore else if (0 == strcmp(arg, "man")) 95095c635efSGarrett D'Amore curp->outtype = OUTT_MAN; 95195c635efSGarrett D'Amore else if (0 == strcmp(arg, "html")) 95295c635efSGarrett D'Amore curp->outtype = OUTT_HTML; 95395c635efSGarrett D'Amore else if (0 == strcmp(arg, "utf8")) 95495c635efSGarrett D'Amore curp->outtype = OUTT_UTF8; 95595c635efSGarrett D'Amore else if (0 == strcmp(arg, "locale")) 95695c635efSGarrett D'Amore curp->outtype = OUTT_LOCALE; 95795c635efSGarrett D'Amore else if (0 == strcmp(arg, "xhtml")) 958260e9a87SYuri Pankov curp->outtype = OUTT_HTML; 95995c635efSGarrett D'Amore else if (0 == strcmp(arg, "ps")) 96095c635efSGarrett D'Amore curp->outtype = OUTT_PS; 96195c635efSGarrett D'Amore else if (0 == strcmp(arg, "pdf")) 96295c635efSGarrett D'Amore curp->outtype = OUTT_PDF; 96395c635efSGarrett D'Amore else { 964371584c2SYuri Pankov warnx("-T %s: Bad argument", arg); 965371584c2SYuri Pankov return 0; 96695c635efSGarrett D'Amore } 96795c635efSGarrett D'Amore 968371584c2SYuri Pankov return 1; 96995c635efSGarrett D'Amore } 97095c635efSGarrett D'Amore 97195c635efSGarrett D'Amore static int 97295c635efSGarrett D'Amore woptions(struct curparse *curp, char *arg) 97395c635efSGarrett D'Amore { 97495c635efSGarrett D'Amore char *v, *o; 975260e9a87SYuri Pankov const char *toks[7]; 97695c635efSGarrett D'Amore 97795c635efSGarrett D'Amore toks[0] = "stop"; 97895c635efSGarrett D'Amore toks[1] = "all"; 97995c635efSGarrett D'Amore toks[2] = "warning"; 98095c635efSGarrett D'Amore toks[3] = "error"; 981260e9a87SYuri Pankov toks[4] = "unsupp"; 982260e9a87SYuri Pankov toks[5] = "fatal"; 983260e9a87SYuri Pankov toks[6] = NULL; 98495c635efSGarrett D'Amore 98595c635efSGarrett D'Amore while (*arg) { 98695c635efSGarrett D'Amore o = arg; 987a5934736SYuri Pankov switch (getsubopt(&arg, (char * const *)toks, &v)) { 988260e9a87SYuri Pankov case 0: 98995c635efSGarrett D'Amore curp->wstop = 1; 99095c635efSGarrett D'Amore break; 991260e9a87SYuri Pankov case 1: 992260e9a87SYuri Pankov case 2: 99395c635efSGarrett D'Amore curp->wlevel = MANDOCLEVEL_WARNING; 99495c635efSGarrett D'Amore break; 995260e9a87SYuri Pankov case 3: 99695c635efSGarrett D'Amore curp->wlevel = MANDOCLEVEL_ERROR; 99795c635efSGarrett D'Amore break; 998260e9a87SYuri Pankov case 4: 999260e9a87SYuri Pankov curp->wlevel = MANDOCLEVEL_UNSUPP; 1000260e9a87SYuri Pankov break; 1001260e9a87SYuri Pankov case 5: 1002260e9a87SYuri Pankov curp->wlevel = MANDOCLEVEL_BADARG; 100395c635efSGarrett D'Amore break; 100495c635efSGarrett D'Amore default: 1005371584c2SYuri Pankov warnx("-W %s: Bad argument", o); 1006371584c2SYuri Pankov return 0; 100795c635efSGarrett D'Amore } 100895c635efSGarrett D'Amore } 100995c635efSGarrett D'Amore 1010371584c2SYuri Pankov return 1; 101195c635efSGarrett D'Amore } 101295c635efSGarrett D'Amore 101395c635efSGarrett D'Amore static void 1014260e9a87SYuri Pankov mmsg(enum mandocerr t, enum mandoclevel lvl, 101595c635efSGarrett D'Amore const char *file, int line, int col, const char *msg) 101695c635efSGarrett D'Amore { 1017260e9a87SYuri Pankov const char *mparse_msg; 1018260e9a87SYuri Pankov 1019371584c2SYuri Pankov fprintf(stderr, "%s: %s:", getprogname(), 1020371584c2SYuri Pankov file == NULL ? "<stdin>" : file); 1021260e9a87SYuri Pankov 1022260e9a87SYuri Pankov if (line) 1023260e9a87SYuri Pankov fprintf(stderr, "%d:%d:", line, col + 1); 102495c635efSGarrett D'Amore 1025260e9a87SYuri Pankov fprintf(stderr, " %s", mparse_strlevel(lvl)); 1026260e9a87SYuri Pankov 1027260e9a87SYuri Pankov if (NULL != (mparse_msg = mparse_strerror(t))) 1028260e9a87SYuri Pankov fprintf(stderr, ": %s", mparse_msg); 102995c635efSGarrett D'Amore 103095c635efSGarrett D'Amore if (msg) 103195c635efSGarrett D'Amore fprintf(stderr, ": %s", msg); 103295c635efSGarrett D'Amore 103395c635efSGarrett D'Amore fputc('\n', stderr); 103495c635efSGarrett D'Amore } 1035260e9a87SYuri Pankov 1036260e9a87SYuri Pankov static pid_t 1037371584c2SYuri Pankov spawn_pager(struct tag_files *tag_files) 1038260e9a87SYuri Pankov { 1039371584c2SYuri Pankov const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ 1040260e9a87SYuri Pankov #define MAX_PAGER_ARGS 16 1041260e9a87SYuri Pankov char *argv[MAX_PAGER_ARGS]; 1042260e9a87SYuri Pankov const char *pager; 1043260e9a87SYuri Pankov char *cp; 1044371584c2SYuri Pankov size_t cmdlen; 1045260e9a87SYuri Pankov int argc; 1046260e9a87SYuri Pankov pid_t pager_pid; 1047260e9a87SYuri Pankov 1048260e9a87SYuri Pankov pager = getenv("MANPAGER"); 1049260e9a87SYuri Pankov if (pager == NULL || *pager == '\0') 1050260e9a87SYuri Pankov pager = getenv("PAGER"); 1051260e9a87SYuri Pankov if (pager == NULL || *pager == '\0') 1052371584c2SYuri Pankov pager = "more -s"; 1053260e9a87SYuri Pankov cp = mandoc_strdup(pager); 1054260e9a87SYuri Pankov 1055260e9a87SYuri Pankov /* 1056260e9a87SYuri Pankov * Parse the pager command into words. 1057260e9a87SYuri Pankov * Intentionally do not do anything fancy here. 1058260e9a87SYuri Pankov */ 1059260e9a87SYuri Pankov 1060260e9a87SYuri Pankov argc = 0; 1061371584c2SYuri Pankov while (argc + 4 < MAX_PAGER_ARGS) { 1062260e9a87SYuri Pankov argv[argc++] = cp; 1063260e9a87SYuri Pankov cp = strchr(cp, ' '); 1064260e9a87SYuri Pankov if (cp == NULL) 1065260e9a87SYuri Pankov break; 1066260e9a87SYuri Pankov *cp++ = '\0'; 1067260e9a87SYuri Pankov while (*cp == ' ') 1068260e9a87SYuri Pankov cp++; 1069260e9a87SYuri Pankov if (*cp == '\0') 1070260e9a87SYuri Pankov break; 1071260e9a87SYuri Pankov } 1072371584c2SYuri Pankov 1073371584c2SYuri Pankov /* For less(1), use the tag file. */ 1074371584c2SYuri Pankov 1075371584c2SYuri Pankov if ((cmdlen = strlen(argv[0])) >= 4) { 1076371584c2SYuri Pankov cp = argv[0] + cmdlen - 4; 1077371584c2SYuri Pankov if (strcmp(cp, "less") == 0) { 1078371584c2SYuri Pankov argv[argc++] = mandoc_strdup("-T"); 1079371584c2SYuri Pankov argv[argc++] = tag_files->tfn; 1080371584c2SYuri Pankov } 1081371584c2SYuri Pankov } 1082371584c2SYuri Pankov argv[argc++] = tag_files->ofn; 1083260e9a87SYuri Pankov argv[argc] = NULL; 1084260e9a87SYuri Pankov 1085371584c2SYuri Pankov switch (pager_pid = fork()) { 1086371584c2SYuri Pankov case -1: 1087371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "fork"); 1088371584c2SYuri Pankov case 0: 1089371584c2SYuri Pankov break; 1090371584c2SYuri Pankov default: 1091371584c2SYuri Pankov (void)setpgid(pager_pid, 0); 1092*a40ea1a7SYuri Pankov (void)tcsetpgrp(tag_files->ofd, pager_pid); 1093371584c2SYuri Pankov #if HAVE_PLEDGE 1094371584c2SYuri Pankov if (pledge("stdio rpath tmppath tty proc", NULL) == -1) 1095371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 1096371584c2SYuri Pankov #endif 1097371584c2SYuri Pankov tag_files->pager_pid = pager_pid; 1098371584c2SYuri Pankov return pager_pid; 1099371584c2SYuri Pankov } 1100371584c2SYuri Pankov 1101371584c2SYuri Pankov /* The child process becomes the pager. */ 1102371584c2SYuri Pankov 1103371584c2SYuri Pankov if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) 1104371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pager stdout"); 1105371584c2SYuri Pankov close(tag_files->ofd); 1106371584c2SYuri Pankov close(tag_files->tfd); 1107371584c2SYuri Pankov 1108371584c2SYuri Pankov /* Do not start the pager before controlling the terminal. */ 1109371584c2SYuri Pankov 1110*a40ea1a7SYuri Pankov while (tcgetpgrp(STDOUT_FILENO) != getpid()) 1111371584c2SYuri Pankov nanosleep(&timeout, NULL); 1112260e9a87SYuri Pankov 1113260e9a87SYuri Pankov execvp(argv[0], argv); 1114371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]); 1115260e9a87SYuri Pankov } 1116