1*c66b8046SYuri Pankov /* $Id: main.c,v 1.301 2017/07/26 10:21:55 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" 46*c66b8046SYuri Pankov #include "mandoc_xr.h" 47371584c2SYuri Pankov #include "roff.h" 4895c635efSGarrett D'Amore #include "mdoc.h" 4995c635efSGarrett D'Amore #include "man.h" 50371584c2SYuri Pankov #include "tag.h" 51371584c2SYuri Pankov #include "main.h" 52371584c2SYuri Pankov #include "manconf.h" 53260e9a87SYuri Pankov #include "mansearch.h" 5495c635efSGarrett D'Amore 55260e9a87SYuri Pankov enum outmode { 56260e9a87SYuri Pankov OUTMODE_DEF = 0, 57260e9a87SYuri Pankov OUTMODE_FLN, 58260e9a87SYuri Pankov OUTMODE_LST, 59260e9a87SYuri Pankov OUTMODE_ALL, 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 */ 70*c66b8046SYuri Pankov OUTT_MARKDOWN, /* -Tmarkdown */ 7195c635efSGarrett D'Amore OUTT_LINT, /* -Tlint */ 7295c635efSGarrett D'Amore OUTT_PS, /* -Tps */ 7395c635efSGarrett D'Amore OUTT_PDF /* -Tpdf */ 7495c635efSGarrett D'Amore }; 7595c635efSGarrett D'Amore 7695c635efSGarrett D'Amore struct curparse { 7795c635efSGarrett D'Amore struct mparse *mp; 78*c66b8046SYuri Pankov struct manoutput *outopts; /* output options */ 79*c66b8046SYuri Pankov void *outdata; /* data for output */ 80*c66b8046SYuri Pankov char *os_s; /* operating system for display */ 8195c635efSGarrett D'Amore int wstop; /* stop after a file with a warning */ 82*c66b8046SYuri Pankov enum mandocerr mmin; /* ignore messages below this */ 83*c66b8046SYuri Pankov enum mandoc_os os_e; /* check base system conventions */ 84260e9a87SYuri Pankov enum outt outtype; /* which output to use */ 8595c635efSGarrett D'Amore }; 8695c635efSGarrett D'Amore 87a5934736SYuri Pankov 88a5934736SYuri Pankov int mandocdb(int, char *[]); 89a5934736SYuri Pankov 90*c66b8046SYuri Pankov static void check_xr(const char *); 91260e9a87SYuri Pankov static int fs_lookup(const struct manpaths *, 92260e9a87SYuri Pankov size_t ipath, const char *, 93260e9a87SYuri Pankov const char *, const char *, 94260e9a87SYuri Pankov struct manpage **, size_t *); 95*c66b8046SYuri Pankov static int fs_search(const struct mansearch *, 96260e9a87SYuri Pankov const struct manpaths *, int, char**, 97260e9a87SYuri Pankov struct manpage **, size_t *); 98260e9a87SYuri Pankov static int koptions(int *, char *); 99*c66b8046SYuri Pankov static void moptions(int *, char *); 10095c635efSGarrett D'Amore static void mmsg(enum mandocerr, enum mandoclevel, 10195c635efSGarrett D'Amore const char *, int, int, const char *); 102a5934736SYuri Pankov static void outdata_alloc(struct curparse *); 103371584c2SYuri Pankov static void parse(struct curparse *, int, const char *); 104371584c2SYuri Pankov static void passthrough(const char *, int, int); 105371584c2SYuri Pankov static pid_t spawn_pager(struct tag_files *); 10695c635efSGarrett D'Amore static int toptions(struct curparse *, char *); 107a40ea1a7SYuri Pankov static void usage(enum argmode) __attribute__((__noreturn__)); 10895c635efSGarrett D'Amore static int woptions(struct curparse *, char *); 10995c635efSGarrett D'Amore 110260e9a87SYuri Pankov static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; 111260e9a87SYuri Pankov static char help_arg[] = "help"; 112260e9a87SYuri Pankov static char *help_argv[] = {help_arg, NULL}; 113371584c2SYuri Pankov static enum mandoclevel rc; 114*c66b8046SYuri Pankov static FILE *mmsg_stream; 11595c635efSGarrett D'Amore 116260e9a87SYuri Pankov 11795c635efSGarrett D'Amore int 11895c635efSGarrett D'Amore main(int argc, char *argv[]) 11995c635efSGarrett D'Amore { 120371584c2SYuri Pankov struct manconf conf; 121260e9a87SYuri Pankov struct mansearch search; 122a40ea1a7SYuri Pankov struct curparse curp; 123371584c2SYuri Pankov struct tag_files *tag_files; 124260e9a87SYuri Pankov struct manpage *res, *resp; 125a40ea1a7SYuri Pankov const char *progname, *sec, *thisarg; 126a40ea1a7SYuri Pankov char *conf_file, *defpaths, *auxpaths; 127*c66b8046SYuri Pankov char *oarg; 128a40ea1a7SYuri Pankov unsigned char *uc; 129371584c2SYuri Pankov size_t i, sz; 130371584c2SYuri Pankov int prio, best_prio; 131260e9a87SYuri Pankov enum outmode outmode; 132260e9a87SYuri Pankov int fd; 133260e9a87SYuri Pankov int show_usage; 134260e9a87SYuri Pankov int options; 135371584c2SYuri Pankov int use_pager; 136371584c2SYuri Pankov int status, signum; 137260e9a87SYuri Pankov int c; 138371584c2SYuri Pankov pid_t pager_pid, tc_pgid, man_pgid, pid; 13995c635efSGarrett D'Amore 140371584c2SYuri Pankov #if HAVE_PROGNAME 141371584c2SYuri Pankov progname = getprogname(); 142371584c2SYuri Pankov #else 143260e9a87SYuri Pankov if (argc < 1) 144371584c2SYuri Pankov progname = mandoc_strdup("mandoc"); 145260e9a87SYuri Pankov else if ((progname = strrchr(argv[0], '/')) == NULL) 14695c635efSGarrett D'Amore progname = argv[0]; 14795c635efSGarrett D'Amore else 14895c635efSGarrett D'Amore ++progname; 149371584c2SYuri Pankov setprogname(progname); 150371584c2SYuri Pankov #endif 15195c635efSGarrett D'Amore 152371584c2SYuri Pankov if (strncmp(progname, "mandocdb", 8) == 0 || 153371584c2SYuri Pankov strcmp(progname, BINM_MAKEWHATIS) == 0) 154371584c2SYuri Pankov return mandocdb(argc, argv); 155371584c2SYuri Pankov 156371584c2SYuri Pankov #if HAVE_PLEDGE 157*c66b8046SYuri Pankov if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) 158371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 159371584c2SYuri Pankov #endif 160371584c2SYuri Pankov 161371584c2SYuri Pankov #if HAVE_SANDBOX_INIT 162371584c2SYuri Pankov if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) 163371584c2SYuri Pankov errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); 164260e9a87SYuri Pankov #endif 165260e9a87SYuri Pankov 166260e9a87SYuri Pankov /* Search options. */ 16795c635efSGarrett D'Amore 168371584c2SYuri Pankov memset(&conf, 0, sizeof(conf)); 169260e9a87SYuri Pankov conf_file = defpaths = NULL; 170260e9a87SYuri Pankov auxpaths = NULL; 171260e9a87SYuri Pankov 172260e9a87SYuri Pankov memset(&search, 0, sizeof(struct mansearch)); 173260e9a87SYuri Pankov search.outkey = "Nd"; 174a40ea1a7SYuri Pankov oarg = NULL; 175260e9a87SYuri Pankov 176260e9a87SYuri Pankov if (strcmp(progname, BINM_MAN) == 0) 177260e9a87SYuri Pankov search.argmode = ARG_NAME; 178260e9a87SYuri Pankov else if (strcmp(progname, BINM_APROPOS) == 0) 179260e9a87SYuri Pankov search.argmode = ARG_EXPR; 180260e9a87SYuri Pankov else if (strcmp(progname, BINM_WHATIS) == 0) 181260e9a87SYuri Pankov search.argmode = ARG_WORD; 182260e9a87SYuri Pankov else if (strncmp(progname, "help", 4) == 0) 183260e9a87SYuri Pankov search.argmode = ARG_NAME; 184260e9a87SYuri Pankov else 185260e9a87SYuri Pankov search.argmode = ARG_FILE; 186260e9a87SYuri Pankov 187260e9a87SYuri Pankov /* Parser and formatter options. */ 188260e9a87SYuri Pankov 189260e9a87SYuri Pankov memset(&curp, 0, sizeof(struct curparse)); 190260e9a87SYuri Pankov curp.outtype = OUTT_LOCALE; 191*c66b8046SYuri Pankov curp.mmin = MANDOCERR_MAX; 192371584c2SYuri Pankov curp.outopts = &conf.output; 193260e9a87SYuri Pankov options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; 194*c66b8046SYuri Pankov mmsg_stream = stderr; 19595c635efSGarrett D'Amore 196371584c2SYuri Pankov use_pager = 1; 197371584c2SYuri Pankov tag_files = NULL; 198260e9a87SYuri Pankov show_usage = 0; 199260e9a87SYuri Pankov outmode = OUTMODE_DEF; 200260e9a87SYuri Pankov 201*c66b8046SYuri Pankov while ((c = getopt(argc, argv, 202*c66b8046SYuri Pankov "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) { 203*c66b8046SYuri Pankov if (c == 'i' && search.argmode == ARG_EXPR) { 204*c66b8046SYuri Pankov optind--; 205*c66b8046SYuri Pankov break; 206*c66b8046SYuri Pankov } 20795c635efSGarrett D'Amore switch (c) { 208260e9a87SYuri Pankov case 'a': 209260e9a87SYuri Pankov outmode = OUTMODE_ALL; 210260e9a87SYuri Pankov break; 211260e9a87SYuri Pankov case 'C': 212260e9a87SYuri Pankov conf_file = optarg; 213260e9a87SYuri Pankov break; 214260e9a87SYuri Pankov case 'c': 215371584c2SYuri Pankov use_pager = 0; 216260e9a87SYuri Pankov break; 217260e9a87SYuri Pankov case 'f': 218260e9a87SYuri Pankov search.argmode = ARG_WORD; 219260e9a87SYuri Pankov break; 220260e9a87SYuri Pankov case 'h': 221371584c2SYuri Pankov conf.output.synopsisonly = 1; 222371584c2SYuri Pankov use_pager = 0; 223260e9a87SYuri Pankov outmode = OUTMODE_ALL; 224260e9a87SYuri Pankov break; 225260e9a87SYuri Pankov case 'I': 226698f87a4SGarrett D'Amore if (strncmp(optarg, "os=", 3)) { 227371584c2SYuri Pankov warnx("-I %s: Bad argument", optarg); 228371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 229698f87a4SGarrett D'Amore } 230*c66b8046SYuri Pankov if (curp.os_s != NULL) { 231371584c2SYuri Pankov warnx("-I %s: Duplicate argument", optarg); 232371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 233698f87a4SGarrett D'Amore } 234*c66b8046SYuri Pankov curp.os_s = mandoc_strdup(optarg + 3); 235260e9a87SYuri Pankov break; 236260e9a87SYuri Pankov case 'K': 237260e9a87SYuri Pankov if ( ! koptions(&options, optarg)) 238371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 23995c635efSGarrett D'Amore break; 240260e9a87SYuri Pankov case 'k': 241260e9a87SYuri Pankov search.argmode = ARG_EXPR; 242260e9a87SYuri Pankov break; 243260e9a87SYuri Pankov case 'l': 244260e9a87SYuri Pankov search.argmode = ARG_FILE; 245260e9a87SYuri Pankov outmode = OUTMODE_ALL; 246260e9a87SYuri Pankov break; 247260e9a87SYuri Pankov case 'M': 248260e9a87SYuri Pankov defpaths = optarg; 249260e9a87SYuri Pankov break; 250260e9a87SYuri Pankov case 'm': 251260e9a87SYuri Pankov auxpaths = optarg; 252260e9a87SYuri Pankov break; 253260e9a87SYuri Pankov case 'O': 254a40ea1a7SYuri Pankov oarg = optarg; 25595c635efSGarrett D'Amore break; 256260e9a87SYuri Pankov case 'S': 257260e9a87SYuri Pankov search.arch = optarg; 258260e9a87SYuri Pankov break; 259260e9a87SYuri Pankov case 's': 260260e9a87SYuri Pankov search.sec = optarg; 261260e9a87SYuri Pankov break; 262260e9a87SYuri Pankov case 'T': 26395c635efSGarrett D'Amore if ( ! toptions(&curp, optarg)) 264371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 26595c635efSGarrett D'Amore break; 266260e9a87SYuri Pankov case 'W': 26795c635efSGarrett D'Amore if ( ! woptions(&curp, optarg)) 268371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 26995c635efSGarrett D'Amore break; 270260e9a87SYuri Pankov case 'w': 271260e9a87SYuri Pankov outmode = OUTMODE_FLN; 272260e9a87SYuri Pankov break; 27395c635efSGarrett D'Amore default: 274260e9a87SYuri Pankov show_usage = 1; 275260e9a87SYuri Pankov break; 27695c635efSGarrett D'Amore } 277260e9a87SYuri Pankov } 278260e9a87SYuri Pankov 279260e9a87SYuri Pankov if (show_usage) 280260e9a87SYuri Pankov usage(search.argmode); 28195c635efSGarrett D'Amore 282260e9a87SYuri Pankov /* Postprocess options. */ 283260e9a87SYuri Pankov 284260e9a87SYuri Pankov if (outmode == OUTMODE_DEF) { 285260e9a87SYuri Pankov switch (search.argmode) { 286260e9a87SYuri Pankov case ARG_FILE: 287260e9a87SYuri Pankov outmode = OUTMODE_ALL; 288371584c2SYuri Pankov use_pager = 0; 289260e9a87SYuri Pankov break; 290260e9a87SYuri Pankov case ARG_NAME: 291260e9a87SYuri Pankov outmode = OUTMODE_ONE; 292260e9a87SYuri Pankov break; 293260e9a87SYuri Pankov default: 294260e9a87SYuri Pankov outmode = OUTMODE_LST; 295260e9a87SYuri Pankov break; 296260e9a87SYuri Pankov } 297260e9a87SYuri Pankov } 298260e9a87SYuri Pankov 299a40ea1a7SYuri Pankov if (oarg != NULL) { 300a40ea1a7SYuri Pankov if (outmode == OUTMODE_LST) 301a40ea1a7SYuri Pankov search.outkey = oarg; 302a40ea1a7SYuri Pankov else { 303a40ea1a7SYuri Pankov while (oarg != NULL) { 304a40ea1a7SYuri Pankov thisarg = oarg; 305a40ea1a7SYuri Pankov if (manconf_output(&conf.output, 306a40ea1a7SYuri Pankov strsep(&oarg, ","), 0) == 0) 307a40ea1a7SYuri Pankov continue; 308a40ea1a7SYuri Pankov warnx("-O %s: Bad argument", thisarg); 309a40ea1a7SYuri Pankov return (int)MANDOCLEVEL_BADARG; 310a40ea1a7SYuri Pankov } 311a40ea1a7SYuri Pankov } 312a40ea1a7SYuri Pankov } 313a40ea1a7SYuri Pankov 314371584c2SYuri Pankov if (outmode == OUTMODE_FLN || 315371584c2SYuri Pankov outmode == OUTMODE_LST || 316371584c2SYuri Pankov !isatty(STDOUT_FILENO)) 317371584c2SYuri Pankov use_pager = 0; 318371584c2SYuri Pankov 319371584c2SYuri Pankov #if HAVE_PLEDGE 320371584c2SYuri Pankov if (!use_pager) 321*c66b8046SYuri Pankov if (pledge("stdio rpath", NULL) == -1) 322371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 323371584c2SYuri Pankov #endif 324371584c2SYuri Pankov 325260e9a87SYuri Pankov /* Parse arguments. */ 326260e9a87SYuri Pankov 327260e9a87SYuri Pankov if (argc > 0) { 328260e9a87SYuri Pankov argc -= optind; 329260e9a87SYuri Pankov argv += optind; 330260e9a87SYuri Pankov } 331260e9a87SYuri Pankov resp = NULL; 332260e9a87SYuri Pankov 333260e9a87SYuri Pankov /* 334260e9a87SYuri Pankov * Quirks for help(1) 335260e9a87SYuri Pankov * and for a man(1) section argument without -s. 336260e9a87SYuri Pankov */ 337260e9a87SYuri Pankov 338260e9a87SYuri Pankov if (search.argmode == ARG_NAME) { 339260e9a87SYuri Pankov if (*progname == 'h') { 340260e9a87SYuri Pankov if (argc == 0) { 341260e9a87SYuri Pankov argv = help_argv; 342260e9a87SYuri Pankov argc = 1; 343260e9a87SYuri Pankov } 344260e9a87SYuri Pankov } else if (argc > 1 && 345260e9a87SYuri Pankov ((uc = (unsigned char *)argv[0]) != NULL) && 346260e9a87SYuri Pankov ((isdigit(uc[0]) && (uc[1] == '\0' || 347260e9a87SYuri Pankov (isalpha(uc[1]) && uc[2] == '\0'))) || 348260e9a87SYuri Pankov (uc[0] == 'n' && uc[1] == '\0'))) { 349260e9a87SYuri Pankov search.sec = (char *)uc; 350260e9a87SYuri Pankov argv++; 351260e9a87SYuri Pankov argc--; 352260e9a87SYuri Pankov } 353260e9a87SYuri Pankov if (search.arch == NULL) 354260e9a87SYuri Pankov search.arch = getenv("MACHINE"); 355260e9a87SYuri Pankov #ifdef MACHINE 356260e9a87SYuri Pankov if (search.arch == NULL) 357260e9a87SYuri Pankov search.arch = MACHINE; 358260e9a87SYuri Pankov #endif 359260e9a87SYuri Pankov } 360260e9a87SYuri Pankov 361260e9a87SYuri Pankov rc = MANDOCLEVEL_OK; 362260e9a87SYuri Pankov 363260e9a87SYuri Pankov /* man(1), whatis(1), apropos(1) */ 364260e9a87SYuri Pankov 365260e9a87SYuri Pankov if (search.argmode != ARG_FILE) { 366260e9a87SYuri Pankov if (search.argmode == ARG_NAME && 367260e9a87SYuri Pankov outmode == OUTMODE_ONE) 368260e9a87SYuri Pankov search.firstmatch = 1; 369260e9a87SYuri Pankov 370260e9a87SYuri Pankov /* Access the mandoc database. */ 371260e9a87SYuri Pankov 372371584c2SYuri Pankov manconf_parse(&conf, conf_file, defpaths, auxpaths); 373371584c2SYuri Pankov if ( ! mansearch(&search, &conf.manpath, 374371584c2SYuri Pankov argc, argv, &res, &sz)) 375260e9a87SYuri Pankov usage(search.argmode); 376260e9a87SYuri Pankov 377371584c2SYuri Pankov if (sz == 0) { 378371584c2SYuri Pankov if (search.argmode == ARG_NAME) 379371584c2SYuri Pankov fs_search(&search, &conf.manpath, 380371584c2SYuri Pankov argc, argv, &res, &sz); 381371584c2SYuri Pankov else 382371584c2SYuri Pankov warnx("nothing appropriate"); 383371584c2SYuri Pankov } 384260e9a87SYuri Pankov 385260e9a87SYuri Pankov if (sz == 0) { 386260e9a87SYuri Pankov rc = MANDOCLEVEL_BADARG; 387260e9a87SYuri Pankov goto out; 388260e9a87SYuri Pankov } 389260e9a87SYuri Pankov 390260e9a87SYuri Pankov /* 391260e9a87SYuri Pankov * For standard man(1) and -a output mode, 392260e9a87SYuri Pankov * prepare for copying filename pointers 393260e9a87SYuri Pankov * into the program parameter array. 394260e9a87SYuri Pankov */ 395260e9a87SYuri Pankov 396260e9a87SYuri Pankov if (outmode == OUTMODE_ONE) { 397260e9a87SYuri Pankov argc = 1; 398371584c2SYuri Pankov best_prio = 20; 399260e9a87SYuri Pankov } else if (outmode == OUTMODE_ALL) 400260e9a87SYuri Pankov argc = (int)sz; 401260e9a87SYuri Pankov 402260e9a87SYuri Pankov /* Iterate all matching manuals. */ 403260e9a87SYuri Pankov 404260e9a87SYuri Pankov resp = res; 405260e9a87SYuri Pankov for (i = 0; i < sz; i++) { 406260e9a87SYuri Pankov if (outmode == OUTMODE_FLN) 407260e9a87SYuri Pankov puts(res[i].file); 408260e9a87SYuri Pankov else if (outmode == OUTMODE_LST) 409260e9a87SYuri Pankov printf("%s - %s\n", res[i].names, 410260e9a87SYuri Pankov res[i].output == NULL ? "" : 411260e9a87SYuri Pankov res[i].output); 412260e9a87SYuri Pankov else if (outmode == OUTMODE_ONE) { 413260e9a87SYuri Pankov /* Search for the best section. */ 414371584c2SYuri Pankov sec = res[i].file; 415371584c2SYuri Pankov sec += strcspn(sec, "123456789"); 416371584c2SYuri Pankov if (sec[0] == '\0') 417260e9a87SYuri Pankov continue; 418371584c2SYuri Pankov prio = sec_prios[sec[0] - '1']; 419371584c2SYuri Pankov if (sec[1] != '/') 420371584c2SYuri Pankov prio += 10; 421260e9a87SYuri Pankov if (prio >= best_prio) 422260e9a87SYuri Pankov continue; 423260e9a87SYuri Pankov best_prio = prio; 424260e9a87SYuri Pankov resp = res + i; 425260e9a87SYuri Pankov } 426260e9a87SYuri Pankov } 427260e9a87SYuri Pankov 428260e9a87SYuri Pankov /* 429260e9a87SYuri Pankov * For man(1), -a and -i output mode, fall through 430260e9a87SYuri Pankov * to the main mandoc(1) code iterating files 431260e9a87SYuri Pankov * and running the parsers on each of them. 432260e9a87SYuri Pankov */ 433260e9a87SYuri Pankov 434260e9a87SYuri Pankov if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) 435260e9a87SYuri Pankov goto out; 436260e9a87SYuri Pankov } 437260e9a87SYuri Pankov 438260e9a87SYuri Pankov /* mandoc(1) */ 439260e9a87SYuri Pankov 440371584c2SYuri Pankov #if HAVE_PLEDGE 441371584c2SYuri Pankov if (use_pager) { 442371584c2SYuri Pankov if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) 443371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 444371584c2SYuri Pankov } else { 445371584c2SYuri Pankov if (pledge("stdio rpath", NULL) == -1) 446371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 447371584c2SYuri Pankov } 448371584c2SYuri Pankov #endif 449371584c2SYuri Pankov 450*c66b8046SYuri Pankov if (search.argmode == ARG_FILE) 451*c66b8046SYuri Pankov moptions(&options, auxpaths); 452260e9a87SYuri Pankov 453371584c2SYuri Pankov mchars_alloc(); 454*c66b8046SYuri Pankov curp.mp = mparse_alloc(options, curp.mmin, mmsg, 455*c66b8046SYuri Pankov curp.os_e, curp.os_s); 45695c635efSGarrett D'Amore 45795c635efSGarrett D'Amore /* 45895c635efSGarrett D'Amore * Conditionally start up the lookaside buffer before parsing. 45995c635efSGarrett D'Amore */ 46095c635efSGarrett D'Amore if (OUTT_MAN == curp.outtype) 46195c635efSGarrett D'Amore mparse_keep(curp.mp); 46295c635efSGarrett D'Amore 463260e9a87SYuri Pankov if (argc < 1) { 464371584c2SYuri Pankov if (use_pager) 465371584c2SYuri Pankov tag_files = tag_init(); 466371584c2SYuri Pankov parse(&curp, STDIN_FILENO, "<stdin>"); 467260e9a87SYuri Pankov } 46895c635efSGarrett D'Amore 469260e9a87SYuri Pankov while (argc > 0) { 470371584c2SYuri Pankov fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv); 471260e9a87SYuri Pankov if (fd != -1) { 472371584c2SYuri Pankov if (use_pager) { 473371584c2SYuri Pankov tag_files = tag_init(); 474371584c2SYuri Pankov use_pager = 0; 475371584c2SYuri Pankov } 476260e9a87SYuri Pankov 477260e9a87SYuri Pankov if (resp == NULL) 478371584c2SYuri Pankov parse(&curp, fd, *argv); 479a40ea1a7SYuri Pankov else if (resp->form == FORM_SRC) { 480260e9a87SYuri Pankov /* For .so only; ignore failure. */ 481*c66b8046SYuri Pankov (void)chdir(conf.manpath.paths[resp->ipath]); 482371584c2SYuri Pankov parse(&curp, fd, resp->file); 483371584c2SYuri Pankov } else 484371584c2SYuri Pankov passthrough(resp->file, fd, 485371584c2SYuri Pankov conf.output.synopsisonly); 486260e9a87SYuri Pankov 487a5934736SYuri Pankov if (argc > 1 && curp.outtype <= OUTT_UTF8) { 488a5934736SYuri Pankov if (curp.outdata == NULL) 489a5934736SYuri Pankov outdata_alloc(&curp); 490371584c2SYuri Pankov terminal_sepline(curp.outdata); 491a5934736SYuri Pankov } 492371584c2SYuri Pankov } else if (rc < MANDOCLEVEL_ERROR) 493371584c2SYuri Pankov rc = MANDOCLEVEL_ERROR; 49495c635efSGarrett D'Amore 49595c635efSGarrett D'Amore if (MANDOCLEVEL_OK != rc && curp.wstop) 49695c635efSGarrett D'Amore break; 497260e9a87SYuri Pankov 498260e9a87SYuri Pankov if (resp != NULL) 499260e9a87SYuri Pankov resp++; 500260e9a87SYuri Pankov else 501260e9a87SYuri Pankov argv++; 502260e9a87SYuri Pankov if (--argc) 503260e9a87SYuri Pankov mparse_reset(curp.mp); 50495c635efSGarrett D'Amore } 50595c635efSGarrett D'Amore 506371584c2SYuri Pankov if (curp.outdata != NULL) { 507371584c2SYuri Pankov switch (curp.outtype) { 508371584c2SYuri Pankov case OUTT_HTML: 509371584c2SYuri Pankov html_free(curp.outdata); 510371584c2SYuri Pankov break; 511371584c2SYuri Pankov case OUTT_UTF8: 512371584c2SYuri Pankov case OUTT_LOCALE: 513371584c2SYuri Pankov case OUTT_ASCII: 514371584c2SYuri Pankov ascii_free(curp.outdata); 515371584c2SYuri Pankov break; 516371584c2SYuri Pankov case OUTT_PDF: 517371584c2SYuri Pankov case OUTT_PS: 518371584c2SYuri Pankov pspdf_free(curp.outdata); 519371584c2SYuri Pankov break; 520371584c2SYuri Pankov default: 521371584c2SYuri Pankov break; 522371584c2SYuri Pankov } 523371584c2SYuri Pankov } 524*c66b8046SYuri Pankov mandoc_xr_free(); 525260e9a87SYuri Pankov mparse_free(curp.mp); 526371584c2SYuri Pankov mchars_free(); 527260e9a87SYuri Pankov 528260e9a87SYuri Pankov out: 529260e9a87SYuri Pankov if (search.argmode != ARG_FILE) { 530371584c2SYuri Pankov manconf_free(&conf); 531260e9a87SYuri Pankov mansearch_free(res, sz); 532260e9a87SYuri Pankov } 533260e9a87SYuri Pankov 534*c66b8046SYuri Pankov free(curp.os_s); 53595c635efSGarrett D'Amore 536260e9a87SYuri Pankov /* 537371584c2SYuri Pankov * When using a pager, finish writing both temporary files, 538371584c2SYuri Pankov * fork it, wait for the user to close it, and clean up. 539260e9a87SYuri Pankov */ 540260e9a87SYuri Pankov 541371584c2SYuri Pankov if (tag_files != NULL) { 542260e9a87SYuri Pankov fclose(stdout); 543371584c2SYuri Pankov tag_write(); 544371584c2SYuri Pankov man_pgid = getpgid(0); 545371584c2SYuri Pankov tag_files->tcpgid = man_pgid == getpid() ? 546371584c2SYuri Pankov getpgid(getppid()) : man_pgid; 547371584c2SYuri Pankov pager_pid = 0; 548371584c2SYuri Pankov signum = SIGSTOP; 549371584c2SYuri Pankov for (;;) { 550371584c2SYuri Pankov 551371584c2SYuri Pankov /* Stop here until moved to the foreground. */ 552371584c2SYuri Pankov 553a40ea1a7SYuri Pankov tc_pgid = tcgetpgrp(tag_files->ofd); 554371584c2SYuri Pankov if (tc_pgid != man_pgid) { 555371584c2SYuri Pankov if (tc_pgid == pager_pid) { 556a40ea1a7SYuri Pankov (void)tcsetpgrp(tag_files->ofd, 557371584c2SYuri Pankov man_pgid); 558371584c2SYuri Pankov if (signum == SIGTTIN) 559371584c2SYuri Pankov continue; 560371584c2SYuri Pankov } else 561371584c2SYuri Pankov tag_files->tcpgid = tc_pgid; 562371584c2SYuri Pankov kill(0, signum); 563371584c2SYuri Pankov continue; 564371584c2SYuri Pankov } 565371584c2SYuri Pankov 566371584c2SYuri Pankov /* Once in the foreground, activate the pager. */ 567371584c2SYuri Pankov 568371584c2SYuri Pankov if (pager_pid) { 569a40ea1a7SYuri Pankov (void)tcsetpgrp(tag_files->ofd, pager_pid); 570371584c2SYuri Pankov kill(pager_pid, SIGCONT); 571371584c2SYuri Pankov } else 572371584c2SYuri Pankov pager_pid = spawn_pager(tag_files); 573371584c2SYuri Pankov 574371584c2SYuri Pankov /* Wait for the pager to stop or exit. */ 575371584c2SYuri Pankov 576371584c2SYuri Pankov while ((pid = waitpid(pager_pid, &status, 577371584c2SYuri Pankov WUNTRACED)) == -1 && errno == EINTR) 578371584c2SYuri Pankov continue; 579371584c2SYuri Pankov 580371584c2SYuri Pankov if (pid == -1) { 581371584c2SYuri Pankov warn("wait"); 582371584c2SYuri Pankov rc = MANDOCLEVEL_SYSERR; 583371584c2SYuri Pankov break; 584371584c2SYuri Pankov } 585371584c2SYuri Pankov if (!WIFSTOPPED(status)) 586371584c2SYuri Pankov break; 587371584c2SYuri Pankov 588371584c2SYuri Pankov signum = WSTOPSIG(status); 589371584c2SYuri Pankov } 590371584c2SYuri Pankov tag_unlink(); 591260e9a87SYuri Pankov } 592260e9a87SYuri Pankov 593371584c2SYuri Pankov return (int)rc; 59495c635efSGarrett D'Amore } 59595c635efSGarrett D'Amore 59695c635efSGarrett D'Amore static void 597260e9a87SYuri Pankov usage(enum argmode argmode) 59895c635efSGarrett D'Amore { 59995c635efSGarrett D'Amore 600260e9a87SYuri Pankov switch (argmode) { 601260e9a87SYuri Pankov case ARG_FILE: 602*c66b8046SYuri Pankov fputs("usage: mandoc [-ac] [-I os=name] " 603*c66b8046SYuri Pankov "[-K encoding] [-mdoc | -man] [-O options]\n" 604371584c2SYuri Pankov "\t [-T output] [-W level] [file ...]\n", stderr); 605260e9a87SYuri Pankov break; 606260e9a87SYuri Pankov case ARG_NAME: 607*c66b8046SYuri Pankov fputs("usage: man [-acfhklw] [-C file] [-M path] " 608*c66b8046SYuri Pankov "[-m path] [-S subsection]\n" 609*c66b8046SYuri Pankov "\t [[-s] section] name ...\n", stderr); 610260e9a87SYuri Pankov break; 611260e9a87SYuri Pankov case ARG_WORD: 612*c66b8046SYuri Pankov fputs("usage: whatis [-afk] [-C file] " 613260e9a87SYuri Pankov "[-M path] [-m path] [-O outkey] [-S arch]\n" 614260e9a87SYuri Pankov "\t [-s section] name ...\n", stderr); 615260e9a87SYuri Pankov break; 616260e9a87SYuri Pankov case ARG_EXPR: 617*c66b8046SYuri Pankov fputs("usage: apropos [-afk] [-C file] " 618260e9a87SYuri Pankov "[-M path] [-m path] [-O outkey] [-S arch]\n" 619260e9a87SYuri Pankov "\t [-s section] expression ...\n", stderr); 620260e9a87SYuri Pankov break; 621260e9a87SYuri Pankov } 622260e9a87SYuri Pankov exit((int)MANDOCLEVEL_BADARG); 62395c635efSGarrett D'Amore } 62495c635efSGarrett D'Amore 625260e9a87SYuri Pankov static int 626260e9a87SYuri Pankov fs_lookup(const struct manpaths *paths, size_t ipath, 627260e9a87SYuri Pankov const char *sec, const char *arch, const char *name, 628260e9a87SYuri Pankov struct manpage **res, size_t *ressz) 62995c635efSGarrett D'Amore { 630260e9a87SYuri Pankov glob_t globinfo; 631260e9a87SYuri Pankov struct manpage *page; 632260e9a87SYuri Pankov char *file; 633a40ea1a7SYuri Pankov int globres; 634a40ea1a7SYuri Pankov enum form form; 635260e9a87SYuri Pankov 636260e9a87SYuri Pankov form = FORM_SRC; 637260e9a87SYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s.%s", 638260e9a87SYuri Pankov paths->paths[ipath], sec, name, sec); 639260e9a87SYuri Pankov if (access(file, R_OK) != -1) 640260e9a87SYuri Pankov goto found; 641260e9a87SYuri Pankov free(file); 642260e9a87SYuri Pankov 643260e9a87SYuri Pankov mandoc_asprintf(&file, "%s/cat%s/%s.0", 644260e9a87SYuri Pankov paths->paths[ipath], sec, name); 645260e9a87SYuri Pankov if (access(file, R_OK) != -1) { 646260e9a87SYuri Pankov form = FORM_CAT; 647260e9a87SYuri Pankov goto found; 648260e9a87SYuri Pankov } 649260e9a87SYuri Pankov free(file); 650260e9a87SYuri Pankov 651260e9a87SYuri Pankov if (arch != NULL) { 652260e9a87SYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", 653260e9a87SYuri Pankov paths->paths[ipath], sec, arch, name, sec); 654260e9a87SYuri Pankov if (access(file, R_OK) != -1) 655260e9a87SYuri Pankov goto found; 656260e9a87SYuri Pankov free(file); 657260e9a87SYuri Pankov } 658260e9a87SYuri Pankov 659371584c2SYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", 660260e9a87SYuri Pankov paths->paths[ipath], sec, name); 661260e9a87SYuri Pankov globres = glob(file, 0, NULL, &globinfo); 662260e9a87SYuri Pankov if (globres != 0 && globres != GLOB_NOMATCH) 663371584c2SYuri Pankov warn("%s: glob", file); 664260e9a87SYuri Pankov free(file); 665260e9a87SYuri Pankov if (globres == 0) 666260e9a87SYuri Pankov file = mandoc_strdup(*globinfo.gl_pathv); 667260e9a87SYuri Pankov globfree(&globinfo); 668*c66b8046SYuri Pankov if (globres == 0) 669*c66b8046SYuri Pankov goto found; 670*c66b8046SYuri Pankov if (res != NULL || ipath + 1 != paths->sz) 671371584c2SYuri Pankov return 0; 67295c635efSGarrett D'Amore 673*c66b8046SYuri Pankov mandoc_asprintf(&file, "%s.%s", name, sec); 674*c66b8046SYuri Pankov globres = access(file, R_OK); 675*c66b8046SYuri Pankov free(file); 676*c66b8046SYuri Pankov return globres != -1; 677*c66b8046SYuri Pankov 678260e9a87SYuri Pankov found: 679371584c2SYuri Pankov warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", 680371584c2SYuri Pankov name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); 681*c66b8046SYuri Pankov if (res == NULL) { 682*c66b8046SYuri Pankov free(file); 683*c66b8046SYuri Pankov return 1; 684*c66b8046SYuri Pankov } 685260e9a87SYuri Pankov *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); 686260e9a87SYuri Pankov page = *res + (*ressz - 1); 687260e9a87SYuri Pankov page->file = file; 688260e9a87SYuri Pankov page->names = NULL; 689260e9a87SYuri Pankov page->output = NULL; 690260e9a87SYuri Pankov page->ipath = ipath; 691260e9a87SYuri Pankov page->bits = NAME_FILE & NAME_MASK; 692260e9a87SYuri Pankov page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; 693260e9a87SYuri Pankov page->form = form; 694371584c2SYuri Pankov return 1; 695260e9a87SYuri Pankov } 696260e9a87SYuri Pankov 697*c66b8046SYuri Pankov static int 698260e9a87SYuri Pankov fs_search(const struct mansearch *cfg, const struct manpaths *paths, 699260e9a87SYuri Pankov int argc, char **argv, struct manpage **res, size_t *ressz) 700260e9a87SYuri Pankov { 701260e9a87SYuri Pankov const char *const sections[] = 702371584c2SYuri Pankov {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; 703260e9a87SYuri Pankov const size_t nsec = sizeof(sections)/sizeof(sections[0]); 704260e9a87SYuri Pankov 705260e9a87SYuri Pankov size_t ipath, isec, lastsz; 706260e9a87SYuri Pankov 707260e9a87SYuri Pankov assert(cfg->argmode == ARG_NAME); 708260e9a87SYuri Pankov 709*c66b8046SYuri Pankov if (res != NULL) 710*c66b8046SYuri Pankov *res = NULL; 711260e9a87SYuri Pankov *ressz = lastsz = 0; 712260e9a87SYuri Pankov while (argc) { 713260e9a87SYuri Pankov for (ipath = 0; ipath < paths->sz; ipath++) { 714260e9a87SYuri Pankov if (cfg->sec != NULL) { 715260e9a87SYuri Pankov if (fs_lookup(paths, ipath, cfg->sec, 716260e9a87SYuri Pankov cfg->arch, *argv, res, ressz) && 717260e9a87SYuri Pankov cfg->firstmatch) 718*c66b8046SYuri Pankov return 1; 719260e9a87SYuri Pankov } else for (isec = 0; isec < nsec; isec++) 720260e9a87SYuri Pankov if (fs_lookup(paths, ipath, sections[isec], 721260e9a87SYuri Pankov cfg->arch, *argv, res, ressz) && 722260e9a87SYuri Pankov cfg->firstmatch) 723*c66b8046SYuri Pankov return 1; 724260e9a87SYuri Pankov } 725*c66b8046SYuri Pankov if (res != NULL && *ressz == lastsz) 726371584c2SYuri Pankov warnx("No entry for %s in the manual.", *argv); 727260e9a87SYuri Pankov lastsz = *ressz; 728260e9a87SYuri Pankov argv++; 729260e9a87SYuri Pankov argc--; 730260e9a87SYuri Pankov } 731*c66b8046SYuri Pankov return 0; 73295c635efSGarrett D'Amore } 73395c635efSGarrett D'Amore 73495c635efSGarrett D'Amore static void 735371584c2SYuri Pankov parse(struct curparse *curp, int fd, const char *file) 73695c635efSGarrett D'Amore { 737371584c2SYuri Pankov enum mandoclevel rctmp; 738371584c2SYuri Pankov struct roff_man *man; 73995c635efSGarrett D'Amore 74095c635efSGarrett D'Amore /* Begin by parsing the file itself. */ 74195c635efSGarrett D'Amore 74295c635efSGarrett D'Amore assert(file); 743371584c2SYuri Pankov assert(fd >= 0); 74495c635efSGarrett D'Amore 745371584c2SYuri Pankov rctmp = mparse_readfd(curp->mp, fd, file); 746371584c2SYuri Pankov if (fd != STDIN_FILENO) 747371584c2SYuri Pankov close(fd); 748371584c2SYuri Pankov if (rc < rctmp) 749371584c2SYuri Pankov rc = rctmp; 75095c635efSGarrett D'Amore 75195c635efSGarrett D'Amore /* 75295c635efSGarrett D'Amore * With -Wstop and warnings or errors of at least the requested 75395c635efSGarrett D'Amore * level, do not produce output. 75495c635efSGarrett D'Amore */ 75595c635efSGarrett D'Amore 756371584c2SYuri Pankov if (rctmp != MANDOCLEVEL_OK && curp->wstop) 757371584c2SYuri Pankov return; 75895c635efSGarrett D'Amore 759a5934736SYuri Pankov if (curp->outdata == NULL) 760a5934736SYuri Pankov outdata_alloc(curp); 761371584c2SYuri Pankov 762371584c2SYuri Pankov mparse_result(curp->mp, &man, NULL); 76395c635efSGarrett D'Amore 764371584c2SYuri Pankov /* Execute the out device, if it exists. */ 765371584c2SYuri Pankov 766371584c2SYuri Pankov if (man == NULL) 767371584c2SYuri Pankov return; 768*c66b8046SYuri Pankov mandoc_xr_reset(); 769371584c2SYuri Pankov if (man->macroset == MACROSET_MDOC) { 770a40ea1a7SYuri Pankov if (curp->outtype != OUTT_TREE || !curp->outopts->noval) 771a40ea1a7SYuri Pankov mdoc_validate(man); 77295c635efSGarrett D'Amore switch (curp->outtype) { 773260e9a87SYuri Pankov case OUTT_HTML: 774371584c2SYuri Pankov html_mdoc(curp->outdata, man); 77595c635efSGarrett D'Amore break; 776260e9a87SYuri Pankov case OUTT_TREE: 777371584c2SYuri Pankov tree_mdoc(curp->outdata, man); 77895c635efSGarrett D'Amore break; 779260e9a87SYuri Pankov case OUTT_MAN: 780371584c2SYuri Pankov man_mdoc(curp->outdata, man); 78195c635efSGarrett D'Amore break; 782260e9a87SYuri Pankov case OUTT_PDF: 783260e9a87SYuri Pankov case OUTT_ASCII: 784260e9a87SYuri Pankov case OUTT_UTF8: 785260e9a87SYuri Pankov case OUTT_LOCALE: 786260e9a87SYuri Pankov case OUTT_PS: 787371584c2SYuri Pankov terminal_mdoc(curp->outdata, man); 788371584c2SYuri Pankov break; 789*c66b8046SYuri Pankov case OUTT_MARKDOWN: 790*c66b8046SYuri Pankov markdown_mdoc(curp->outdata, man); 791*c66b8046SYuri Pankov break; 792371584c2SYuri Pankov default: 793371584c2SYuri Pankov break; 794371584c2SYuri Pankov } 795371584c2SYuri Pankov } 796371584c2SYuri Pankov if (man->macroset == MACROSET_MAN) { 797a40ea1a7SYuri Pankov if (curp->outtype != OUTT_TREE || !curp->outopts->noval) 798a40ea1a7SYuri Pankov man_validate(man); 799371584c2SYuri Pankov switch (curp->outtype) { 800371584c2SYuri Pankov case OUTT_HTML: 801371584c2SYuri Pankov html_man(curp->outdata, man); 802371584c2SYuri Pankov break; 803371584c2SYuri Pankov case OUTT_TREE: 804371584c2SYuri Pankov tree_man(curp->outdata, man); 805371584c2SYuri Pankov break; 806371584c2SYuri Pankov case OUTT_MAN: 807371584c2SYuri Pankov man_man(curp->outdata, man); 808371584c2SYuri Pankov break; 809371584c2SYuri Pankov case OUTT_PDF: 810371584c2SYuri Pankov case OUTT_ASCII: 811371584c2SYuri Pankov case OUTT_UTF8: 812371584c2SYuri Pankov case OUTT_LOCALE: 813371584c2SYuri Pankov case OUTT_PS: 814371584c2SYuri Pankov terminal_man(curp->outdata, man); 81595c635efSGarrett D'Amore break; 81695c635efSGarrett D'Amore default: 81795c635efSGarrett D'Amore break; 81895c635efSGarrett D'Amore } 81995c635efSGarrett D'Amore } 820*c66b8046SYuri Pankov if (curp->mmin < MANDOCERR_STYLE) 821*c66b8046SYuri Pankov check_xr(file); 822a5934736SYuri Pankov mparse_updaterc(curp->mp, &rc); 823a5934736SYuri Pankov } 824a5934736SYuri Pankov 825*c66b8046SYuri Pankov static void 826*c66b8046SYuri Pankov check_xr(const char *file) 827*c66b8046SYuri Pankov { 828*c66b8046SYuri Pankov static struct manpaths paths; 829*c66b8046SYuri Pankov struct mansearch search; 830*c66b8046SYuri Pankov struct mandoc_xr *xr; 831*c66b8046SYuri Pankov char *cp; 832*c66b8046SYuri Pankov size_t sz; 833*c66b8046SYuri Pankov 834*c66b8046SYuri Pankov if (paths.sz == 0) 835*c66b8046SYuri Pankov manpath_base(&paths); 836*c66b8046SYuri Pankov 837*c66b8046SYuri Pankov for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { 838*c66b8046SYuri Pankov if (xr->line == -1) 839*c66b8046SYuri Pankov continue; 840*c66b8046SYuri Pankov search.arch = NULL; 841*c66b8046SYuri Pankov search.sec = xr->sec; 842*c66b8046SYuri Pankov search.outkey = NULL; 843*c66b8046SYuri Pankov search.argmode = ARG_NAME; 844*c66b8046SYuri Pankov search.firstmatch = 1; 845*c66b8046SYuri Pankov if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz)) 846*c66b8046SYuri Pankov continue; 847*c66b8046SYuri Pankov if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz)) 848*c66b8046SYuri Pankov continue; 849*c66b8046SYuri Pankov if (xr->count == 1) 850*c66b8046SYuri Pankov mandoc_asprintf(&cp, "Xr %s %s", xr->name, xr->sec); 851*c66b8046SYuri Pankov else 852*c66b8046SYuri Pankov mandoc_asprintf(&cp, "Xr %s %s (%d times)", 853*c66b8046SYuri Pankov xr->name, xr->sec, xr->count); 854*c66b8046SYuri Pankov mmsg(MANDOCERR_XR_BAD, MANDOCLEVEL_STYLE, 855*c66b8046SYuri Pankov file, xr->line, xr->pos + 1, cp); 856*c66b8046SYuri Pankov free(cp); 857*c66b8046SYuri Pankov } 858*c66b8046SYuri Pankov } 859*c66b8046SYuri Pankov 860a5934736SYuri Pankov static void 861a5934736SYuri Pankov outdata_alloc(struct curparse *curp) 862a5934736SYuri Pankov { 863a5934736SYuri Pankov switch (curp->outtype) { 864a5934736SYuri Pankov case OUTT_HTML: 865a5934736SYuri Pankov curp->outdata = html_alloc(curp->outopts); 866a5934736SYuri Pankov break; 867a5934736SYuri Pankov case OUTT_UTF8: 868a5934736SYuri Pankov curp->outdata = utf8_alloc(curp->outopts); 869a5934736SYuri Pankov break; 870a5934736SYuri Pankov case OUTT_LOCALE: 871a5934736SYuri Pankov curp->outdata = locale_alloc(curp->outopts); 872a5934736SYuri Pankov break; 873a5934736SYuri Pankov case OUTT_ASCII: 874a5934736SYuri Pankov curp->outdata = ascii_alloc(curp->outopts); 875a5934736SYuri Pankov break; 876a5934736SYuri Pankov case OUTT_PDF: 877a5934736SYuri Pankov curp->outdata = pdf_alloc(curp->outopts); 878a5934736SYuri Pankov break; 879a5934736SYuri Pankov case OUTT_PS: 880a5934736SYuri Pankov curp->outdata = ps_alloc(curp->outopts); 881a5934736SYuri Pankov break; 882a5934736SYuri Pankov default: 883a5934736SYuri Pankov break; 884a5934736SYuri Pankov } 88595c635efSGarrett D'Amore } 88695c635efSGarrett D'Amore 887371584c2SYuri Pankov static void 888260e9a87SYuri Pankov passthrough(const char *file, int fd, int synopsis_only) 889260e9a87SYuri Pankov { 890260e9a87SYuri Pankov const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; 891260e9a87SYuri Pankov const char synr[] = "SYNOPSIS"; 892260e9a87SYuri Pankov 893260e9a87SYuri Pankov FILE *stream; 894260e9a87SYuri Pankov const char *syscall; 895371584c2SYuri Pankov char *line, *cp; 896371584c2SYuri Pankov size_t linesz; 897a5934736SYuri Pankov ssize_t len, written; 898260e9a87SYuri Pankov int print; 899260e9a87SYuri Pankov 900371584c2SYuri Pankov line = NULL; 901371584c2SYuri Pankov linesz = 0; 902260e9a87SYuri Pankov 903a5934736SYuri Pankov if (fflush(stdout) == EOF) { 904a5934736SYuri Pankov syscall = "fflush"; 905a5934736SYuri Pankov goto fail; 906a5934736SYuri Pankov } 907a5934736SYuri Pankov 908260e9a87SYuri Pankov if ((stream = fdopen(fd, "r")) == NULL) { 909260e9a87SYuri Pankov close(fd); 910260e9a87SYuri Pankov syscall = "fdopen"; 911260e9a87SYuri Pankov goto fail; 912260e9a87SYuri Pankov } 913260e9a87SYuri Pankov 914260e9a87SYuri Pankov print = 0; 915a5934736SYuri Pankov while ((len = getline(&line, &linesz, stream)) != -1) { 916371584c2SYuri Pankov cp = line; 917260e9a87SYuri Pankov if (synopsis_only) { 918260e9a87SYuri Pankov if (print) { 919371584c2SYuri Pankov if ( ! isspace((unsigned char)*cp)) 920260e9a87SYuri Pankov goto done; 921a5934736SYuri Pankov while (isspace((unsigned char)*cp)) { 922371584c2SYuri Pankov cp++; 923a5934736SYuri Pankov len--; 924a5934736SYuri Pankov } 925260e9a87SYuri Pankov } else { 926371584c2SYuri Pankov if (strcmp(cp, synb) == 0 || 927371584c2SYuri Pankov strcmp(cp, synr) == 0) 928260e9a87SYuri Pankov print = 1; 929260e9a87SYuri Pankov continue; 930260e9a87SYuri Pankov } 931260e9a87SYuri Pankov } 932a5934736SYuri Pankov for (; len > 0; len -= written) { 933a5934736SYuri Pankov if ((written = write(STDOUT_FILENO, cp, len)) != -1) 934a5934736SYuri Pankov continue; 935371584c2SYuri Pankov fclose(stream); 936a5934736SYuri Pankov syscall = "write"; 937371584c2SYuri Pankov goto fail; 938371584c2SYuri Pankov } 939260e9a87SYuri Pankov } 940260e9a87SYuri Pankov 941260e9a87SYuri Pankov if (ferror(stream)) { 942260e9a87SYuri Pankov fclose(stream); 943371584c2SYuri Pankov syscall = "getline"; 944260e9a87SYuri Pankov goto fail; 945260e9a87SYuri Pankov } 946260e9a87SYuri Pankov 947260e9a87SYuri Pankov done: 948371584c2SYuri Pankov free(line); 949260e9a87SYuri Pankov fclose(stream); 950371584c2SYuri Pankov return; 951260e9a87SYuri Pankov 952260e9a87SYuri Pankov fail: 953371584c2SYuri Pankov free(line); 954371584c2SYuri Pankov warn("%s: SYSERR: %s", file, syscall); 955371584c2SYuri Pankov if (rc < MANDOCLEVEL_SYSERR) 956371584c2SYuri Pankov rc = MANDOCLEVEL_SYSERR; 957260e9a87SYuri Pankov } 958260e9a87SYuri Pankov 959260e9a87SYuri Pankov static int 960260e9a87SYuri Pankov koptions(int *options, char *arg) 961260e9a87SYuri Pankov { 962260e9a87SYuri Pankov 963260e9a87SYuri Pankov if ( ! strcmp(arg, "utf-8")) { 964260e9a87SYuri Pankov *options |= MPARSE_UTF8; 965260e9a87SYuri Pankov *options &= ~MPARSE_LATIN1; 966260e9a87SYuri Pankov } else if ( ! strcmp(arg, "iso-8859-1")) { 967260e9a87SYuri Pankov *options |= MPARSE_LATIN1; 968260e9a87SYuri Pankov *options &= ~MPARSE_UTF8; 969260e9a87SYuri Pankov } else if ( ! strcmp(arg, "us-ascii")) { 970260e9a87SYuri Pankov *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); 971260e9a87SYuri Pankov } else { 972371584c2SYuri Pankov warnx("-K %s: Bad argument", arg); 973371584c2SYuri Pankov return 0; 974260e9a87SYuri Pankov } 975371584c2SYuri Pankov return 1; 976260e9a87SYuri Pankov } 977260e9a87SYuri Pankov 978*c66b8046SYuri Pankov static void 979260e9a87SYuri Pankov moptions(int *options, char *arg) 98095c635efSGarrett D'Amore { 98195c635efSGarrett D'Amore 982260e9a87SYuri Pankov if (arg == NULL) 983*c66b8046SYuri Pankov return; 984*c66b8046SYuri Pankov if (strcmp(arg, "doc") == 0) 985260e9a87SYuri Pankov *options |= MPARSE_MDOC; 986*c66b8046SYuri Pankov else if (strcmp(arg, "an") == 0) 987260e9a87SYuri Pankov *options |= MPARSE_MAN; 98895c635efSGarrett D'Amore } 98995c635efSGarrett D'Amore 99095c635efSGarrett D'Amore static int 99195c635efSGarrett D'Amore toptions(struct curparse *curp, char *arg) 99295c635efSGarrett D'Amore { 99395c635efSGarrett D'Amore 99495c635efSGarrett D'Amore if (0 == strcmp(arg, "ascii")) 99595c635efSGarrett D'Amore curp->outtype = OUTT_ASCII; 99695c635efSGarrett D'Amore else if (0 == strcmp(arg, "lint")) { 99795c635efSGarrett D'Amore curp->outtype = OUTT_LINT; 998*c66b8046SYuri Pankov curp->mmin = MANDOCERR_BASE; 999*c66b8046SYuri Pankov mmsg_stream = stdout; 100095c635efSGarrett D'Amore } else if (0 == strcmp(arg, "tree")) 100195c635efSGarrett D'Amore curp->outtype = OUTT_TREE; 100295c635efSGarrett D'Amore else if (0 == strcmp(arg, "man")) 100395c635efSGarrett D'Amore curp->outtype = OUTT_MAN; 100495c635efSGarrett D'Amore else if (0 == strcmp(arg, "html")) 100595c635efSGarrett D'Amore curp->outtype = OUTT_HTML; 1006*c66b8046SYuri Pankov else if (0 == strcmp(arg, "markdown")) 1007*c66b8046SYuri Pankov curp->outtype = OUTT_MARKDOWN; 100895c635efSGarrett D'Amore else if (0 == strcmp(arg, "utf8")) 100995c635efSGarrett D'Amore curp->outtype = OUTT_UTF8; 101095c635efSGarrett D'Amore else if (0 == strcmp(arg, "locale")) 101195c635efSGarrett D'Amore curp->outtype = OUTT_LOCALE; 101295c635efSGarrett D'Amore else if (0 == strcmp(arg, "ps")) 101395c635efSGarrett D'Amore curp->outtype = OUTT_PS; 101495c635efSGarrett D'Amore else if (0 == strcmp(arg, "pdf")) 101595c635efSGarrett D'Amore curp->outtype = OUTT_PDF; 101695c635efSGarrett D'Amore else { 1017371584c2SYuri Pankov warnx("-T %s: Bad argument", arg); 1018371584c2SYuri Pankov return 0; 101995c635efSGarrett D'Amore } 102095c635efSGarrett D'Amore 1021371584c2SYuri Pankov return 1; 102295c635efSGarrett D'Amore } 102395c635efSGarrett D'Amore 102495c635efSGarrett D'Amore static int 102595c635efSGarrett D'Amore woptions(struct curparse *curp, char *arg) 102695c635efSGarrett D'Amore { 102795c635efSGarrett D'Amore char *v, *o; 1028*c66b8046SYuri Pankov const char *toks[11]; 102995c635efSGarrett D'Amore 103095c635efSGarrett D'Amore toks[0] = "stop"; 103195c635efSGarrett D'Amore toks[1] = "all"; 1032*c66b8046SYuri Pankov toks[2] = "base"; 1033*c66b8046SYuri Pankov toks[3] = "style"; 1034*c66b8046SYuri Pankov toks[4] = "warning"; 1035*c66b8046SYuri Pankov toks[5] = "error"; 1036*c66b8046SYuri Pankov toks[6] = "unsupp"; 1037*c66b8046SYuri Pankov toks[7] = "fatal"; 1038*c66b8046SYuri Pankov toks[8] = "openbsd"; 1039*c66b8046SYuri Pankov toks[9] = "netbsd"; 1040*c66b8046SYuri Pankov toks[10] = NULL; 104195c635efSGarrett D'Amore 104295c635efSGarrett D'Amore while (*arg) { 104395c635efSGarrett D'Amore o = arg; 1044a5934736SYuri Pankov switch (getsubopt(&arg, (char * const *)toks, &v)) { 1045260e9a87SYuri Pankov case 0: 104695c635efSGarrett D'Amore curp->wstop = 1; 104795c635efSGarrett D'Amore break; 1048260e9a87SYuri Pankov case 1: 1049260e9a87SYuri Pankov case 2: 1050*c66b8046SYuri Pankov curp->mmin = MANDOCERR_BASE; 105195c635efSGarrett D'Amore break; 1052260e9a87SYuri Pankov case 3: 1053*c66b8046SYuri Pankov curp->mmin = MANDOCERR_STYLE; 105495c635efSGarrett D'Amore break; 1055260e9a87SYuri Pankov case 4: 1056*c66b8046SYuri Pankov curp->mmin = MANDOCERR_WARNING; 1057260e9a87SYuri Pankov break; 1058260e9a87SYuri Pankov case 5: 1059*c66b8046SYuri Pankov curp->mmin = MANDOCERR_ERROR; 1060*c66b8046SYuri Pankov break; 1061*c66b8046SYuri Pankov case 6: 1062*c66b8046SYuri Pankov curp->mmin = MANDOCERR_UNSUPP; 1063*c66b8046SYuri Pankov break; 1064*c66b8046SYuri Pankov case 7: 1065*c66b8046SYuri Pankov curp->mmin = MANDOCERR_MAX; 1066*c66b8046SYuri Pankov break; 1067*c66b8046SYuri Pankov case 8: 1068*c66b8046SYuri Pankov curp->mmin = MANDOCERR_BASE; 1069*c66b8046SYuri Pankov curp->os_e = MANDOC_OS_OPENBSD; 1070*c66b8046SYuri Pankov break; 1071*c66b8046SYuri Pankov case 9: 1072*c66b8046SYuri Pankov curp->mmin = MANDOCERR_BASE; 1073*c66b8046SYuri Pankov curp->os_e = MANDOC_OS_NETBSD; 107495c635efSGarrett D'Amore break; 107595c635efSGarrett D'Amore default: 1076371584c2SYuri Pankov warnx("-W %s: Bad argument", o); 1077371584c2SYuri Pankov return 0; 107895c635efSGarrett D'Amore } 107995c635efSGarrett D'Amore } 1080371584c2SYuri Pankov return 1; 108195c635efSGarrett D'Amore } 108295c635efSGarrett D'Amore 108395c635efSGarrett D'Amore static void 1084260e9a87SYuri Pankov mmsg(enum mandocerr t, enum mandoclevel lvl, 108595c635efSGarrett D'Amore const char *file, int line, int col, const char *msg) 108695c635efSGarrett D'Amore { 1087260e9a87SYuri Pankov const char *mparse_msg; 1088260e9a87SYuri Pankov 1089*c66b8046SYuri Pankov fprintf(mmsg_stream, "%s: %s:", getprogname(), 1090371584c2SYuri Pankov file == NULL ? "<stdin>" : file); 1091260e9a87SYuri Pankov 1092260e9a87SYuri Pankov if (line) 1093*c66b8046SYuri Pankov fprintf(mmsg_stream, "%d:%d:", line, col + 1); 109495c635efSGarrett D'Amore 1095*c66b8046SYuri Pankov fprintf(mmsg_stream, " %s", mparse_strlevel(lvl)); 1096260e9a87SYuri Pankov 1097*c66b8046SYuri Pankov if ((mparse_msg = mparse_strerror(t)) != NULL) 1098*c66b8046SYuri Pankov fprintf(mmsg_stream, ": %s", mparse_msg); 109995c635efSGarrett D'Amore 110095c635efSGarrett D'Amore if (msg) 1101*c66b8046SYuri Pankov fprintf(mmsg_stream, ": %s", msg); 110295c635efSGarrett D'Amore 1103*c66b8046SYuri Pankov fputc('\n', mmsg_stream); 110495c635efSGarrett D'Amore } 1105260e9a87SYuri Pankov 1106260e9a87SYuri Pankov static pid_t 1107371584c2SYuri Pankov spawn_pager(struct tag_files *tag_files) 1108260e9a87SYuri Pankov { 1109371584c2SYuri Pankov const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ 1110260e9a87SYuri Pankov #define MAX_PAGER_ARGS 16 1111260e9a87SYuri Pankov char *argv[MAX_PAGER_ARGS]; 1112260e9a87SYuri Pankov const char *pager; 1113260e9a87SYuri Pankov char *cp; 1114371584c2SYuri Pankov size_t cmdlen; 1115260e9a87SYuri Pankov int argc; 1116260e9a87SYuri Pankov pid_t pager_pid; 1117260e9a87SYuri Pankov 1118260e9a87SYuri Pankov pager = getenv("MANPAGER"); 1119260e9a87SYuri Pankov if (pager == NULL || *pager == '\0') 1120260e9a87SYuri Pankov pager = getenv("PAGER"); 1121260e9a87SYuri Pankov if (pager == NULL || *pager == '\0') 1122371584c2SYuri Pankov pager = "more -s"; 1123260e9a87SYuri Pankov cp = mandoc_strdup(pager); 1124260e9a87SYuri Pankov 1125260e9a87SYuri Pankov /* 1126260e9a87SYuri Pankov * Parse the pager command into words. 1127260e9a87SYuri Pankov * Intentionally do not do anything fancy here. 1128260e9a87SYuri Pankov */ 1129260e9a87SYuri Pankov 1130260e9a87SYuri Pankov argc = 0; 1131371584c2SYuri Pankov while (argc + 4 < MAX_PAGER_ARGS) { 1132260e9a87SYuri Pankov argv[argc++] = cp; 1133260e9a87SYuri Pankov cp = strchr(cp, ' '); 1134260e9a87SYuri Pankov if (cp == NULL) 1135260e9a87SYuri Pankov break; 1136260e9a87SYuri Pankov *cp++ = '\0'; 1137260e9a87SYuri Pankov while (*cp == ' ') 1138260e9a87SYuri Pankov cp++; 1139260e9a87SYuri Pankov if (*cp == '\0') 1140260e9a87SYuri Pankov break; 1141260e9a87SYuri Pankov } 1142371584c2SYuri Pankov 1143371584c2SYuri Pankov /* For less(1), use the tag file. */ 1144371584c2SYuri Pankov 1145371584c2SYuri Pankov if ((cmdlen = strlen(argv[0])) >= 4) { 1146371584c2SYuri Pankov cp = argv[0] + cmdlen - 4; 1147371584c2SYuri Pankov if (strcmp(cp, "less") == 0) { 1148371584c2SYuri Pankov argv[argc++] = mandoc_strdup("-T"); 1149371584c2SYuri Pankov argv[argc++] = tag_files->tfn; 1150371584c2SYuri Pankov } 1151371584c2SYuri Pankov } 1152371584c2SYuri Pankov argv[argc++] = tag_files->ofn; 1153260e9a87SYuri Pankov argv[argc] = NULL; 1154260e9a87SYuri Pankov 1155371584c2SYuri Pankov switch (pager_pid = fork()) { 1156371584c2SYuri Pankov case -1: 1157371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "fork"); 1158371584c2SYuri Pankov case 0: 1159371584c2SYuri Pankov break; 1160371584c2SYuri Pankov default: 1161371584c2SYuri Pankov (void)setpgid(pager_pid, 0); 1162a40ea1a7SYuri Pankov (void)tcsetpgrp(tag_files->ofd, pager_pid); 1163371584c2SYuri Pankov #if HAVE_PLEDGE 1164371584c2SYuri Pankov if (pledge("stdio rpath tmppath tty proc", NULL) == -1) 1165371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 1166371584c2SYuri Pankov #endif 1167371584c2SYuri Pankov tag_files->pager_pid = pager_pid; 1168371584c2SYuri Pankov return pager_pid; 1169371584c2SYuri Pankov } 1170371584c2SYuri Pankov 1171371584c2SYuri Pankov /* The child process becomes the pager. */ 1172371584c2SYuri Pankov 1173371584c2SYuri Pankov if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) 1174371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pager stdout"); 1175371584c2SYuri Pankov close(tag_files->ofd); 1176371584c2SYuri Pankov close(tag_files->tfd); 1177371584c2SYuri Pankov 1178371584c2SYuri Pankov /* Do not start the pager before controlling the terminal. */ 1179371584c2SYuri Pankov 1180a40ea1a7SYuri Pankov while (tcgetpgrp(STDOUT_FILENO) != getpid()) 1181371584c2SYuri Pankov nanosleep(&timeout, NULL); 1182260e9a87SYuri Pankov 1183260e9a87SYuri Pankov execvp(argv[0], argv); 1184371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]); 1185260e9a87SYuri Pankov } 1186