1*371584c2SYuri Pankov /* $Id: main.c,v 1.269 2016/07/12 05:18:38 kristaps Exp $ */ 295c635efSGarrett D'Amore /* 3260e9a87SYuri Pankov * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4*371584c2SYuri Pankov * Copyright (c) 2010-2012, 2014-2016 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 * 11*371584c2SYuri 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 13*371584c2SYuri 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> 27*371584c2SYuri Pankov #if HAVE_ERR 28*371584c2SYuri Pankov #include <err.h> 29*371584c2SYuri Pankov #endif 30260e9a87SYuri Pankov #include <errno.h> 31260e9a87SYuri Pankov #include <fcntl.h> 32260e9a87SYuri Pankov #include <glob.h> 33*371584c2SYuri Pankov #if HAVE_SANDBOX_INIT 34*371584c2SYuri Pankov #include <sandbox.h> 35*371584c2SYuri Pankov #endif 36*371584c2SYuri 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> 41*371584c2SYuri Pankov #include <time.h> 4295c635efSGarrett D'Amore #include <unistd.h> 4395c635efSGarrett D'Amore 44260e9a87SYuri Pankov #include "mandoc_aux.h" 45*371584c2SYuri Pankov #include "mandoc.h" 46*371584c2SYuri Pankov #include "roff.h" 4795c635efSGarrett D'Amore #include "mdoc.h" 4895c635efSGarrett D'Amore #include "man.h" 49*371584c2SYuri Pankov #include "tag.h" 50*371584c2SYuri Pankov #include "main.h" 51*371584c2SYuri Pankov #include "manconf.h" 52260e9a87SYuri Pankov #include "mansearch.h" 5395c635efSGarrett D'Amore 5495c635efSGarrett D'Amore #if !defined(__GNUC__) || (__GNUC__ < 2) 5595c635efSGarrett D'Amore # if !defined(lint) 5695c635efSGarrett D'Amore # define __attribute__(x) 5795c635efSGarrett D'Amore # endif 5895c635efSGarrett D'Amore #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ 5995c635efSGarrett D'Amore 60260e9a87SYuri Pankov enum outmode { 61260e9a87SYuri Pankov OUTMODE_DEF = 0, 62260e9a87SYuri Pankov OUTMODE_FLN, 63260e9a87SYuri Pankov OUTMODE_LST, 64260e9a87SYuri Pankov OUTMODE_ALL, 65260e9a87SYuri Pankov OUTMODE_INT, 66260e9a87SYuri Pankov OUTMODE_ONE 67260e9a87SYuri Pankov }; 68260e9a87SYuri Pankov 6995c635efSGarrett D'Amore enum outt { 7095c635efSGarrett D'Amore OUTT_ASCII = 0, /* -Tascii */ 7195c635efSGarrett D'Amore OUTT_LOCALE, /* -Tlocale */ 7295c635efSGarrett D'Amore OUTT_UTF8, /* -Tutf8 */ 7395c635efSGarrett D'Amore OUTT_TREE, /* -Ttree */ 7495c635efSGarrett D'Amore OUTT_MAN, /* -Tman */ 7595c635efSGarrett D'Amore OUTT_HTML, /* -Thtml */ 7695c635efSGarrett D'Amore OUTT_LINT, /* -Tlint */ 7795c635efSGarrett D'Amore OUTT_PS, /* -Tps */ 7895c635efSGarrett D'Amore OUTT_PDF /* -Tpdf */ 7995c635efSGarrett D'Amore }; 8095c635efSGarrett D'Amore 8195c635efSGarrett D'Amore struct curparse { 8295c635efSGarrett D'Amore struct mparse *mp; 8395c635efSGarrett D'Amore enum mandoclevel wlevel; /* ignore messages below this */ 8495c635efSGarrett D'Amore int wstop; /* stop after a file with a warning */ 85260e9a87SYuri Pankov enum outt outtype; /* which output to use */ 8695c635efSGarrett D'Amore void *outdata; /* data for output */ 87*371584c2SYuri Pankov struct manoutput *outopts; /* output options */ 8895c635efSGarrett D'Amore }; 8995c635efSGarrett D'Amore 90260e9a87SYuri Pankov static int fs_lookup(const struct manpaths *, 91260e9a87SYuri Pankov size_t ipath, const char *, 92260e9a87SYuri Pankov const char *, const char *, 93260e9a87SYuri Pankov struct manpage **, size_t *); 94260e9a87SYuri Pankov static void fs_search(const struct mansearch *, 95260e9a87SYuri Pankov const struct manpaths *, int, char**, 96260e9a87SYuri Pankov struct manpage **, size_t *); 97260e9a87SYuri Pankov static int koptions(int *, char *); 98260e9a87SYuri Pankov #if HAVE_SQLITE3 99260e9a87SYuri Pankov int mandocdb(int, char**); 100260e9a87SYuri Pankov #endif 101260e9a87SYuri Pankov static int moptions(int *, char *); 10295c635efSGarrett D'Amore static void mmsg(enum mandocerr, enum mandoclevel, 10395c635efSGarrett D'Amore const char *, int, int, const char *); 104*371584c2SYuri Pankov static void parse(struct curparse *, int, const char *); 105*371584c2SYuri Pankov static void passthrough(const char *, int, int); 106*371584c2SYuri Pankov static pid_t spawn_pager(struct tag_files *); 10795c635efSGarrett D'Amore static int toptions(struct curparse *, char *); 108260e9a87SYuri Pankov static void usage(enum argmode) __attribute__((noreturn)); 10995c635efSGarrett D'Amore static int woptions(struct curparse *, char *); 11095c635efSGarrett D'Amore 111260e9a87SYuri Pankov static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; 112260e9a87SYuri Pankov static char help_arg[] = "help"; 113260e9a87SYuri Pankov static char *help_argv[] = {help_arg, NULL}; 114*371584c2SYuri Pankov static enum mandoclevel rc; 11595c635efSGarrett D'Amore 116260e9a87SYuri Pankov 11795c635efSGarrett D'Amore int 11895c635efSGarrett D'Amore main(int argc, char *argv[]) 11995c635efSGarrett D'Amore { 120*371584c2SYuri Pankov struct manconf conf; 12195c635efSGarrett D'Amore struct curparse curp; 122260e9a87SYuri Pankov struct mansearch search; 123*371584c2SYuri Pankov struct tag_files *tag_files; 124*371584c2SYuri Pankov const char *progname; 125260e9a87SYuri Pankov char *auxpaths; 126698f87a4SGarrett D'Amore char *defos; 127260e9a87SYuri Pankov unsigned char *uc; 128260e9a87SYuri Pankov struct manpage *res, *resp; 129260e9a87SYuri Pankov char *conf_file, *defpaths; 130*371584c2SYuri Pankov const char *sec; 131*371584c2SYuri Pankov size_t i, sz; 132*371584c2SYuri Pankov int prio, best_prio; 133260e9a87SYuri Pankov enum outmode outmode; 134260e9a87SYuri Pankov int fd; 135260e9a87SYuri Pankov int show_usage; 136260e9a87SYuri Pankov int options; 137*371584c2SYuri Pankov int use_pager; 138*371584c2SYuri Pankov int status, signum; 139260e9a87SYuri Pankov int c; 140*371584c2SYuri Pankov pid_t pager_pid, tc_pgid, man_pgid, pid; 14195c635efSGarrett D'Amore 142*371584c2SYuri Pankov #if HAVE_PROGNAME 143*371584c2SYuri Pankov progname = getprogname(); 144*371584c2SYuri Pankov #else 145260e9a87SYuri Pankov if (argc < 1) 146*371584c2SYuri Pankov progname = mandoc_strdup("mandoc"); 147260e9a87SYuri Pankov else if ((progname = strrchr(argv[0], '/')) == NULL) 14895c635efSGarrett D'Amore progname = argv[0]; 14995c635efSGarrett D'Amore else 15095c635efSGarrett D'Amore ++progname; 151*371584c2SYuri Pankov setprogname(progname); 152*371584c2SYuri Pankov #endif 15395c635efSGarrett D'Amore 154260e9a87SYuri Pankov #if HAVE_SQLITE3 155*371584c2SYuri Pankov if (strncmp(progname, "mandocdb", 8) == 0 || 156*371584c2SYuri Pankov strcmp(progname, BINM_MAKEWHATIS) == 0) 157*371584c2SYuri Pankov return mandocdb(argc, argv); 158*371584c2SYuri Pankov #endif 159*371584c2SYuri Pankov 160*371584c2SYuri Pankov #if HAVE_PLEDGE 161*371584c2SYuri Pankov if (pledge("stdio rpath tmppath tty proc exec flock", NULL) == -1) 162*371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 163*371584c2SYuri Pankov #endif 164*371584c2SYuri Pankov 165*371584c2SYuri Pankov #if HAVE_SANDBOX_INIT 166*371584c2SYuri Pankov if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) 167*371584c2SYuri Pankov errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); 168260e9a87SYuri Pankov #endif 169260e9a87SYuri Pankov 170260e9a87SYuri Pankov /* Search options. */ 17195c635efSGarrett D'Amore 172*371584c2SYuri Pankov memset(&conf, 0, sizeof(conf)); 173260e9a87SYuri Pankov conf_file = defpaths = NULL; 174260e9a87SYuri Pankov auxpaths = NULL; 175260e9a87SYuri Pankov 176260e9a87SYuri Pankov memset(&search, 0, sizeof(struct mansearch)); 177260e9a87SYuri Pankov search.outkey = "Nd"; 178260e9a87SYuri Pankov 179260e9a87SYuri Pankov if (strcmp(progname, BINM_MAN) == 0) 180260e9a87SYuri Pankov search.argmode = ARG_NAME; 181260e9a87SYuri Pankov else if (strcmp(progname, BINM_APROPOS) == 0) 182260e9a87SYuri Pankov search.argmode = ARG_EXPR; 183260e9a87SYuri Pankov else if (strcmp(progname, BINM_WHATIS) == 0) 184260e9a87SYuri Pankov search.argmode = ARG_WORD; 185260e9a87SYuri Pankov else if (strncmp(progname, "help", 4) == 0) 186260e9a87SYuri Pankov search.argmode = ARG_NAME; 187260e9a87SYuri Pankov else 188260e9a87SYuri Pankov search.argmode = ARG_FILE; 189260e9a87SYuri Pankov 190260e9a87SYuri Pankov /* Parser and formatter options. */ 191260e9a87SYuri Pankov 192260e9a87SYuri Pankov memset(&curp, 0, sizeof(struct curparse)); 193260e9a87SYuri Pankov curp.outtype = OUTT_LOCALE; 194260e9a87SYuri Pankov curp.wlevel = MANDOCLEVEL_BADARG; 195*371584c2SYuri Pankov curp.outopts = &conf.output; 196260e9a87SYuri Pankov options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; 197698f87a4SGarrett D'Amore defos = NULL; 19895c635efSGarrett D'Amore 199*371584c2SYuri Pankov use_pager = 1; 200*371584c2SYuri Pankov tag_files = NULL; 201260e9a87SYuri Pankov show_usage = 0; 202260e9a87SYuri Pankov outmode = OUTMODE_DEF; 203260e9a87SYuri Pankov 204260e9a87SYuri Pankov while (-1 != (c = getopt(argc, argv, 205260e9a87SYuri Pankov "aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) { 20695c635efSGarrett D'Amore switch (c) { 207260e9a87SYuri Pankov case 'a': 208260e9a87SYuri Pankov outmode = OUTMODE_ALL; 209260e9a87SYuri Pankov break; 210260e9a87SYuri Pankov case 'C': 211260e9a87SYuri Pankov conf_file = optarg; 212260e9a87SYuri Pankov break; 213260e9a87SYuri Pankov case 'c': 214*371584c2SYuri Pankov use_pager = 0; 215260e9a87SYuri Pankov break; 216260e9a87SYuri Pankov case 'f': 217260e9a87SYuri Pankov search.argmode = ARG_WORD; 218260e9a87SYuri Pankov break; 219260e9a87SYuri Pankov case 'h': 220*371584c2SYuri Pankov conf.output.synopsisonly = 1; 221*371584c2SYuri Pankov use_pager = 0; 222260e9a87SYuri Pankov outmode = OUTMODE_ALL; 223260e9a87SYuri Pankov break; 224260e9a87SYuri Pankov case 'I': 225698f87a4SGarrett D'Amore if (strncmp(optarg, "os=", 3)) { 226*371584c2SYuri Pankov warnx("-I %s: Bad argument", optarg); 227*371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 228698f87a4SGarrett D'Amore } 229698f87a4SGarrett D'Amore if (defos) { 230*371584c2SYuri Pankov warnx("-I %s: Duplicate argument", optarg); 231*371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 232698f87a4SGarrett D'Amore } 233698f87a4SGarrett D'Amore defos = mandoc_strdup(optarg + 3); 234698f87a4SGarrett D'Amore break; 235260e9a87SYuri Pankov case 'i': 236260e9a87SYuri Pankov outmode = OUTMODE_INT; 237260e9a87SYuri Pankov break; 238260e9a87SYuri Pankov case 'K': 239260e9a87SYuri Pankov if ( ! koptions(&options, optarg)) 240*371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 24195c635efSGarrett D'Amore break; 242260e9a87SYuri Pankov case 'k': 243260e9a87SYuri Pankov search.argmode = ARG_EXPR; 244260e9a87SYuri Pankov break; 245260e9a87SYuri Pankov case 'l': 246260e9a87SYuri Pankov search.argmode = ARG_FILE; 247260e9a87SYuri Pankov outmode = OUTMODE_ALL; 248260e9a87SYuri Pankov break; 249260e9a87SYuri Pankov case 'M': 250260e9a87SYuri Pankov defpaths = optarg; 251260e9a87SYuri Pankov break; 252260e9a87SYuri Pankov case 'm': 253260e9a87SYuri Pankov auxpaths = optarg; 254260e9a87SYuri Pankov break; 255260e9a87SYuri Pankov case 'O': 256260e9a87SYuri Pankov search.outkey = optarg; 257*371584c2SYuri Pankov while (optarg != NULL) 258*371584c2SYuri Pankov manconf_output(&conf.output, 259*371584c2SYuri Pankov strsep(&optarg, ",")); 26095c635efSGarrett D'Amore break; 261260e9a87SYuri Pankov case 'S': 262260e9a87SYuri Pankov search.arch = optarg; 263260e9a87SYuri Pankov break; 264260e9a87SYuri Pankov case 's': 265260e9a87SYuri Pankov search.sec = optarg; 266260e9a87SYuri Pankov break; 267260e9a87SYuri Pankov case 'T': 26895c635efSGarrett D'Amore if ( ! toptions(&curp, optarg)) 269*371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 27095c635efSGarrett D'Amore break; 271260e9a87SYuri Pankov case 'W': 27295c635efSGarrett D'Amore if ( ! woptions(&curp, optarg)) 273*371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 27495c635efSGarrett D'Amore break; 275260e9a87SYuri Pankov case 'w': 276260e9a87SYuri Pankov outmode = OUTMODE_FLN; 277260e9a87SYuri Pankov break; 27895c635efSGarrett D'Amore default: 279260e9a87SYuri Pankov show_usage = 1; 280260e9a87SYuri Pankov break; 28195c635efSGarrett D'Amore } 282260e9a87SYuri Pankov } 283260e9a87SYuri Pankov 284260e9a87SYuri Pankov if (show_usage) 285260e9a87SYuri Pankov usage(search.argmode); 28695c635efSGarrett D'Amore 287260e9a87SYuri Pankov /* Postprocess options. */ 288260e9a87SYuri Pankov 289260e9a87SYuri Pankov if (outmode == OUTMODE_DEF) { 290260e9a87SYuri Pankov switch (search.argmode) { 291260e9a87SYuri Pankov case ARG_FILE: 292260e9a87SYuri Pankov outmode = OUTMODE_ALL; 293*371584c2SYuri Pankov use_pager = 0; 294260e9a87SYuri Pankov break; 295260e9a87SYuri Pankov case ARG_NAME: 296260e9a87SYuri Pankov outmode = OUTMODE_ONE; 297260e9a87SYuri Pankov break; 298260e9a87SYuri Pankov default: 299260e9a87SYuri Pankov outmode = OUTMODE_LST; 300260e9a87SYuri Pankov break; 301260e9a87SYuri Pankov } 302260e9a87SYuri Pankov } 303260e9a87SYuri Pankov 304*371584c2SYuri Pankov if (outmode == OUTMODE_FLN || 305*371584c2SYuri Pankov outmode == OUTMODE_LST || 306*371584c2SYuri Pankov !isatty(STDOUT_FILENO)) 307*371584c2SYuri Pankov use_pager = 0; 308*371584c2SYuri Pankov 309*371584c2SYuri Pankov #if HAVE_PLEDGE 310*371584c2SYuri Pankov if (!use_pager) 311*371584c2SYuri Pankov if (pledge("stdio rpath flock", NULL) == -1) 312*371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 313*371584c2SYuri Pankov #endif 314*371584c2SYuri Pankov 315260e9a87SYuri Pankov /* Parse arguments. */ 316260e9a87SYuri Pankov 317260e9a87SYuri Pankov if (argc > 0) { 318260e9a87SYuri Pankov argc -= optind; 319260e9a87SYuri Pankov argv += optind; 320260e9a87SYuri Pankov } 321260e9a87SYuri Pankov resp = NULL; 322260e9a87SYuri Pankov 323260e9a87SYuri Pankov /* 324260e9a87SYuri Pankov * Quirks for help(1) 325260e9a87SYuri Pankov * and for a man(1) section argument without -s. 326260e9a87SYuri Pankov */ 327260e9a87SYuri Pankov 328260e9a87SYuri Pankov if (search.argmode == ARG_NAME) { 329260e9a87SYuri Pankov if (*progname == 'h') { 330260e9a87SYuri Pankov if (argc == 0) { 331260e9a87SYuri Pankov argv = help_argv; 332260e9a87SYuri Pankov argc = 1; 333260e9a87SYuri Pankov } 334260e9a87SYuri Pankov } else if (argc > 1 && 335260e9a87SYuri Pankov ((uc = (unsigned char *)argv[0]) != NULL) && 336260e9a87SYuri Pankov ((isdigit(uc[0]) && (uc[1] == '\0' || 337260e9a87SYuri Pankov (isalpha(uc[1]) && uc[2] == '\0'))) || 338260e9a87SYuri Pankov (uc[0] == 'n' && uc[1] == '\0'))) { 339260e9a87SYuri Pankov search.sec = (char *)uc; 340260e9a87SYuri Pankov argv++; 341260e9a87SYuri Pankov argc--; 342260e9a87SYuri Pankov } 343260e9a87SYuri Pankov if (search.arch == NULL) 344260e9a87SYuri Pankov search.arch = getenv("MACHINE"); 345260e9a87SYuri Pankov #ifdef MACHINE 346260e9a87SYuri Pankov if (search.arch == NULL) 347260e9a87SYuri Pankov search.arch = MACHINE; 348260e9a87SYuri Pankov #endif 349260e9a87SYuri Pankov } 350260e9a87SYuri Pankov 351260e9a87SYuri Pankov rc = MANDOCLEVEL_OK; 352260e9a87SYuri Pankov 353260e9a87SYuri Pankov /* man(1), whatis(1), apropos(1) */ 354260e9a87SYuri Pankov 355260e9a87SYuri Pankov if (search.argmode != ARG_FILE) { 356260e9a87SYuri Pankov if (argc == 0) 357260e9a87SYuri Pankov usage(search.argmode); 358260e9a87SYuri Pankov 359260e9a87SYuri Pankov if (search.argmode == ARG_NAME && 360260e9a87SYuri Pankov outmode == OUTMODE_ONE) 361260e9a87SYuri Pankov search.firstmatch = 1; 362260e9a87SYuri Pankov 363260e9a87SYuri Pankov /* Access the mandoc database. */ 364260e9a87SYuri Pankov 365*371584c2SYuri Pankov manconf_parse(&conf, conf_file, defpaths, auxpaths); 366260e9a87SYuri Pankov #if HAVE_SQLITE3 367260e9a87SYuri Pankov mansearch_setup(1); 368*371584c2SYuri Pankov if ( ! mansearch(&search, &conf.manpath, 369*371584c2SYuri Pankov argc, argv, &res, &sz)) 370260e9a87SYuri Pankov usage(search.argmode); 371260e9a87SYuri Pankov #else 372260e9a87SYuri Pankov if (search.argmode != ARG_NAME) { 373260e9a87SYuri Pankov fputs("mandoc: database support not compiled in\n", 374260e9a87SYuri Pankov stderr); 375*371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 376260e9a87SYuri Pankov } 377260e9a87SYuri Pankov sz = 0; 378260e9a87SYuri Pankov #endif 379260e9a87SYuri Pankov 380*371584c2SYuri Pankov if (sz == 0) { 381*371584c2SYuri Pankov if (search.argmode == ARG_NAME) 382*371584c2SYuri Pankov fs_search(&search, &conf.manpath, 383*371584c2SYuri Pankov argc, argv, &res, &sz); 384*371584c2SYuri Pankov else 385*371584c2SYuri Pankov warnx("nothing appropriate"); 386*371584c2SYuri Pankov } 387260e9a87SYuri Pankov 388260e9a87SYuri Pankov if (sz == 0) { 389260e9a87SYuri Pankov rc = MANDOCLEVEL_BADARG; 390260e9a87SYuri Pankov goto out; 391260e9a87SYuri Pankov } 392260e9a87SYuri Pankov 393260e9a87SYuri Pankov /* 394260e9a87SYuri Pankov * For standard man(1) and -a output mode, 395260e9a87SYuri Pankov * prepare for copying filename pointers 396260e9a87SYuri Pankov * into the program parameter array. 397260e9a87SYuri Pankov */ 398260e9a87SYuri Pankov 399260e9a87SYuri Pankov if (outmode == OUTMODE_ONE) { 400260e9a87SYuri Pankov argc = 1; 401*371584c2SYuri Pankov best_prio = 20; 402260e9a87SYuri Pankov } else if (outmode == OUTMODE_ALL) 403260e9a87SYuri Pankov argc = (int)sz; 404260e9a87SYuri Pankov 405260e9a87SYuri Pankov /* Iterate all matching manuals. */ 406260e9a87SYuri Pankov 407260e9a87SYuri Pankov resp = res; 408260e9a87SYuri Pankov for (i = 0; i < sz; i++) { 409260e9a87SYuri Pankov if (outmode == OUTMODE_FLN) 410260e9a87SYuri Pankov puts(res[i].file); 411260e9a87SYuri Pankov else if (outmode == OUTMODE_LST) 412260e9a87SYuri Pankov printf("%s - %s\n", res[i].names, 413260e9a87SYuri Pankov res[i].output == NULL ? "" : 414260e9a87SYuri Pankov res[i].output); 415260e9a87SYuri Pankov else if (outmode == OUTMODE_ONE) { 416260e9a87SYuri Pankov /* Search for the best section. */ 417*371584c2SYuri Pankov sec = res[i].file; 418*371584c2SYuri Pankov sec += strcspn(sec, "123456789"); 419*371584c2SYuri Pankov if (sec[0] == '\0') 420260e9a87SYuri Pankov continue; 421*371584c2SYuri Pankov prio = sec_prios[sec[0] - '1']; 422*371584c2SYuri Pankov if (sec[1] != '/') 423*371584c2SYuri Pankov prio += 10; 424260e9a87SYuri Pankov if (prio >= best_prio) 425260e9a87SYuri Pankov continue; 426260e9a87SYuri Pankov best_prio = prio; 427260e9a87SYuri Pankov resp = res + i; 428260e9a87SYuri Pankov } 429260e9a87SYuri Pankov } 430260e9a87SYuri Pankov 431260e9a87SYuri Pankov /* 432260e9a87SYuri Pankov * For man(1), -a and -i output mode, fall through 433260e9a87SYuri Pankov * to the main mandoc(1) code iterating files 434260e9a87SYuri Pankov * and running the parsers on each of them. 435260e9a87SYuri Pankov */ 436260e9a87SYuri Pankov 437260e9a87SYuri Pankov if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) 438260e9a87SYuri Pankov goto out; 439260e9a87SYuri Pankov } 440260e9a87SYuri Pankov 441260e9a87SYuri Pankov /* mandoc(1) */ 442260e9a87SYuri Pankov 443*371584c2SYuri Pankov #if HAVE_PLEDGE 444*371584c2SYuri Pankov if (use_pager) { 445*371584c2SYuri Pankov if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) 446*371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 447*371584c2SYuri Pankov } else { 448*371584c2SYuri Pankov if (pledge("stdio rpath", NULL) == -1) 449*371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 450*371584c2SYuri Pankov } 451*371584c2SYuri Pankov #endif 452*371584c2SYuri Pankov 453260e9a87SYuri Pankov if (search.argmode == ARG_FILE && ! moptions(&options, auxpaths)) 454*371584c2SYuri Pankov return (int)MANDOCLEVEL_BADARG; 455260e9a87SYuri Pankov 456*371584c2SYuri Pankov mchars_alloc(); 457*371584c2SYuri Pankov curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos); 45895c635efSGarrett D'Amore 45995c635efSGarrett D'Amore /* 46095c635efSGarrett D'Amore * Conditionally start up the lookaside buffer before parsing. 46195c635efSGarrett D'Amore */ 46295c635efSGarrett D'Amore if (OUTT_MAN == curp.outtype) 46395c635efSGarrett D'Amore mparse_keep(curp.mp); 46495c635efSGarrett D'Amore 465260e9a87SYuri Pankov if (argc < 1) { 466*371584c2SYuri Pankov if (use_pager) 467*371584c2SYuri Pankov tag_files = tag_init(); 468*371584c2SYuri Pankov parse(&curp, STDIN_FILENO, "<stdin>"); 469260e9a87SYuri Pankov } 47095c635efSGarrett D'Amore 471260e9a87SYuri Pankov while (argc > 0) { 472*371584c2SYuri Pankov fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv); 473260e9a87SYuri Pankov if (fd != -1) { 474*371584c2SYuri Pankov if (use_pager) { 475*371584c2SYuri Pankov tag_files = tag_init(); 476*371584c2SYuri Pankov use_pager = 0; 477*371584c2SYuri Pankov } 478260e9a87SYuri Pankov 479260e9a87SYuri Pankov if (resp == NULL) 480*371584c2SYuri Pankov parse(&curp, fd, *argv); 481260e9a87SYuri Pankov else if (resp->form & FORM_SRC) { 482260e9a87SYuri Pankov /* For .so only; ignore failure. */ 483*371584c2SYuri Pankov chdir(conf.manpath.paths[resp->ipath]); 484*371584c2SYuri Pankov parse(&curp, fd, resp->file); 485*371584c2SYuri Pankov } else 486*371584c2SYuri Pankov passthrough(resp->file, fd, 487*371584c2SYuri Pankov conf.output.synopsisonly); 488260e9a87SYuri Pankov 489260e9a87SYuri Pankov if (argc > 1 && curp.outtype <= OUTT_UTF8) 490*371584c2SYuri Pankov terminal_sepline(curp.outdata); 491*371584c2SYuri Pankov } else if (rc < MANDOCLEVEL_ERROR) 492*371584c2SYuri Pankov rc = MANDOCLEVEL_ERROR; 49395c635efSGarrett D'Amore 49495c635efSGarrett D'Amore if (MANDOCLEVEL_OK != rc && curp.wstop) 49595c635efSGarrett D'Amore break; 496260e9a87SYuri Pankov 497260e9a87SYuri Pankov if (resp != NULL) 498260e9a87SYuri Pankov resp++; 499260e9a87SYuri Pankov else 500260e9a87SYuri Pankov argv++; 501260e9a87SYuri Pankov if (--argc) 502260e9a87SYuri Pankov mparse_reset(curp.mp); 50395c635efSGarrett D'Amore } 50495c635efSGarrett D'Amore 505*371584c2SYuri Pankov if (curp.outdata != NULL) { 506*371584c2SYuri Pankov switch (curp.outtype) { 507*371584c2SYuri Pankov case OUTT_HTML: 508*371584c2SYuri Pankov html_free(curp.outdata); 509*371584c2SYuri Pankov break; 510*371584c2SYuri Pankov case OUTT_UTF8: 511*371584c2SYuri Pankov case OUTT_LOCALE: 512*371584c2SYuri Pankov case OUTT_ASCII: 513*371584c2SYuri Pankov ascii_free(curp.outdata); 514*371584c2SYuri Pankov break; 515*371584c2SYuri Pankov case OUTT_PDF: 516*371584c2SYuri Pankov case OUTT_PS: 517*371584c2SYuri Pankov pspdf_free(curp.outdata); 518*371584c2SYuri Pankov break; 519*371584c2SYuri Pankov default: 520*371584c2SYuri Pankov break; 521*371584c2SYuri Pankov } 522*371584c2SYuri Pankov } 523260e9a87SYuri Pankov mparse_free(curp.mp); 524*371584c2SYuri Pankov mchars_free(); 525260e9a87SYuri Pankov 526260e9a87SYuri Pankov out: 527260e9a87SYuri Pankov if (search.argmode != ARG_FILE) { 528*371584c2SYuri Pankov manconf_free(&conf); 529260e9a87SYuri Pankov #if HAVE_SQLITE3 530260e9a87SYuri Pankov mansearch_free(res, sz); 531260e9a87SYuri Pankov mansearch_setup(0); 532260e9a87SYuri Pankov #endif 533260e9a87SYuri Pankov } 534260e9a87SYuri Pankov 535698f87a4SGarrett D'Amore free(defos); 53695c635efSGarrett D'Amore 537260e9a87SYuri Pankov /* 538*371584c2SYuri Pankov * When using a pager, finish writing both temporary files, 539*371584c2SYuri Pankov * fork it, wait for the user to close it, and clean up. 540260e9a87SYuri Pankov */ 541260e9a87SYuri Pankov 542*371584c2SYuri Pankov if (tag_files != NULL) { 543260e9a87SYuri Pankov fclose(stdout); 544*371584c2SYuri Pankov tag_write(); 545*371584c2SYuri Pankov man_pgid = getpgid(0); 546*371584c2SYuri Pankov tag_files->tcpgid = man_pgid == getpid() ? 547*371584c2SYuri Pankov getpgid(getppid()) : man_pgid; 548*371584c2SYuri Pankov pager_pid = 0; 549*371584c2SYuri Pankov signum = SIGSTOP; 550*371584c2SYuri Pankov for (;;) { 551*371584c2SYuri Pankov 552*371584c2SYuri Pankov /* Stop here until moved to the foreground. */ 553*371584c2SYuri Pankov 554*371584c2SYuri Pankov tc_pgid = tcgetpgrp(STDIN_FILENO); 555*371584c2SYuri Pankov if (tc_pgid != man_pgid) { 556*371584c2SYuri Pankov if (tc_pgid == pager_pid) { 557*371584c2SYuri Pankov (void)tcsetpgrp(STDIN_FILENO, 558*371584c2SYuri Pankov man_pgid); 559*371584c2SYuri Pankov if (signum == SIGTTIN) 560*371584c2SYuri Pankov continue; 561*371584c2SYuri Pankov } else 562*371584c2SYuri Pankov tag_files->tcpgid = tc_pgid; 563*371584c2SYuri Pankov kill(0, signum); 564*371584c2SYuri Pankov continue; 565*371584c2SYuri Pankov } 566*371584c2SYuri Pankov 567*371584c2SYuri Pankov /* Once in the foreground, activate the pager. */ 568*371584c2SYuri Pankov 569*371584c2SYuri Pankov if (pager_pid) { 570*371584c2SYuri Pankov (void)tcsetpgrp(STDIN_FILENO, pager_pid); 571*371584c2SYuri Pankov kill(pager_pid, SIGCONT); 572*371584c2SYuri Pankov } else 573*371584c2SYuri Pankov pager_pid = spawn_pager(tag_files); 574*371584c2SYuri Pankov 575*371584c2SYuri Pankov /* Wait for the pager to stop or exit. */ 576*371584c2SYuri Pankov 577*371584c2SYuri Pankov while ((pid = waitpid(pager_pid, &status, 578*371584c2SYuri Pankov WUNTRACED)) == -1 && errno == EINTR) 579*371584c2SYuri Pankov continue; 580*371584c2SYuri Pankov 581*371584c2SYuri Pankov if (pid == -1) { 582*371584c2SYuri Pankov warn("wait"); 583*371584c2SYuri Pankov rc = MANDOCLEVEL_SYSERR; 584*371584c2SYuri Pankov break; 585*371584c2SYuri Pankov } 586*371584c2SYuri Pankov if (!WIFSTOPPED(status)) 587*371584c2SYuri Pankov break; 588*371584c2SYuri Pankov 589*371584c2SYuri Pankov signum = WSTOPSIG(status); 590*371584c2SYuri Pankov } 591*371584c2SYuri Pankov tag_unlink(); 592260e9a87SYuri Pankov } 593260e9a87SYuri Pankov 594*371584c2SYuri Pankov return (int)rc; 59595c635efSGarrett D'Amore } 59695c635efSGarrett D'Amore 59795c635efSGarrett D'Amore static void 598260e9a87SYuri Pankov usage(enum argmode argmode) 59995c635efSGarrett D'Amore { 60095c635efSGarrett D'Amore 601260e9a87SYuri Pankov switch (argmode) { 602260e9a87SYuri Pankov case ARG_FILE: 603*371584c2SYuri Pankov fputs("usage: mandoc [-acfhkl] [-I os=name] " 604*371584c2SYuri Pankov "[-K encoding] [-mformat] [-O option]\n" 605*371584c2SYuri Pankov "\t [-T output] [-W level] [file ...]\n", stderr); 606260e9a87SYuri Pankov break; 607260e9a87SYuri Pankov case ARG_NAME: 608260e9a87SYuri Pankov fputs("usage: man [-acfhklw] [-C file] [-I os=name] " 609260e9a87SYuri Pankov "[-K encoding] [-M path] [-m path]\n" 610260e9a87SYuri Pankov "\t [-O option=value] [-S subsection] [-s section] " 611260e9a87SYuri Pankov "[-T output] [-W level]\n" 612260e9a87SYuri Pankov "\t [section] name ...\n", stderr); 613260e9a87SYuri Pankov break; 614260e9a87SYuri Pankov case ARG_WORD: 615260e9a87SYuri Pankov fputs("usage: whatis [-acfhklw] [-C file] " 616260e9a87SYuri Pankov "[-M path] [-m path] [-O outkey] [-S arch]\n" 617260e9a87SYuri Pankov "\t [-s section] name ...\n", stderr); 618260e9a87SYuri Pankov break; 619260e9a87SYuri Pankov case ARG_EXPR: 620260e9a87SYuri Pankov fputs("usage: apropos [-acfhklw] [-C file] " 621260e9a87SYuri Pankov "[-M path] [-m path] [-O outkey] [-S arch]\n" 622260e9a87SYuri Pankov "\t [-s section] expression ...\n", stderr); 623260e9a87SYuri Pankov break; 624260e9a87SYuri Pankov } 625260e9a87SYuri Pankov exit((int)MANDOCLEVEL_BADARG); 62695c635efSGarrett D'Amore } 62795c635efSGarrett D'Amore 628260e9a87SYuri Pankov static int 629260e9a87SYuri Pankov fs_lookup(const struct manpaths *paths, size_t ipath, 630260e9a87SYuri Pankov const char *sec, const char *arch, const char *name, 631260e9a87SYuri Pankov struct manpage **res, size_t *ressz) 63295c635efSGarrett D'Amore { 633260e9a87SYuri Pankov glob_t globinfo; 634260e9a87SYuri Pankov struct manpage *page; 635260e9a87SYuri Pankov char *file; 636260e9a87SYuri Pankov int form, globres; 637260e9a87SYuri Pankov 638260e9a87SYuri Pankov form = FORM_SRC; 639260e9a87SYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s.%s", 640260e9a87SYuri Pankov paths->paths[ipath], sec, name, sec); 641260e9a87SYuri Pankov if (access(file, R_OK) != -1) 642260e9a87SYuri Pankov goto found; 643260e9a87SYuri Pankov free(file); 644260e9a87SYuri Pankov 645260e9a87SYuri Pankov mandoc_asprintf(&file, "%s/cat%s/%s.0", 646260e9a87SYuri Pankov paths->paths[ipath], sec, name); 647260e9a87SYuri Pankov if (access(file, R_OK) != -1) { 648260e9a87SYuri Pankov form = FORM_CAT; 649260e9a87SYuri Pankov goto found; 650260e9a87SYuri Pankov } 651260e9a87SYuri Pankov free(file); 652260e9a87SYuri Pankov 653260e9a87SYuri Pankov if (arch != NULL) { 654260e9a87SYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", 655260e9a87SYuri Pankov paths->paths[ipath], sec, arch, name, sec); 656260e9a87SYuri Pankov if (access(file, R_OK) != -1) 657260e9a87SYuri Pankov goto found; 658260e9a87SYuri Pankov free(file); 659260e9a87SYuri Pankov } 660260e9a87SYuri Pankov 661*371584c2SYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", 662260e9a87SYuri Pankov paths->paths[ipath], sec, name); 663260e9a87SYuri Pankov globres = glob(file, 0, NULL, &globinfo); 664260e9a87SYuri Pankov if (globres != 0 && globres != GLOB_NOMATCH) 665*371584c2SYuri Pankov warn("%s: glob", file); 666260e9a87SYuri Pankov free(file); 667260e9a87SYuri Pankov if (globres == 0) 668260e9a87SYuri Pankov file = mandoc_strdup(*globinfo.gl_pathv); 669260e9a87SYuri Pankov globfree(&globinfo); 670260e9a87SYuri Pankov if (globres != 0) 671*371584c2SYuri Pankov return 0; 67295c635efSGarrett D'Amore 673260e9a87SYuri Pankov found: 674260e9a87SYuri Pankov #if HAVE_SQLITE3 675*371584c2SYuri Pankov warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", 676*371584c2SYuri Pankov name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); 677260e9a87SYuri Pankov #endif 678260e9a87SYuri Pankov *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); 679260e9a87SYuri Pankov page = *res + (*ressz - 1); 680260e9a87SYuri Pankov page->file = file; 681260e9a87SYuri Pankov page->names = NULL; 682260e9a87SYuri Pankov page->output = NULL; 683260e9a87SYuri Pankov page->ipath = ipath; 684260e9a87SYuri Pankov page->bits = NAME_FILE & NAME_MASK; 685260e9a87SYuri Pankov page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; 686260e9a87SYuri Pankov page->form = form; 687*371584c2SYuri Pankov return 1; 688260e9a87SYuri Pankov } 689260e9a87SYuri Pankov 690260e9a87SYuri Pankov static void 691260e9a87SYuri Pankov fs_search(const struct mansearch *cfg, const struct manpaths *paths, 692260e9a87SYuri Pankov int argc, char **argv, struct manpage **res, size_t *ressz) 693260e9a87SYuri Pankov { 694260e9a87SYuri Pankov const char *const sections[] = 695*371584c2SYuri Pankov {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; 696260e9a87SYuri Pankov const size_t nsec = sizeof(sections)/sizeof(sections[0]); 697260e9a87SYuri Pankov 698260e9a87SYuri Pankov size_t ipath, isec, lastsz; 699260e9a87SYuri Pankov 700260e9a87SYuri Pankov assert(cfg->argmode == ARG_NAME); 701260e9a87SYuri Pankov 702260e9a87SYuri Pankov *res = NULL; 703260e9a87SYuri Pankov *ressz = lastsz = 0; 704260e9a87SYuri Pankov while (argc) { 705260e9a87SYuri Pankov for (ipath = 0; ipath < paths->sz; ipath++) { 706260e9a87SYuri Pankov if (cfg->sec != NULL) { 707260e9a87SYuri Pankov if (fs_lookup(paths, ipath, cfg->sec, 708260e9a87SYuri Pankov cfg->arch, *argv, res, ressz) && 709260e9a87SYuri Pankov cfg->firstmatch) 710260e9a87SYuri Pankov return; 711260e9a87SYuri Pankov } else for (isec = 0; isec < nsec; isec++) 712260e9a87SYuri Pankov if (fs_lookup(paths, ipath, sections[isec], 713260e9a87SYuri Pankov cfg->arch, *argv, res, ressz) && 714260e9a87SYuri Pankov cfg->firstmatch) 715260e9a87SYuri Pankov return; 716260e9a87SYuri Pankov } 717260e9a87SYuri Pankov if (*ressz == lastsz) 718*371584c2SYuri Pankov warnx("No entry for %s in the manual.", *argv); 719260e9a87SYuri Pankov lastsz = *ressz; 720260e9a87SYuri Pankov argv++; 721260e9a87SYuri Pankov argc--; 722260e9a87SYuri Pankov } 72395c635efSGarrett D'Amore } 72495c635efSGarrett D'Amore 72595c635efSGarrett D'Amore static void 726*371584c2SYuri Pankov parse(struct curparse *curp, int fd, const char *file) 72795c635efSGarrett D'Amore { 728*371584c2SYuri Pankov enum mandoclevel rctmp; 729*371584c2SYuri Pankov struct roff_man *man; 73095c635efSGarrett D'Amore 73195c635efSGarrett D'Amore /* Begin by parsing the file itself. */ 73295c635efSGarrett D'Amore 73395c635efSGarrett D'Amore assert(file); 734*371584c2SYuri Pankov assert(fd >= 0); 73595c635efSGarrett D'Amore 736*371584c2SYuri Pankov rctmp = mparse_readfd(curp->mp, fd, file); 737*371584c2SYuri Pankov if (fd != STDIN_FILENO) 738*371584c2SYuri Pankov close(fd); 739*371584c2SYuri Pankov if (rc < rctmp) 740*371584c2SYuri Pankov rc = rctmp; 74195c635efSGarrett D'Amore 74295c635efSGarrett D'Amore /* 74395c635efSGarrett D'Amore * With -Wstop and warnings or errors of at least the requested 74495c635efSGarrett D'Amore * level, do not produce output. 74595c635efSGarrett D'Amore */ 74695c635efSGarrett D'Amore 747*371584c2SYuri Pankov if (rctmp != MANDOCLEVEL_OK && curp->wstop) 748*371584c2SYuri Pankov return; 74995c635efSGarrett D'Amore 75095c635efSGarrett D'Amore /* If unset, allocate output dev now (if applicable). */ 75195c635efSGarrett D'Amore 752*371584c2SYuri Pankov if (curp->outdata == NULL) { 75395c635efSGarrett D'Amore switch (curp->outtype) { 754260e9a87SYuri Pankov case OUTT_HTML: 755*371584c2SYuri Pankov curp->outdata = html_alloc(curp->outopts); 75695c635efSGarrett D'Amore break; 757260e9a87SYuri Pankov case OUTT_UTF8: 758*371584c2SYuri Pankov curp->outdata = utf8_alloc(curp->outopts); 75995c635efSGarrett D'Amore break; 760260e9a87SYuri Pankov case OUTT_LOCALE: 761*371584c2SYuri Pankov curp->outdata = locale_alloc(curp->outopts); 76295c635efSGarrett D'Amore break; 763260e9a87SYuri Pankov case OUTT_ASCII: 764*371584c2SYuri Pankov curp->outdata = ascii_alloc(curp->outopts); 76595c635efSGarrett D'Amore break; 766260e9a87SYuri Pankov case OUTT_PDF: 767*371584c2SYuri Pankov curp->outdata = pdf_alloc(curp->outopts); 76895c635efSGarrett D'Amore break; 769260e9a87SYuri Pankov case OUTT_PS: 770*371584c2SYuri Pankov curp->outdata = ps_alloc(curp->outopts); 77195c635efSGarrett D'Amore break; 77295c635efSGarrett D'Amore default: 77395c635efSGarrett D'Amore break; 77495c635efSGarrett D'Amore } 775*371584c2SYuri Pankov } 776*371584c2SYuri Pankov 777*371584c2SYuri Pankov mparse_result(curp->mp, &man, NULL); 77895c635efSGarrett D'Amore 779*371584c2SYuri Pankov /* Execute the out device, if it exists. */ 780*371584c2SYuri Pankov 781*371584c2SYuri Pankov if (man == NULL) 782*371584c2SYuri Pankov return; 783*371584c2SYuri Pankov if (man->macroset == MACROSET_MDOC) { 784*371584c2SYuri Pankov mdoc_validate(man); 78595c635efSGarrett D'Amore switch (curp->outtype) { 786260e9a87SYuri Pankov case OUTT_HTML: 787*371584c2SYuri Pankov html_mdoc(curp->outdata, man); 78895c635efSGarrett D'Amore break; 789260e9a87SYuri Pankov case OUTT_TREE: 790*371584c2SYuri Pankov tree_mdoc(curp->outdata, man); 79195c635efSGarrett D'Amore break; 792260e9a87SYuri Pankov case OUTT_MAN: 793*371584c2SYuri Pankov man_mdoc(curp->outdata, man); 79495c635efSGarrett D'Amore break; 795260e9a87SYuri Pankov case OUTT_PDF: 796260e9a87SYuri Pankov case OUTT_ASCII: 797260e9a87SYuri Pankov case OUTT_UTF8: 798260e9a87SYuri Pankov case OUTT_LOCALE: 799260e9a87SYuri Pankov case OUTT_PS: 800*371584c2SYuri Pankov terminal_mdoc(curp->outdata, man); 801*371584c2SYuri Pankov break; 802*371584c2SYuri Pankov default: 803*371584c2SYuri Pankov break; 804*371584c2SYuri Pankov } 805*371584c2SYuri Pankov } 806*371584c2SYuri Pankov if (man->macroset == MACROSET_MAN) { 807*371584c2SYuri Pankov man_validate(man); 808*371584c2SYuri Pankov switch (curp->outtype) { 809*371584c2SYuri Pankov case OUTT_HTML: 810*371584c2SYuri Pankov html_man(curp->outdata, man); 811*371584c2SYuri Pankov break; 812*371584c2SYuri Pankov case OUTT_TREE: 813*371584c2SYuri Pankov tree_man(curp->outdata, man); 814*371584c2SYuri Pankov break; 815*371584c2SYuri Pankov case OUTT_MAN: 816*371584c2SYuri Pankov man_man(curp->outdata, man); 817*371584c2SYuri Pankov break; 818*371584c2SYuri Pankov case OUTT_PDF: 819*371584c2SYuri Pankov case OUTT_ASCII: 820*371584c2SYuri Pankov case OUTT_UTF8: 821*371584c2SYuri Pankov case OUTT_LOCALE: 822*371584c2SYuri Pankov case OUTT_PS: 823*371584c2SYuri Pankov terminal_man(curp->outdata, man); 82495c635efSGarrett D'Amore break; 82595c635efSGarrett D'Amore default: 82695c635efSGarrett D'Amore break; 82795c635efSGarrett D'Amore } 82895c635efSGarrett D'Amore } 82995c635efSGarrett D'Amore } 83095c635efSGarrett D'Amore 831*371584c2SYuri Pankov static void 832260e9a87SYuri Pankov passthrough(const char *file, int fd, int synopsis_only) 833260e9a87SYuri Pankov { 834260e9a87SYuri Pankov const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; 835260e9a87SYuri Pankov const char synr[] = "SYNOPSIS"; 836260e9a87SYuri Pankov 837260e9a87SYuri Pankov FILE *stream; 838260e9a87SYuri Pankov const char *syscall; 839*371584c2SYuri Pankov char *line, *cp; 840*371584c2SYuri Pankov size_t linesz; 841260e9a87SYuri Pankov int print; 842260e9a87SYuri Pankov 843*371584c2SYuri Pankov line = NULL; 844*371584c2SYuri Pankov linesz = 0; 845260e9a87SYuri Pankov 846260e9a87SYuri Pankov if ((stream = fdopen(fd, "r")) == NULL) { 847260e9a87SYuri Pankov close(fd); 848260e9a87SYuri Pankov syscall = "fdopen"; 849260e9a87SYuri Pankov goto fail; 850260e9a87SYuri Pankov } 851260e9a87SYuri Pankov 852260e9a87SYuri Pankov print = 0; 853*371584c2SYuri Pankov while (getline(&line, &linesz, stream) != -1) { 854*371584c2SYuri Pankov cp = line; 855260e9a87SYuri Pankov if (synopsis_only) { 856260e9a87SYuri Pankov if (print) { 857*371584c2SYuri Pankov if ( ! isspace((unsigned char)*cp)) 858260e9a87SYuri Pankov goto done; 859*371584c2SYuri Pankov while (isspace((unsigned char)*cp)) 860*371584c2SYuri Pankov cp++; 861260e9a87SYuri Pankov } else { 862*371584c2SYuri Pankov if (strcmp(cp, synb) == 0 || 863*371584c2SYuri Pankov strcmp(cp, synr) == 0) 864260e9a87SYuri Pankov print = 1; 865260e9a87SYuri Pankov continue; 866260e9a87SYuri Pankov } 867260e9a87SYuri Pankov } 868*371584c2SYuri Pankov if (fputs(cp, stdout)) { 869*371584c2SYuri Pankov fclose(stream); 870*371584c2SYuri Pankov syscall = "fputs"; 871*371584c2SYuri Pankov goto fail; 872*371584c2SYuri Pankov } 873260e9a87SYuri Pankov } 874260e9a87SYuri Pankov 875260e9a87SYuri Pankov if (ferror(stream)) { 876260e9a87SYuri Pankov fclose(stream); 877*371584c2SYuri Pankov syscall = "getline"; 878260e9a87SYuri Pankov goto fail; 879260e9a87SYuri Pankov } 880260e9a87SYuri Pankov 881260e9a87SYuri Pankov done: 882*371584c2SYuri Pankov free(line); 883260e9a87SYuri Pankov fclose(stream); 884*371584c2SYuri Pankov return; 885260e9a87SYuri Pankov 886260e9a87SYuri Pankov fail: 887*371584c2SYuri Pankov free(line); 888*371584c2SYuri Pankov warn("%s: SYSERR: %s", file, syscall); 889*371584c2SYuri Pankov if (rc < MANDOCLEVEL_SYSERR) 890*371584c2SYuri Pankov rc = MANDOCLEVEL_SYSERR; 891260e9a87SYuri Pankov } 892260e9a87SYuri Pankov 893260e9a87SYuri Pankov static int 894260e9a87SYuri Pankov koptions(int *options, char *arg) 895260e9a87SYuri Pankov { 896260e9a87SYuri Pankov 897260e9a87SYuri Pankov if ( ! strcmp(arg, "utf-8")) { 898260e9a87SYuri Pankov *options |= MPARSE_UTF8; 899260e9a87SYuri Pankov *options &= ~MPARSE_LATIN1; 900260e9a87SYuri Pankov } else if ( ! strcmp(arg, "iso-8859-1")) { 901260e9a87SYuri Pankov *options |= MPARSE_LATIN1; 902260e9a87SYuri Pankov *options &= ~MPARSE_UTF8; 903260e9a87SYuri Pankov } else if ( ! strcmp(arg, "us-ascii")) { 904260e9a87SYuri Pankov *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); 905260e9a87SYuri Pankov } else { 906*371584c2SYuri Pankov warnx("-K %s: Bad argument", arg); 907*371584c2SYuri Pankov return 0; 908260e9a87SYuri Pankov } 909*371584c2SYuri Pankov return 1; 910260e9a87SYuri Pankov } 911260e9a87SYuri Pankov 91295c635efSGarrett D'Amore static int 913260e9a87SYuri Pankov moptions(int *options, char *arg) 91495c635efSGarrett D'Amore { 91595c635efSGarrett D'Amore 916260e9a87SYuri Pankov if (arg == NULL) 917260e9a87SYuri Pankov /* nothing to do */; 918260e9a87SYuri Pankov else if (0 == strcmp(arg, "doc")) 919260e9a87SYuri Pankov *options |= MPARSE_MDOC; 92095c635efSGarrett D'Amore else if (0 == strcmp(arg, "andoc")) 921260e9a87SYuri Pankov /* nothing to do */; 92295c635efSGarrett D'Amore else if (0 == strcmp(arg, "an")) 923260e9a87SYuri Pankov *options |= MPARSE_MAN; 92495c635efSGarrett D'Amore else { 925*371584c2SYuri Pankov warnx("-m %s: Bad argument", arg); 926*371584c2SYuri Pankov return 0; 92795c635efSGarrett D'Amore } 92895c635efSGarrett D'Amore 929*371584c2SYuri Pankov return 1; 93095c635efSGarrett D'Amore } 93195c635efSGarrett D'Amore 93295c635efSGarrett D'Amore static int 93395c635efSGarrett D'Amore toptions(struct curparse *curp, char *arg) 93495c635efSGarrett D'Amore { 93595c635efSGarrett D'Amore 93695c635efSGarrett D'Amore if (0 == strcmp(arg, "ascii")) 93795c635efSGarrett D'Amore curp->outtype = OUTT_ASCII; 93895c635efSGarrett D'Amore else if (0 == strcmp(arg, "lint")) { 93995c635efSGarrett D'Amore curp->outtype = OUTT_LINT; 94095c635efSGarrett D'Amore curp->wlevel = MANDOCLEVEL_WARNING; 94195c635efSGarrett D'Amore } else if (0 == strcmp(arg, "tree")) 94295c635efSGarrett D'Amore curp->outtype = OUTT_TREE; 94395c635efSGarrett D'Amore else if (0 == strcmp(arg, "man")) 94495c635efSGarrett D'Amore curp->outtype = OUTT_MAN; 94595c635efSGarrett D'Amore else if (0 == strcmp(arg, "html")) 94695c635efSGarrett D'Amore curp->outtype = OUTT_HTML; 94795c635efSGarrett D'Amore else if (0 == strcmp(arg, "utf8")) 94895c635efSGarrett D'Amore curp->outtype = OUTT_UTF8; 94995c635efSGarrett D'Amore else if (0 == strcmp(arg, "locale")) 95095c635efSGarrett D'Amore curp->outtype = OUTT_LOCALE; 95195c635efSGarrett D'Amore else if (0 == strcmp(arg, "xhtml")) 952260e9a87SYuri Pankov curp->outtype = OUTT_HTML; 95395c635efSGarrett D'Amore else if (0 == strcmp(arg, "ps")) 95495c635efSGarrett D'Amore curp->outtype = OUTT_PS; 95595c635efSGarrett D'Amore else if (0 == strcmp(arg, "pdf")) 95695c635efSGarrett D'Amore curp->outtype = OUTT_PDF; 95795c635efSGarrett D'Amore else { 958*371584c2SYuri Pankov warnx("-T %s: Bad argument", arg); 959*371584c2SYuri Pankov return 0; 96095c635efSGarrett D'Amore } 96195c635efSGarrett D'Amore 962*371584c2SYuri Pankov return 1; 96395c635efSGarrett D'Amore } 96495c635efSGarrett D'Amore 96595c635efSGarrett D'Amore static int 96695c635efSGarrett D'Amore woptions(struct curparse *curp, char *arg) 96795c635efSGarrett D'Amore { 96895c635efSGarrett D'Amore char *v, *o; 969260e9a87SYuri Pankov const char *toks[7]; 97095c635efSGarrett D'Amore 97195c635efSGarrett D'Amore toks[0] = "stop"; 97295c635efSGarrett D'Amore toks[1] = "all"; 97395c635efSGarrett D'Amore toks[2] = "warning"; 97495c635efSGarrett D'Amore toks[3] = "error"; 975260e9a87SYuri Pankov toks[4] = "unsupp"; 976260e9a87SYuri Pankov toks[5] = "fatal"; 977260e9a87SYuri Pankov toks[6] = NULL; 97895c635efSGarrett D'Amore 97995c635efSGarrett D'Amore while (*arg) { 98095c635efSGarrett D'Amore o = arg; 98195c635efSGarrett D'Amore switch (getsubopt(&arg, UNCONST(toks), &v)) { 982260e9a87SYuri Pankov case 0: 98395c635efSGarrett D'Amore curp->wstop = 1; 98495c635efSGarrett D'Amore break; 985260e9a87SYuri Pankov case 1: 986260e9a87SYuri Pankov case 2: 98795c635efSGarrett D'Amore curp->wlevel = MANDOCLEVEL_WARNING; 98895c635efSGarrett D'Amore break; 989260e9a87SYuri Pankov case 3: 99095c635efSGarrett D'Amore curp->wlevel = MANDOCLEVEL_ERROR; 99195c635efSGarrett D'Amore break; 992260e9a87SYuri Pankov case 4: 993260e9a87SYuri Pankov curp->wlevel = MANDOCLEVEL_UNSUPP; 994260e9a87SYuri Pankov break; 995260e9a87SYuri Pankov case 5: 996260e9a87SYuri Pankov curp->wlevel = MANDOCLEVEL_BADARG; 99795c635efSGarrett D'Amore break; 99895c635efSGarrett D'Amore default: 999*371584c2SYuri Pankov warnx("-W %s: Bad argument", o); 1000*371584c2SYuri Pankov return 0; 100195c635efSGarrett D'Amore } 100295c635efSGarrett D'Amore } 100395c635efSGarrett D'Amore 1004*371584c2SYuri Pankov return 1; 100595c635efSGarrett D'Amore } 100695c635efSGarrett D'Amore 100795c635efSGarrett D'Amore static void 1008260e9a87SYuri Pankov mmsg(enum mandocerr t, enum mandoclevel lvl, 100995c635efSGarrett D'Amore const char *file, int line, int col, const char *msg) 101095c635efSGarrett D'Amore { 1011260e9a87SYuri Pankov const char *mparse_msg; 1012260e9a87SYuri Pankov 1013*371584c2SYuri Pankov fprintf(stderr, "%s: %s:", getprogname(), 1014*371584c2SYuri Pankov file == NULL ? "<stdin>" : file); 1015260e9a87SYuri Pankov 1016260e9a87SYuri Pankov if (line) 1017260e9a87SYuri Pankov fprintf(stderr, "%d:%d:", line, col + 1); 101895c635efSGarrett D'Amore 1019260e9a87SYuri Pankov fprintf(stderr, " %s", mparse_strlevel(lvl)); 1020260e9a87SYuri Pankov 1021260e9a87SYuri Pankov if (NULL != (mparse_msg = mparse_strerror(t))) 1022260e9a87SYuri Pankov fprintf(stderr, ": %s", mparse_msg); 102395c635efSGarrett D'Amore 102495c635efSGarrett D'Amore if (msg) 102595c635efSGarrett D'Amore fprintf(stderr, ": %s", msg); 102695c635efSGarrett D'Amore 102795c635efSGarrett D'Amore fputc('\n', stderr); 102895c635efSGarrett D'Amore } 1029260e9a87SYuri Pankov 1030260e9a87SYuri Pankov static pid_t 1031*371584c2SYuri Pankov spawn_pager(struct tag_files *tag_files) 1032260e9a87SYuri Pankov { 1033*371584c2SYuri Pankov const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ 1034260e9a87SYuri Pankov #define MAX_PAGER_ARGS 16 1035260e9a87SYuri Pankov char *argv[MAX_PAGER_ARGS]; 1036260e9a87SYuri Pankov const char *pager; 1037260e9a87SYuri Pankov char *cp; 1038*371584c2SYuri Pankov size_t cmdlen; 1039260e9a87SYuri Pankov int argc; 1040260e9a87SYuri Pankov pid_t pager_pid; 1041260e9a87SYuri Pankov 1042260e9a87SYuri Pankov pager = getenv("MANPAGER"); 1043260e9a87SYuri Pankov if (pager == NULL || *pager == '\0') 1044260e9a87SYuri Pankov pager = getenv("PAGER"); 1045260e9a87SYuri Pankov if (pager == NULL || *pager == '\0') 1046*371584c2SYuri Pankov pager = "more -s"; 1047260e9a87SYuri Pankov cp = mandoc_strdup(pager); 1048260e9a87SYuri Pankov 1049260e9a87SYuri Pankov /* 1050260e9a87SYuri Pankov * Parse the pager command into words. 1051260e9a87SYuri Pankov * Intentionally do not do anything fancy here. 1052260e9a87SYuri Pankov */ 1053260e9a87SYuri Pankov 1054260e9a87SYuri Pankov argc = 0; 1055*371584c2SYuri Pankov while (argc + 4 < MAX_PAGER_ARGS) { 1056260e9a87SYuri Pankov argv[argc++] = cp; 1057260e9a87SYuri Pankov cp = strchr(cp, ' '); 1058260e9a87SYuri Pankov if (cp == NULL) 1059260e9a87SYuri Pankov break; 1060260e9a87SYuri Pankov *cp++ = '\0'; 1061260e9a87SYuri Pankov while (*cp == ' ') 1062260e9a87SYuri Pankov cp++; 1063260e9a87SYuri Pankov if (*cp == '\0') 1064260e9a87SYuri Pankov break; 1065260e9a87SYuri Pankov } 1066*371584c2SYuri Pankov 1067*371584c2SYuri Pankov /* For less(1), use the tag file. */ 1068*371584c2SYuri Pankov 1069*371584c2SYuri Pankov if ((cmdlen = strlen(argv[0])) >= 4) { 1070*371584c2SYuri Pankov cp = argv[0] + cmdlen - 4; 1071*371584c2SYuri Pankov if (strcmp(cp, "less") == 0) { 1072*371584c2SYuri Pankov argv[argc++] = mandoc_strdup("-T"); 1073*371584c2SYuri Pankov argv[argc++] = tag_files->tfn; 1074*371584c2SYuri Pankov } 1075*371584c2SYuri Pankov } 1076*371584c2SYuri Pankov argv[argc++] = tag_files->ofn; 1077260e9a87SYuri Pankov argv[argc] = NULL; 1078260e9a87SYuri Pankov 1079*371584c2SYuri Pankov switch (pager_pid = fork()) { 1080*371584c2SYuri Pankov case -1: 1081*371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "fork"); 1082*371584c2SYuri Pankov case 0: 1083*371584c2SYuri Pankov break; 1084*371584c2SYuri Pankov default: 1085*371584c2SYuri Pankov (void)setpgid(pager_pid, 0); 1086*371584c2SYuri Pankov (void)tcsetpgrp(STDIN_FILENO, pager_pid); 1087*371584c2SYuri Pankov #if HAVE_PLEDGE 1088*371584c2SYuri Pankov if (pledge("stdio rpath tmppath tty proc", NULL) == -1) 1089*371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 1090*371584c2SYuri Pankov #endif 1091*371584c2SYuri Pankov tag_files->pager_pid = pager_pid; 1092*371584c2SYuri Pankov return pager_pid; 1093*371584c2SYuri Pankov } 1094*371584c2SYuri Pankov 1095*371584c2SYuri Pankov /* The child process becomes the pager. */ 1096*371584c2SYuri Pankov 1097*371584c2SYuri Pankov if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) 1098*371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pager stdout"); 1099*371584c2SYuri Pankov close(tag_files->ofd); 1100*371584c2SYuri Pankov close(tag_files->tfd); 1101*371584c2SYuri Pankov 1102*371584c2SYuri Pankov /* Do not start the pager before controlling the terminal. */ 1103*371584c2SYuri Pankov 1104*371584c2SYuri Pankov while (tcgetpgrp(STDIN_FILENO) != getpid()) 1105*371584c2SYuri Pankov nanosleep(&timeout, NULL); 1106260e9a87SYuri Pankov 1107260e9a87SYuri Pankov execvp(argv[0], argv); 1108*371584c2SYuri Pankov err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]); 1109260e9a87SYuri Pankov } 1110