1*6d38604fSBaptiste Daroussin /* $Id: main.c,v 1.358 2021/09/04 22:38:46 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 3*6d38604fSBaptiste Daroussin * Copyright (c) 2010-2012, 2014-2021 Ingo Schwarze <schwarze@openbsd.org> 461d06d6bSBaptiste Daroussin * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 561d06d6bSBaptiste Daroussin * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> 661d06d6bSBaptiste Daroussin * 761d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 861d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 961d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 1061d06d6bSBaptiste Daroussin * 1161d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1261d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1361d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1461d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1561d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1661d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1761d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18*6d38604fSBaptiste Daroussin * 19*6d38604fSBaptiste Daroussin * Main program for mandoc(1), man(1), apropos(1), whatis(1), and help(1). 2061d06d6bSBaptiste Daroussin */ 2161d06d6bSBaptiste Daroussin #include "config.h" 2261d06d6bSBaptiste Daroussin 2361d06d6bSBaptiste Daroussin #include <sys/types.h> 2461d06d6bSBaptiste Daroussin #include <sys/ioctl.h> 2561d06d6bSBaptiste Daroussin #include <sys/param.h> /* MACHINE */ 2645a5aec3SBaptiste Daroussin #include <sys/stat.h> 2761d06d6bSBaptiste Daroussin #include <sys/wait.h> 2861d06d6bSBaptiste Daroussin 2961d06d6bSBaptiste Daroussin #include <assert.h> 3061d06d6bSBaptiste Daroussin #include <ctype.h> 3161d06d6bSBaptiste Daroussin #if HAVE_ERR 3261d06d6bSBaptiste Daroussin #include <err.h> 3361d06d6bSBaptiste Daroussin #endif 3461d06d6bSBaptiste Daroussin #include <errno.h> 3561d06d6bSBaptiste Daroussin #include <fcntl.h> 3661d06d6bSBaptiste Daroussin #include <glob.h> 37*6d38604fSBaptiste Daroussin #include <limits.h> 3861d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT 3961d06d6bSBaptiste Daroussin #include <sandbox.h> 4061d06d6bSBaptiste Daroussin #endif 4161d06d6bSBaptiste Daroussin #include <signal.h> 4261d06d6bSBaptiste Daroussin #include <stdio.h> 4361d06d6bSBaptiste Daroussin #include <stdint.h> 4461d06d6bSBaptiste Daroussin #include <stdlib.h> 4561d06d6bSBaptiste Daroussin #include <string.h> 4661d06d6bSBaptiste Daroussin #include <termios.h> 4761d06d6bSBaptiste Daroussin #include <time.h> 4861d06d6bSBaptiste Daroussin #include <unistd.h> 4961d06d6bSBaptiste Daroussin 5061d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 5161d06d6bSBaptiste Daroussin #include "mandoc.h" 5261d06d6bSBaptiste Daroussin #include "mandoc_xr.h" 5361d06d6bSBaptiste Daroussin #include "roff.h" 5461d06d6bSBaptiste Daroussin #include "mdoc.h" 5561d06d6bSBaptiste Daroussin #include "man.h" 567295610fSBaptiste Daroussin #include "mandoc_parse.h" 5761d06d6bSBaptiste Daroussin #include "tag.h" 58*6d38604fSBaptiste Daroussin #include "term_tag.h" 5961d06d6bSBaptiste Daroussin #include "main.h" 6061d06d6bSBaptiste Daroussin #include "manconf.h" 6161d06d6bSBaptiste Daroussin #include "mansearch.h" 6261d06d6bSBaptiste Daroussin 6361d06d6bSBaptiste Daroussin enum outmode { 6461d06d6bSBaptiste Daroussin OUTMODE_DEF = 0, 6561d06d6bSBaptiste Daroussin OUTMODE_FLN, 6661d06d6bSBaptiste Daroussin OUTMODE_LST, 6761d06d6bSBaptiste Daroussin OUTMODE_ALL, 6861d06d6bSBaptiste Daroussin OUTMODE_ONE 6961d06d6bSBaptiste Daroussin }; 7061d06d6bSBaptiste Daroussin 7161d06d6bSBaptiste Daroussin enum outt { 7261d06d6bSBaptiste Daroussin OUTT_ASCII = 0, /* -Tascii */ 7361d06d6bSBaptiste Daroussin OUTT_LOCALE, /* -Tlocale */ 7461d06d6bSBaptiste Daroussin OUTT_UTF8, /* -Tutf8 */ 7561d06d6bSBaptiste Daroussin OUTT_TREE, /* -Ttree */ 7661d06d6bSBaptiste Daroussin OUTT_MAN, /* -Tman */ 7761d06d6bSBaptiste Daroussin OUTT_HTML, /* -Thtml */ 7861d06d6bSBaptiste Daroussin OUTT_MARKDOWN, /* -Tmarkdown */ 7961d06d6bSBaptiste Daroussin OUTT_LINT, /* -Tlint */ 8061d06d6bSBaptiste Daroussin OUTT_PS, /* -Tps */ 8161d06d6bSBaptiste Daroussin OUTT_PDF /* -Tpdf */ 8261d06d6bSBaptiste Daroussin }; 8361d06d6bSBaptiste Daroussin 84*6d38604fSBaptiste Daroussin struct outstate { 85*6d38604fSBaptiste Daroussin struct tag_files *tag_files; /* Tagging state variables. */ 8661d06d6bSBaptiste Daroussin void *outdata; /* data for output */ 87*6d38604fSBaptiste Daroussin int use_pager; 8861d06d6bSBaptiste Daroussin int wstop; /* stop after a file with a warning */ 89*6d38604fSBaptiste Daroussin int had_output; /* Some output was generated. */ 9061d06d6bSBaptiste Daroussin enum outt outtype; /* which output to use */ 9161d06d6bSBaptiste Daroussin }; 9261d06d6bSBaptiste Daroussin 9361d06d6bSBaptiste Daroussin 9461d06d6bSBaptiste Daroussin int mandocdb(int, char *[]); 9561d06d6bSBaptiste Daroussin 96*6d38604fSBaptiste Daroussin static void check_xr(struct manpaths *); 97*6d38604fSBaptiste Daroussin static void fs_append(char **, size_t, int, 98*6d38604fSBaptiste Daroussin size_t, const char *, enum form, 99*6d38604fSBaptiste Daroussin struct manpage **, size_t *); 100*6d38604fSBaptiste Daroussin static int fs_lookup(const struct manpaths *, size_t, 101*6d38604fSBaptiste Daroussin const char *, const char *, const char *, 10261d06d6bSBaptiste Daroussin struct manpage **, size_t *); 10361d06d6bSBaptiste Daroussin static int fs_search(const struct mansearch *, 104*6d38604fSBaptiste Daroussin const struct manpaths *, const char *, 10561d06d6bSBaptiste Daroussin struct manpage **, size_t *); 106*6d38604fSBaptiste Daroussin static void glob_esc(char **, const char *, const char *); 107*6d38604fSBaptiste Daroussin static void outdata_alloc(struct outstate *, struct manoutput *); 108*6d38604fSBaptiste Daroussin static void parse(struct mparse *, int, const char *, 109*6d38604fSBaptiste Daroussin struct outstate *, struct manconf *); 11045a5aec3SBaptiste Daroussin static void passthrough(int, int); 111*6d38604fSBaptiste Daroussin static void process_onefile(struct mparse *, struct manpage *, 112*6d38604fSBaptiste Daroussin int, struct outstate *, struct manconf *); 113*6d38604fSBaptiste Daroussin static void run_pager(struct outstate *, char *); 114*6d38604fSBaptiste Daroussin static pid_t spawn_pager(struct outstate *, char *); 11561d06d6bSBaptiste Daroussin static void usage(enum argmode) __attribute__((__noreturn__)); 116*6d38604fSBaptiste Daroussin static int woptions(char *, enum mandoc_os *, int *); 11761d06d6bSBaptiste Daroussin 11861d06d6bSBaptiste Daroussin static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; 11961d06d6bSBaptiste Daroussin static char help_arg[] = "help"; 12061d06d6bSBaptiste Daroussin static char *help_argv[] = {help_arg, NULL}; 12161d06d6bSBaptiste Daroussin 12261d06d6bSBaptiste Daroussin 12361d06d6bSBaptiste Daroussin int 12461d06d6bSBaptiste Daroussin main(int argc, char *argv[]) 12561d06d6bSBaptiste Daroussin { 126*6d38604fSBaptiste Daroussin struct manconf conf; /* Manpaths and output options. */ 127*6d38604fSBaptiste Daroussin struct outstate outst; /* Output state. */ 128*6d38604fSBaptiste Daroussin struct winsize ws; /* Result of ioctl(TIOCGWINSZ). */ 129*6d38604fSBaptiste Daroussin struct mansearch search; /* Search options. */ 130*6d38604fSBaptiste Daroussin struct manpage *res; /* Complete list of search results. */ 131*6d38604fSBaptiste Daroussin struct manpage *resn; /* Search results for one name. */ 132*6d38604fSBaptiste Daroussin struct mparse *mp; /* Opaque parser object. */ 133*6d38604fSBaptiste Daroussin const char *conf_file; /* -C: alternate config file. */ 134*6d38604fSBaptiste Daroussin const char *os_s; /* -I: Operating system for display. */ 135*6d38604fSBaptiste Daroussin const char *progname, *sec, *ep; 136*6d38604fSBaptiste Daroussin char *defpaths; /* -M: override manpaths. */ 137*6d38604fSBaptiste Daroussin char *auxpaths; /* -m: additional manpaths. */ 138*6d38604fSBaptiste Daroussin char *oarg; /* -O: output option string. */ 139*6d38604fSBaptiste Daroussin char *tagarg; /* -O tag: default value. */ 14061d06d6bSBaptiste Daroussin unsigned char *uc; 141*6d38604fSBaptiste Daroussin size_t ressz; /* Number of elements in res[]. */ 142*6d38604fSBaptiste Daroussin size_t resnsz; /* Number of elements in resn[]. */ 143*6d38604fSBaptiste Daroussin size_t i, ib, ssz; 144*6d38604fSBaptiste Daroussin int options; /* Parser options. */ 145*6d38604fSBaptiste Daroussin int show_usage; /* Invalid argument: give up. */ 14661d06d6bSBaptiste Daroussin int prio, best_prio; 147*6d38604fSBaptiste Daroussin int startdir; 14861d06d6bSBaptiste Daroussin int c; 149*6d38604fSBaptiste Daroussin enum mandoc_os os_e; /* Check base system conventions. */ 150*6d38604fSBaptiste Daroussin enum outmode outmode; /* According to command line. */ 15161d06d6bSBaptiste Daroussin 15261d06d6bSBaptiste Daroussin #if HAVE_PROGNAME 15361d06d6bSBaptiste Daroussin progname = getprogname(); 15461d06d6bSBaptiste Daroussin #else 15561d06d6bSBaptiste Daroussin if (argc < 1) 15661d06d6bSBaptiste Daroussin progname = mandoc_strdup("mandoc"); 15761d06d6bSBaptiste Daroussin else if ((progname = strrchr(argv[0], '/')) == NULL) 15861d06d6bSBaptiste Daroussin progname = argv[0]; 15961d06d6bSBaptiste Daroussin else 16061d06d6bSBaptiste Daroussin ++progname; 16161d06d6bSBaptiste Daroussin setprogname(progname); 16261d06d6bSBaptiste Daroussin #endif 16361d06d6bSBaptiste Daroussin 1647295610fSBaptiste Daroussin mandoc_msg_setoutfile(stderr); 16561d06d6bSBaptiste Daroussin if (strncmp(progname, "mandocdb", 8) == 0 || 16661d06d6bSBaptiste Daroussin strcmp(progname, BINM_MAKEWHATIS) == 0) 16761d06d6bSBaptiste Daroussin return mandocdb(argc, argv); 16861d06d6bSBaptiste Daroussin 16961d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 170*6d38604fSBaptiste Daroussin if (pledge("stdio rpath wpath cpath tmppath tty proc exec", NULL) == -1) { 17145a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); 17245a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 17345a5aec3SBaptiste Daroussin } 17461d06d6bSBaptiste Daroussin #endif 17561d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT 17661d06d6bSBaptiste Daroussin if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) 17761d06d6bSBaptiste Daroussin errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); 17861d06d6bSBaptiste Daroussin #endif 17961d06d6bSBaptiste Daroussin 18061d06d6bSBaptiste Daroussin /* Search options. */ 18161d06d6bSBaptiste Daroussin 18261d06d6bSBaptiste Daroussin memset(&conf, 0, sizeof(conf)); 183*6d38604fSBaptiste Daroussin conf_file = NULL; 184*6d38604fSBaptiste Daroussin defpaths = auxpaths = NULL; 18561d06d6bSBaptiste Daroussin 18661d06d6bSBaptiste Daroussin memset(&search, 0, sizeof(struct mansearch)); 18761d06d6bSBaptiste Daroussin search.outkey = "Nd"; 18861d06d6bSBaptiste Daroussin oarg = NULL; 18961d06d6bSBaptiste Daroussin 19061d06d6bSBaptiste Daroussin if (strcmp(progname, BINM_MAN) == 0) 19161d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 19261d06d6bSBaptiste Daroussin else if (strcmp(progname, BINM_APROPOS) == 0) 19361d06d6bSBaptiste Daroussin search.argmode = ARG_EXPR; 19461d06d6bSBaptiste Daroussin else if (strcmp(progname, BINM_WHATIS) == 0) 19561d06d6bSBaptiste Daroussin search.argmode = ARG_WORD; 19661d06d6bSBaptiste Daroussin else if (strncmp(progname, "help", 4) == 0) 19761d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 19861d06d6bSBaptiste Daroussin else 19961d06d6bSBaptiste Daroussin search.argmode = ARG_FILE; 20061d06d6bSBaptiste Daroussin 201*6d38604fSBaptiste Daroussin /* Parser options. */ 20261d06d6bSBaptiste Daroussin 20361d06d6bSBaptiste Daroussin options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; 204*6d38604fSBaptiste Daroussin os_e = MANDOC_OS_OTHER; 205*6d38604fSBaptiste Daroussin os_s = NULL; 20661d06d6bSBaptiste Daroussin 207*6d38604fSBaptiste Daroussin /* Formatter options. */ 208*6d38604fSBaptiste Daroussin 209*6d38604fSBaptiste Daroussin memset(&outst, 0, sizeof(outst)); 210*6d38604fSBaptiste Daroussin outst.tag_files = NULL; 211*6d38604fSBaptiste Daroussin outst.outtype = OUTT_LOCALE; 212*6d38604fSBaptiste Daroussin outst.use_pager = 1; 213*6d38604fSBaptiste Daroussin 21461d06d6bSBaptiste Daroussin show_usage = 0; 21561d06d6bSBaptiste Daroussin outmode = OUTMODE_DEF; 21661d06d6bSBaptiste Daroussin 21761d06d6bSBaptiste Daroussin while ((c = getopt(argc, argv, 21861d06d6bSBaptiste Daroussin "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) { 21961d06d6bSBaptiste Daroussin if (c == 'i' && search.argmode == ARG_EXPR) { 22061d06d6bSBaptiste Daroussin optind--; 22161d06d6bSBaptiste Daroussin break; 22261d06d6bSBaptiste Daroussin } 22361d06d6bSBaptiste Daroussin switch (c) { 22461d06d6bSBaptiste Daroussin case 'a': 22561d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 22661d06d6bSBaptiste Daroussin break; 22761d06d6bSBaptiste Daroussin case 'C': 22861d06d6bSBaptiste Daroussin conf_file = optarg; 22961d06d6bSBaptiste Daroussin break; 23061d06d6bSBaptiste Daroussin case 'c': 231*6d38604fSBaptiste Daroussin outst.use_pager = 0; 23261d06d6bSBaptiste Daroussin break; 23361d06d6bSBaptiste Daroussin case 'f': 23461d06d6bSBaptiste Daroussin search.argmode = ARG_WORD; 23561d06d6bSBaptiste Daroussin break; 23661d06d6bSBaptiste Daroussin case 'h': 23761d06d6bSBaptiste Daroussin conf.output.synopsisonly = 1; 238*6d38604fSBaptiste Daroussin outst.use_pager = 0; 23961d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 24061d06d6bSBaptiste Daroussin break; 24161d06d6bSBaptiste Daroussin case 'I': 24245a5aec3SBaptiste Daroussin if (strncmp(optarg, "os=", 3) != 0) { 24345a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, 24445a5aec3SBaptiste Daroussin "-I %s", optarg); 24545a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 24661d06d6bSBaptiste Daroussin } 247*6d38604fSBaptiste Daroussin if (os_s != NULL) { 24845a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_DUPE, 0, 0, 24945a5aec3SBaptiste Daroussin "-I %s", optarg); 25045a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 25161d06d6bSBaptiste Daroussin } 252*6d38604fSBaptiste Daroussin os_s = optarg + 3; 25361d06d6bSBaptiste Daroussin break; 25461d06d6bSBaptiste Daroussin case 'K': 25545a5aec3SBaptiste Daroussin options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); 25645a5aec3SBaptiste Daroussin if (strcmp(optarg, "utf-8") == 0) 25745a5aec3SBaptiste Daroussin options |= MPARSE_UTF8; 25845a5aec3SBaptiste Daroussin else if (strcmp(optarg, "iso-8859-1") == 0) 25945a5aec3SBaptiste Daroussin options |= MPARSE_LATIN1; 26045a5aec3SBaptiste Daroussin else if (strcmp(optarg, "us-ascii") != 0) { 26145a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, 26245a5aec3SBaptiste Daroussin "-K %s", optarg); 26345a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 26445a5aec3SBaptiste Daroussin } 26561d06d6bSBaptiste Daroussin break; 26661d06d6bSBaptiste Daroussin case 'k': 26761d06d6bSBaptiste Daroussin search.argmode = ARG_EXPR; 26861d06d6bSBaptiste Daroussin break; 26961d06d6bSBaptiste Daroussin case 'l': 27061d06d6bSBaptiste Daroussin search.argmode = ARG_FILE; 27161d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 27261d06d6bSBaptiste Daroussin break; 27361d06d6bSBaptiste Daroussin case 'M': 274a10034cbSYuri Pankov #ifdef __FreeBSD__ 275a10034cbSYuri Pankov defpaths = strdup(optarg); 276a10034cbSYuri Pankov if (defpaths == NULL) 277a10034cbSYuri Pankov err(1, "strdup"); 278a10034cbSYuri Pankov #else 27961d06d6bSBaptiste Daroussin defpaths = optarg; 280a10034cbSYuri Pankov #endif 28161d06d6bSBaptiste Daroussin break; 28261d06d6bSBaptiste Daroussin case 'm': 28361d06d6bSBaptiste Daroussin auxpaths = optarg; 28461d06d6bSBaptiste Daroussin break; 28561d06d6bSBaptiste Daroussin case 'O': 28661d06d6bSBaptiste Daroussin oarg = optarg; 28761d06d6bSBaptiste Daroussin break; 28861d06d6bSBaptiste Daroussin case 'S': 28961d06d6bSBaptiste Daroussin search.arch = optarg; 29061d06d6bSBaptiste Daroussin break; 29161d06d6bSBaptiste Daroussin case 's': 29261d06d6bSBaptiste Daroussin search.sec = optarg; 29361d06d6bSBaptiste Daroussin break; 29461d06d6bSBaptiste Daroussin case 'T': 29545a5aec3SBaptiste Daroussin if (strcmp(optarg, "ascii") == 0) 296*6d38604fSBaptiste Daroussin outst.outtype = OUTT_ASCII; 29745a5aec3SBaptiste Daroussin else if (strcmp(optarg, "lint") == 0) { 298*6d38604fSBaptiste Daroussin outst.outtype = OUTT_LINT; 29945a5aec3SBaptiste Daroussin mandoc_msg_setoutfile(stdout); 30045a5aec3SBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 30145a5aec3SBaptiste Daroussin } else if (strcmp(optarg, "tree") == 0) 302*6d38604fSBaptiste Daroussin outst.outtype = OUTT_TREE; 30345a5aec3SBaptiste Daroussin else if (strcmp(optarg, "man") == 0) 304*6d38604fSBaptiste Daroussin outst.outtype = OUTT_MAN; 30545a5aec3SBaptiste Daroussin else if (strcmp(optarg, "html") == 0) 306*6d38604fSBaptiste Daroussin outst.outtype = OUTT_HTML; 30745a5aec3SBaptiste Daroussin else if (strcmp(optarg, "markdown") == 0) 308*6d38604fSBaptiste Daroussin outst.outtype = OUTT_MARKDOWN; 30945a5aec3SBaptiste Daroussin else if (strcmp(optarg, "utf8") == 0) 310*6d38604fSBaptiste Daroussin outst.outtype = OUTT_UTF8; 31145a5aec3SBaptiste Daroussin else if (strcmp(optarg, "locale") == 0) 312*6d38604fSBaptiste Daroussin outst.outtype = OUTT_LOCALE; 31345a5aec3SBaptiste Daroussin else if (strcmp(optarg, "ps") == 0) 314*6d38604fSBaptiste Daroussin outst.outtype = OUTT_PS; 31545a5aec3SBaptiste Daroussin else if (strcmp(optarg, "pdf") == 0) 316*6d38604fSBaptiste Daroussin outst.outtype = OUTT_PDF; 31745a5aec3SBaptiste Daroussin else { 31845a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, 31945a5aec3SBaptiste Daroussin "-T %s", optarg); 32045a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 32145a5aec3SBaptiste Daroussin } 32261d06d6bSBaptiste Daroussin break; 32361d06d6bSBaptiste Daroussin case 'W': 324*6d38604fSBaptiste Daroussin if (woptions(optarg, &os_e, &outst.wstop) == -1) 32545a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 32661d06d6bSBaptiste Daroussin break; 32761d06d6bSBaptiste Daroussin case 'w': 32861d06d6bSBaptiste Daroussin outmode = OUTMODE_FLN; 32961d06d6bSBaptiste Daroussin break; 33061d06d6bSBaptiste Daroussin default: 33161d06d6bSBaptiste Daroussin show_usage = 1; 33261d06d6bSBaptiste Daroussin break; 33361d06d6bSBaptiste Daroussin } 33461d06d6bSBaptiste Daroussin } 33561d06d6bSBaptiste Daroussin 33661d06d6bSBaptiste Daroussin if (show_usage) 33761d06d6bSBaptiste Daroussin usage(search.argmode); 33861d06d6bSBaptiste Daroussin 33961d06d6bSBaptiste Daroussin /* Postprocess options. */ 34061d06d6bSBaptiste Daroussin 341*6d38604fSBaptiste Daroussin switch (outmode) { 342*6d38604fSBaptiste Daroussin case OUTMODE_DEF: 34361d06d6bSBaptiste Daroussin switch (search.argmode) { 34461d06d6bSBaptiste Daroussin case ARG_FILE: 34561d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 346*6d38604fSBaptiste Daroussin outst.use_pager = 0; 34761d06d6bSBaptiste Daroussin break; 34861d06d6bSBaptiste Daroussin case ARG_NAME: 34961d06d6bSBaptiste Daroussin outmode = OUTMODE_ONE; 35061d06d6bSBaptiste Daroussin break; 35161d06d6bSBaptiste Daroussin default: 35261d06d6bSBaptiste Daroussin outmode = OUTMODE_LST; 35361d06d6bSBaptiste Daroussin break; 35461d06d6bSBaptiste Daroussin } 355*6d38604fSBaptiste Daroussin break; 356*6d38604fSBaptiste Daroussin case OUTMODE_FLN: 357*6d38604fSBaptiste Daroussin if (search.argmode == ARG_FILE) 358*6d38604fSBaptiste Daroussin outmode = OUTMODE_ALL; 359*6d38604fSBaptiste Daroussin break; 360*6d38604fSBaptiste Daroussin case OUTMODE_ALL: 361*6d38604fSBaptiste Daroussin break; 362*6d38604fSBaptiste Daroussin case OUTMODE_LST: 363*6d38604fSBaptiste Daroussin case OUTMODE_ONE: 364*6d38604fSBaptiste Daroussin abort(); 36561d06d6bSBaptiste Daroussin } 36661d06d6bSBaptiste Daroussin 36761d06d6bSBaptiste Daroussin if (oarg != NULL) { 36861d06d6bSBaptiste Daroussin if (outmode == OUTMODE_LST) 36961d06d6bSBaptiste Daroussin search.outkey = oarg; 37061d06d6bSBaptiste Daroussin else { 37161d06d6bSBaptiste Daroussin while (oarg != NULL) { 37261d06d6bSBaptiste Daroussin if (manconf_output(&conf.output, 37345a5aec3SBaptiste Daroussin strsep(&oarg, ","), 0) == -1) 37445a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 37561d06d6bSBaptiste Daroussin } 37661d06d6bSBaptiste Daroussin } 37761d06d6bSBaptiste Daroussin } 37861d06d6bSBaptiste Daroussin 379*6d38604fSBaptiste Daroussin if (outst.outtype != OUTT_TREE || conf.output.noval == 0) 3807295610fSBaptiste Daroussin options |= MPARSE_VALIDATE; 3817295610fSBaptiste Daroussin 38261d06d6bSBaptiste Daroussin if (outmode == OUTMODE_FLN || 38361d06d6bSBaptiste Daroussin outmode == OUTMODE_LST || 384*6d38604fSBaptiste Daroussin (conf.output.outfilename == NULL && 385*6d38604fSBaptiste Daroussin conf.output.tagfilename == NULL && 386*6d38604fSBaptiste Daroussin isatty(STDOUT_FILENO) == 0)) 387*6d38604fSBaptiste Daroussin outst.use_pager = 0; 38861d06d6bSBaptiste Daroussin 389*6d38604fSBaptiste Daroussin if (outst.use_pager && 39061d06d6bSBaptiste Daroussin (conf.output.width == 0 || conf.output.indent == 0) && 39161d06d6bSBaptiste Daroussin ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && 39261d06d6bSBaptiste Daroussin ws.ws_col > 1) { 39361d06d6bSBaptiste Daroussin if (conf.output.width == 0 && ws.ws_col < 79) 39461d06d6bSBaptiste Daroussin conf.output.width = ws.ws_col - 1; 39561d06d6bSBaptiste Daroussin if (conf.output.indent == 0 && ws.ws_col < 66) 39661d06d6bSBaptiste Daroussin conf.output.indent = 3; 39761d06d6bSBaptiste Daroussin } 39861d06d6bSBaptiste Daroussin 39961d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 400*6d38604fSBaptiste Daroussin if (outst.use_pager == 0) 401*6d38604fSBaptiste Daroussin c = pledge("stdio rpath", NULL); 402*6d38604fSBaptiste Daroussin else if (conf.output.outfilename != NULL || 403*6d38604fSBaptiste Daroussin conf.output.tagfilename != NULL) 404*6d38604fSBaptiste Daroussin c = pledge("stdio rpath wpath cpath", NULL); 405*6d38604fSBaptiste Daroussin else 406*6d38604fSBaptiste Daroussin c = pledge("stdio rpath tmppath tty proc exec", NULL); 407*6d38604fSBaptiste Daroussin if (c == -1) { 408*6d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); 40945a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 41045a5aec3SBaptiste Daroussin } 41161d06d6bSBaptiste Daroussin #endif 41261d06d6bSBaptiste Daroussin 41361d06d6bSBaptiste Daroussin /* Parse arguments. */ 41461d06d6bSBaptiste Daroussin 41561d06d6bSBaptiste Daroussin if (argc > 0) { 41661d06d6bSBaptiste Daroussin argc -= optind; 41761d06d6bSBaptiste Daroussin argv += optind; 41861d06d6bSBaptiste Daroussin } 41961d06d6bSBaptiste Daroussin 42061d06d6bSBaptiste Daroussin /* 421*6d38604fSBaptiste Daroussin * Quirks for help(1) and man(1), 422*6d38604fSBaptiste Daroussin * in particular for a section argument without -s. 42361d06d6bSBaptiste Daroussin */ 42461d06d6bSBaptiste Daroussin 42561d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME) { 42661d06d6bSBaptiste Daroussin if (*progname == 'h') { 42761d06d6bSBaptiste Daroussin if (argc == 0) { 42861d06d6bSBaptiste Daroussin argv = help_argv; 42961d06d6bSBaptiste Daroussin argc = 1; 43061d06d6bSBaptiste Daroussin } 43161d06d6bSBaptiste Daroussin } else if (argc > 1 && 43261d06d6bSBaptiste Daroussin ((uc = (unsigned char *)argv[0]) != NULL) && 43361d06d6bSBaptiste Daroussin ((isdigit(uc[0]) && (uc[1] == '\0' || 43445a5aec3SBaptiste Daroussin isalpha(uc[1]))) || 43561d06d6bSBaptiste Daroussin (uc[0] == 'n' && uc[1] == '\0'))) { 43661d06d6bSBaptiste Daroussin search.sec = (char *)uc; 43761d06d6bSBaptiste Daroussin argv++; 43861d06d6bSBaptiste Daroussin argc--; 43961d06d6bSBaptiste Daroussin } 44061d06d6bSBaptiste Daroussin if (search.arch == NULL) 44161d06d6bSBaptiste Daroussin search.arch = getenv("MACHINE"); 44261d06d6bSBaptiste Daroussin #ifdef MACHINE 44361d06d6bSBaptiste Daroussin if (search.arch == NULL) 44461d06d6bSBaptiste Daroussin search.arch = MACHINE; 44561d06d6bSBaptiste Daroussin #endif 446*6d38604fSBaptiste Daroussin if (outmode == OUTMODE_ONE) 447*6d38604fSBaptiste Daroussin search.firstmatch = 1; 44861d06d6bSBaptiste Daroussin } 44961d06d6bSBaptiste Daroussin 4507295610fSBaptiste Daroussin /* 4517295610fSBaptiste Daroussin * Use the first argument for -O tag in addition to 4527295610fSBaptiste Daroussin * using it as a search term for man(1) or apropos(1). 4537295610fSBaptiste Daroussin */ 4547295610fSBaptiste Daroussin 4557295610fSBaptiste Daroussin if (conf.output.tag != NULL && *conf.output.tag == '\0') { 4567295610fSBaptiste Daroussin tagarg = argc > 0 && search.argmode == ARG_EXPR ? 4577295610fSBaptiste Daroussin strchr(*argv, '=') : NULL; 4587295610fSBaptiste Daroussin conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; 4597295610fSBaptiste Daroussin } 46061d06d6bSBaptiste Daroussin 461*6d38604fSBaptiste Daroussin if (search.argmode != ARG_FILE || 462*6d38604fSBaptiste Daroussin mandoc_msg_getmin() == MANDOCERR_STYLE) 463a10034cbSYuri Pankov #ifdef __FreeBSD__ 464*6d38604fSBaptiste Daroussin { 465a10034cbSYuri Pankov /* 466a10034cbSYuri Pankov * Use manpath(1) to populate defpaths if -M is not specified. 467a10034cbSYuri Pankov * Don't treat any failures as fatal. 468a10034cbSYuri Pankov */ 469a10034cbSYuri Pankov if (defpaths == NULL) { 470a10034cbSYuri Pankov FILE *fp; 471a10034cbSYuri Pankov size_t linecap = 0; 472a10034cbSYuri Pankov ssize_t linelen; 473a10034cbSYuri Pankov 474a10034cbSYuri Pankov if ((fp = popen("/usr/bin/manpath -q", "r")) != NULL) { 475a10034cbSYuri Pankov if ((linelen = getline(&defpaths, 476a10034cbSYuri Pankov &linecap, fp)) > 0) { 477a10034cbSYuri Pankov /* Strip trailing newline */ 478a10034cbSYuri Pankov defpaths[linelen - 1] = '\0'; 479a10034cbSYuri Pankov } 480a10034cbSYuri Pankov pclose(fp); 481a10034cbSYuri Pankov } 482a10034cbSYuri Pankov } 483a10034cbSYuri Pankov #endif 484*6d38604fSBaptiste Daroussin /* Read the configuration file. */ 48561d06d6bSBaptiste Daroussin manconf_parse(&conf, conf_file, defpaths, auxpaths); 486a10034cbSYuri Pankov #ifdef __FreeBSD__ 487a10034cbSYuri Pankov free(defpaths); 488*6d38604fSBaptiste Daroussin } 489a10034cbSYuri Pankov #endif 490a10034cbSYuri Pankov 491*6d38604fSBaptiste Daroussin /* man(1): Resolve each name individually. */ 49261d06d6bSBaptiste Daroussin 49361d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME) { 494*6d38604fSBaptiste Daroussin if (argc < 1) { 495*6d38604fSBaptiste Daroussin if (outmode != OUTMODE_FLN) 496*6d38604fSBaptiste Daroussin usage(ARG_NAME); 497*6d38604fSBaptiste Daroussin if (conf.manpath.sz == 0) { 498*6d38604fSBaptiste Daroussin warnx("The manpath is empty."); 499*6d38604fSBaptiste Daroussin mandoc_msg_setrc(MANDOCLEVEL_BADARG); 500*6d38604fSBaptiste Daroussin } else { 501*6d38604fSBaptiste Daroussin for (i = 0; i + 1 < conf.manpath.sz; i++) 502*6d38604fSBaptiste Daroussin printf("%s:", conf.manpath.paths[i]); 503*6d38604fSBaptiste Daroussin printf("%s\n", conf.manpath.paths[i]); 504*6d38604fSBaptiste Daroussin } 505*6d38604fSBaptiste Daroussin manconf_free(&conf); 506*6d38604fSBaptiste Daroussin return (int)mandoc_msg_getrc(); 507*6d38604fSBaptiste Daroussin } 508*6d38604fSBaptiste Daroussin for (res = NULL, ressz = 0; argc > 0; argc--, argv++) { 509*6d38604fSBaptiste Daroussin (void)mansearch(&search, &conf.manpath, 510*6d38604fSBaptiste Daroussin 1, argv, &resn, &resnsz); 511*6d38604fSBaptiste Daroussin if (resnsz == 0) 512*6d38604fSBaptiste Daroussin (void)fs_search(&search, &conf.manpath, 513*6d38604fSBaptiste Daroussin *argv, &resn, &resnsz); 514*6d38604fSBaptiste Daroussin if (resnsz == 0 && strchr(*argv, '/') == NULL) { 515*6d38604fSBaptiste Daroussin if (search.arch != NULL && 516*6d38604fSBaptiste Daroussin arch_valid(search.arch, OSENUM) == 0) 517*6d38604fSBaptiste Daroussin warnx("Unknown architecture \"%s\".", 518*6d38604fSBaptiste Daroussin search.arch); 519*6d38604fSBaptiste Daroussin else if (search.sec != NULL) 520*6d38604fSBaptiste Daroussin warnx("No entry for %s in " 521*6d38604fSBaptiste Daroussin "section %s of the manual.", 522*6d38604fSBaptiste Daroussin *argv, search.sec); 523*6d38604fSBaptiste Daroussin else 524*6d38604fSBaptiste Daroussin warnx("No entry for %s in " 525*6d38604fSBaptiste Daroussin "the manual.", *argv); 526*6d38604fSBaptiste Daroussin mandoc_msg_setrc(MANDOCLEVEL_BADARG); 52761d06d6bSBaptiste Daroussin continue; 528*6d38604fSBaptiste Daroussin } 529*6d38604fSBaptiste Daroussin if (resnsz == 0) { 530*6d38604fSBaptiste Daroussin if (access(*argv, R_OK) == -1) { 531*6d38604fSBaptiste Daroussin mandoc_msg_setinfilename(*argv); 53245a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 53345a5aec3SBaptiste Daroussin 0, 0, "%s", strerror(errno)); 53445a5aec3SBaptiste Daroussin mandoc_msg_setinfilename(NULL); 53561d06d6bSBaptiste Daroussin continue; 53661d06d6bSBaptiste Daroussin } 537*6d38604fSBaptiste Daroussin resnsz = 1; 538*6d38604fSBaptiste Daroussin resn = mandoc_calloc(resnsz, sizeof(*res)); 539*6d38604fSBaptiste Daroussin resn->file = mandoc_strdup(*argv); 540*6d38604fSBaptiste Daroussin resn->ipath = SIZE_MAX; 541*6d38604fSBaptiste Daroussin resn->form = FORM_SRC; 542*6d38604fSBaptiste Daroussin } 543*6d38604fSBaptiste Daroussin if (outmode != OUTMODE_ONE || resnsz == 1) { 54461d06d6bSBaptiste Daroussin res = mandoc_reallocarray(res, 545*6d38604fSBaptiste Daroussin ressz + resnsz, sizeof(*res)); 546*6d38604fSBaptiste Daroussin memcpy(res + ressz, resn, 547*6d38604fSBaptiste Daroussin sizeof(*resn) * resnsz); 548*6d38604fSBaptiste Daroussin ressz += resnsz; 549*6d38604fSBaptiste Daroussin continue; 55061d06d6bSBaptiste Daroussin } 55161d06d6bSBaptiste Daroussin 55261d06d6bSBaptiste Daroussin /* Search for the best section. */ 553*6d38604fSBaptiste Daroussin 554*6d38604fSBaptiste Daroussin best_prio = 40; 555*6d38604fSBaptiste Daroussin for (ib = i = 0; i < resnsz; i++) { 556*6d38604fSBaptiste Daroussin sec = resn[i].file; 55761d06d6bSBaptiste Daroussin sec += strcspn(sec, "123456789"); 55861d06d6bSBaptiste Daroussin if (sec[0] == '\0') 55945a5aec3SBaptiste Daroussin continue; /* No section at all. */ 56061d06d6bSBaptiste Daroussin prio = sec_prios[sec[0] - '1']; 56145a5aec3SBaptiste Daroussin if (search.sec != NULL) { 56245a5aec3SBaptiste Daroussin ssz = strlen(search.sec); 56345a5aec3SBaptiste Daroussin if (strncmp(sec, search.sec, ssz) == 0) 56445a5aec3SBaptiste Daroussin sec += ssz; 56545a5aec3SBaptiste Daroussin } else 56645a5aec3SBaptiste Daroussin sec++; /* Prefer without suffix. */ 56745a5aec3SBaptiste Daroussin if (*sec != '/') 56845a5aec3SBaptiste Daroussin prio += 10; /* Wrong dir name. */ 569*6d38604fSBaptiste Daroussin if (search.sec != NULL) { 570*6d38604fSBaptiste Daroussin ep = strchr(sec, '\0'); 571*6d38604fSBaptiste Daroussin if (ep - sec > 3 && 572*6d38604fSBaptiste Daroussin strncmp(ep - 3, ".gz", 3) == 0) 573*6d38604fSBaptiste Daroussin ep -= 3; 574*6d38604fSBaptiste Daroussin if ((size_t)(ep - sec) < ssz + 3 || 575*6d38604fSBaptiste Daroussin strncmp(ep - ssz, search.sec, 576*6d38604fSBaptiste Daroussin ssz) != 0) /* Wrong file */ 577*6d38604fSBaptiste Daroussin prio += 20; /* extension. */ 578*6d38604fSBaptiste Daroussin } 57961d06d6bSBaptiste Daroussin if (prio >= best_prio) 58061d06d6bSBaptiste Daroussin continue; 58161d06d6bSBaptiste Daroussin best_prio = prio; 582*6d38604fSBaptiste Daroussin ib = i; 58361d06d6bSBaptiste Daroussin } 584*6d38604fSBaptiste Daroussin res = mandoc_reallocarray(res, ressz + 1, 585*6d38604fSBaptiste Daroussin sizeof(*res)); 586*6d38604fSBaptiste Daroussin memcpy(res + ressz++, resn + ib, sizeof(*resn)); 58761d06d6bSBaptiste Daroussin } 58861d06d6bSBaptiste Daroussin 589*6d38604fSBaptiste Daroussin /* apropos(1), whatis(1): Process the full search expression. */ 59061d06d6bSBaptiste Daroussin 591*6d38604fSBaptiste Daroussin } else if (search.argmode != ARG_FILE) { 592*6d38604fSBaptiste Daroussin if (mansearch(&search, &conf.manpath, 593*6d38604fSBaptiste Daroussin argc, argv, &res, &ressz) == 0) 594*6d38604fSBaptiste Daroussin usage(search.argmode); 595*6d38604fSBaptiste Daroussin 596*6d38604fSBaptiste Daroussin if (ressz == 0) { 597*6d38604fSBaptiste Daroussin warnx("nothing appropriate"); 598*6d38604fSBaptiste Daroussin mandoc_msg_setrc(MANDOCLEVEL_BADARG); 59961d06d6bSBaptiste Daroussin goto out; 60061d06d6bSBaptiste Daroussin } 60161d06d6bSBaptiste Daroussin 602*6d38604fSBaptiste Daroussin /* mandoc(1): Take command line arguments as file names. */ 60361d06d6bSBaptiste Daroussin 60461d06d6bSBaptiste Daroussin } else { 605*6d38604fSBaptiste Daroussin ressz = argc > 0 ? argc : 1; 606*6d38604fSBaptiste Daroussin res = mandoc_calloc(ressz, sizeof(*res)); 607*6d38604fSBaptiste Daroussin for (i = 0; i < ressz; i++) { 608*6d38604fSBaptiste Daroussin if (argc > 0) 609*6d38604fSBaptiste Daroussin res[i].file = mandoc_strdup(argv[i]); 610*6d38604fSBaptiste Daroussin res[i].ipath = SIZE_MAX; 611*6d38604fSBaptiste Daroussin res[i].form = FORM_SRC; 61245a5aec3SBaptiste Daroussin } 61361d06d6bSBaptiste Daroussin } 614*6d38604fSBaptiste Daroussin 615*6d38604fSBaptiste Daroussin switch (outmode) { 616*6d38604fSBaptiste Daroussin case OUTMODE_FLN: 617*6d38604fSBaptiste Daroussin for (i = 0; i < ressz; i++) 618*6d38604fSBaptiste Daroussin puts(res[i].file); 619*6d38604fSBaptiste Daroussin goto out; 620*6d38604fSBaptiste Daroussin case OUTMODE_LST: 621*6d38604fSBaptiste Daroussin for (i = 0; i < ressz; i++) 622*6d38604fSBaptiste Daroussin printf("%s - %s\n", res[i].names, 623*6d38604fSBaptiste Daroussin res[i].output == NULL ? "" : 624*6d38604fSBaptiste Daroussin res[i].output); 625*6d38604fSBaptiste Daroussin goto out; 626*6d38604fSBaptiste Daroussin default: 627*6d38604fSBaptiste Daroussin break; 628*6d38604fSBaptiste Daroussin } 62961d06d6bSBaptiste Daroussin 63045a5aec3SBaptiste Daroussin if (search.argmode == ARG_FILE && auxpaths != NULL) { 63145a5aec3SBaptiste Daroussin if (strcmp(auxpaths, "doc") == 0) 63245a5aec3SBaptiste Daroussin options |= MPARSE_MDOC; 63345a5aec3SBaptiste Daroussin else if (strcmp(auxpaths, "an") == 0) 63445a5aec3SBaptiste Daroussin options |= MPARSE_MAN; 63545a5aec3SBaptiste Daroussin } 63661d06d6bSBaptiste Daroussin 63761d06d6bSBaptiste Daroussin mchars_alloc(); 638*6d38604fSBaptiste Daroussin mp = mparse_alloc(options, os_e, os_s); 63961d06d6bSBaptiste Daroussin 64061d06d6bSBaptiste Daroussin /* 64161d06d6bSBaptiste Daroussin * Remember the original working directory, if possible. 64261d06d6bSBaptiste Daroussin * This will be needed if some names on the command line 64361d06d6bSBaptiste Daroussin * are page names and some are relative file names. 64461d06d6bSBaptiste Daroussin * Do not error out if the current directory is not 64561d06d6bSBaptiste Daroussin * readable: Maybe it won't be needed after all. 64661d06d6bSBaptiste Daroussin */ 64761d06d6bSBaptiste Daroussin startdir = open(".", O_RDONLY | O_DIRECTORY); 648*6d38604fSBaptiste Daroussin for (i = 0; i < ressz; i++) { 649*6d38604fSBaptiste Daroussin process_onefile(mp, res + i, startdir, &outst, &conf); 650*6d38604fSBaptiste Daroussin if (outst.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) 65161d06d6bSBaptiste Daroussin break; 65261d06d6bSBaptiste Daroussin } 65361d06d6bSBaptiste Daroussin if (startdir != -1) { 65461d06d6bSBaptiste Daroussin (void)fchdir(startdir); 65561d06d6bSBaptiste Daroussin close(startdir); 65661d06d6bSBaptiste Daroussin } 657*6d38604fSBaptiste Daroussin if (conf.output.tag != NULL && conf.output.tag_found == 0) { 658*6d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", conf.output.tag); 659*6d38604fSBaptiste Daroussin conf.output.tag = NULL; 660*6d38604fSBaptiste Daroussin } 661*6d38604fSBaptiste Daroussin if (outst.outdata != NULL) { 662*6d38604fSBaptiste Daroussin switch (outst.outtype) { 66361d06d6bSBaptiste Daroussin case OUTT_HTML: 664*6d38604fSBaptiste Daroussin html_free(outst.outdata); 66561d06d6bSBaptiste Daroussin break; 66661d06d6bSBaptiste Daroussin case OUTT_UTF8: 66761d06d6bSBaptiste Daroussin case OUTT_LOCALE: 66861d06d6bSBaptiste Daroussin case OUTT_ASCII: 669*6d38604fSBaptiste Daroussin ascii_free(outst.outdata); 67061d06d6bSBaptiste Daroussin break; 67161d06d6bSBaptiste Daroussin case OUTT_PDF: 67261d06d6bSBaptiste Daroussin case OUTT_PS: 673*6d38604fSBaptiste Daroussin pspdf_free(outst.outdata); 67461d06d6bSBaptiste Daroussin break; 67561d06d6bSBaptiste Daroussin default: 67661d06d6bSBaptiste Daroussin break; 67761d06d6bSBaptiste Daroussin } 67861d06d6bSBaptiste Daroussin } 67961d06d6bSBaptiste Daroussin mandoc_xr_free(); 680*6d38604fSBaptiste Daroussin mparse_free(mp); 68161d06d6bSBaptiste Daroussin mchars_free(); 68261d06d6bSBaptiste Daroussin 68361d06d6bSBaptiste Daroussin out: 684*6d38604fSBaptiste Daroussin mansearch_free(res, ressz); 685*6d38604fSBaptiste Daroussin if (search.argmode != ARG_FILE) 68661d06d6bSBaptiste Daroussin manconf_free(&conf); 68761d06d6bSBaptiste Daroussin 688*6d38604fSBaptiste Daroussin if (outst.tag_files != NULL) { 689*6d38604fSBaptiste Daroussin if (term_tag_close() != -1 && 690*6d38604fSBaptiste Daroussin conf.output.outfilename == NULL && 691*6d38604fSBaptiste Daroussin conf.output.tagfilename == NULL) 692*6d38604fSBaptiste Daroussin run_pager(&outst, conf.output.tag); 693*6d38604fSBaptiste Daroussin term_tag_unlink(); 694*6d38604fSBaptiste Daroussin } else if (outst.had_output && outst.outtype != OUTT_LINT) 69545a5aec3SBaptiste Daroussin mandoc_msg_summary(); 69645a5aec3SBaptiste Daroussin 6977295610fSBaptiste Daroussin return (int)mandoc_msg_getrc(); 69861d06d6bSBaptiste Daroussin } 69961d06d6bSBaptiste Daroussin 70061d06d6bSBaptiste Daroussin static void 70161d06d6bSBaptiste Daroussin usage(enum argmode argmode) 70261d06d6bSBaptiste Daroussin { 70361d06d6bSBaptiste Daroussin switch (argmode) { 70461d06d6bSBaptiste Daroussin case ARG_FILE: 70561d06d6bSBaptiste Daroussin fputs("usage: mandoc [-ac] [-I os=name] " 70661d06d6bSBaptiste Daroussin "[-K encoding] [-mdoc | -man] [-O options]\n" 70761d06d6bSBaptiste Daroussin "\t [-T output] [-W level] [file ...]\n", stderr); 70861d06d6bSBaptiste Daroussin break; 70961d06d6bSBaptiste Daroussin case ARG_NAME: 71061d06d6bSBaptiste Daroussin fputs("usage: man [-acfhklw] [-C file] [-M path] " 71161d06d6bSBaptiste Daroussin "[-m path] [-S subsection]\n" 71261d06d6bSBaptiste Daroussin "\t [[-s] section] name ...\n", stderr); 71361d06d6bSBaptiste Daroussin break; 71461d06d6bSBaptiste Daroussin case ARG_WORD: 71561d06d6bSBaptiste Daroussin fputs("usage: whatis [-afk] [-C file] " 71661d06d6bSBaptiste Daroussin "[-M path] [-m path] [-O outkey] [-S arch]\n" 71761d06d6bSBaptiste Daroussin "\t [-s section] name ...\n", stderr); 71861d06d6bSBaptiste Daroussin break; 71961d06d6bSBaptiste Daroussin case ARG_EXPR: 72061d06d6bSBaptiste Daroussin fputs("usage: apropos [-afk] [-C file] " 72161d06d6bSBaptiste Daroussin "[-M path] [-m path] [-O outkey] [-S arch]\n" 72261d06d6bSBaptiste Daroussin "\t [-s section] expression ...\n", stderr); 72361d06d6bSBaptiste Daroussin break; 72461d06d6bSBaptiste Daroussin } 72561d06d6bSBaptiste Daroussin exit((int)MANDOCLEVEL_BADARG); 72661d06d6bSBaptiste Daroussin } 72761d06d6bSBaptiste Daroussin 728*6d38604fSBaptiste Daroussin static void 729*6d38604fSBaptiste Daroussin glob_esc(char **dst, const char *src, const char *suffix) 730*6d38604fSBaptiste Daroussin { 731*6d38604fSBaptiste Daroussin while (*src != '\0') { 732*6d38604fSBaptiste Daroussin if (strchr("*?[", *src) != NULL) 733*6d38604fSBaptiste Daroussin *(*dst)++ = '\\'; 734*6d38604fSBaptiste Daroussin *(*dst)++ = *src++; 735*6d38604fSBaptiste Daroussin } 736*6d38604fSBaptiste Daroussin while (*suffix != '\0') 737*6d38604fSBaptiste Daroussin *(*dst)++ = *suffix++; 738*6d38604fSBaptiste Daroussin } 739*6d38604fSBaptiste Daroussin 740*6d38604fSBaptiste Daroussin static void 741*6d38604fSBaptiste Daroussin fs_append(char **file, size_t filesz, int copy, size_t ipath, 742*6d38604fSBaptiste Daroussin const char *sec, enum form form, struct manpage **res, size_t *ressz) 743*6d38604fSBaptiste Daroussin { 744*6d38604fSBaptiste Daroussin struct manpage *page; 745*6d38604fSBaptiste Daroussin 746*6d38604fSBaptiste Daroussin *res = mandoc_reallocarray(*res, *ressz + filesz, sizeof(**res)); 747*6d38604fSBaptiste Daroussin page = *res + *ressz; 748*6d38604fSBaptiste Daroussin *ressz += filesz; 749*6d38604fSBaptiste Daroussin for (;;) { 750*6d38604fSBaptiste Daroussin page->file = copy ? mandoc_strdup(*file) : *file; 751*6d38604fSBaptiste Daroussin page->names = NULL; 752*6d38604fSBaptiste Daroussin page->output = NULL; 753*6d38604fSBaptiste Daroussin page->bits = NAME_FILE & NAME_MASK; 754*6d38604fSBaptiste Daroussin page->ipath = ipath; 755*6d38604fSBaptiste Daroussin page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; 756*6d38604fSBaptiste Daroussin page->form = form; 757*6d38604fSBaptiste Daroussin if (--filesz == 0) 758*6d38604fSBaptiste Daroussin break; 759*6d38604fSBaptiste Daroussin file++; 760*6d38604fSBaptiste Daroussin page++; 761*6d38604fSBaptiste Daroussin } 762*6d38604fSBaptiste Daroussin } 763*6d38604fSBaptiste Daroussin 76461d06d6bSBaptiste Daroussin static int 76561d06d6bSBaptiste Daroussin fs_lookup(const struct manpaths *paths, size_t ipath, 76661d06d6bSBaptiste Daroussin const char *sec, const char *arch, const char *name, 76761d06d6bSBaptiste Daroussin struct manpage **res, size_t *ressz) 76861d06d6bSBaptiste Daroussin { 76945a5aec3SBaptiste Daroussin struct stat sb; 77061d06d6bSBaptiste Daroussin glob_t globinfo; 771*6d38604fSBaptiste Daroussin char *file, *cp, secnum[2]; 77261d06d6bSBaptiste Daroussin int globres; 77361d06d6bSBaptiste Daroussin enum form form; 77461d06d6bSBaptiste Daroussin 775*6d38604fSBaptiste Daroussin const char *const slman = "/man"; 776*6d38604fSBaptiste Daroussin const char *const slash = "/"; 777*6d38604fSBaptiste Daroussin const char *const sglob = ".[01-9]*"; 778*6d38604fSBaptiste Daroussin const char *const dot = "."; 779*6d38604fSBaptiste Daroussin const char *const aster = "*"; 780*6d38604fSBaptiste Daroussin 781*6d38604fSBaptiste Daroussin memset(&globinfo, 0, sizeof(globinfo)); 78261d06d6bSBaptiste Daroussin form = FORM_SRC; 783*6d38604fSBaptiste Daroussin 78461d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s.%s", 78561d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name, sec); 78645a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) 78761d06d6bSBaptiste Daroussin goto found; 78861d06d6bSBaptiste Daroussin free(file); 78961d06d6bSBaptiste Daroussin 79061d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/cat%s/%s.0", 79161d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name); 79245a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) { 79361d06d6bSBaptiste Daroussin form = FORM_CAT; 79461d06d6bSBaptiste Daroussin goto found; 79561d06d6bSBaptiste Daroussin } 79661d06d6bSBaptiste Daroussin free(file); 79761d06d6bSBaptiste Daroussin 79861d06d6bSBaptiste Daroussin if (arch != NULL) { 79961d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", 80061d06d6bSBaptiste Daroussin paths->paths[ipath], sec, arch, name, sec); 80145a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) 80261d06d6bSBaptiste Daroussin goto found; 80361d06d6bSBaptiste Daroussin free(file); 80461d06d6bSBaptiste Daroussin } 80561d06d6bSBaptiste Daroussin 806*6d38604fSBaptiste Daroussin cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 + 807*6d38604fSBaptiste Daroussin strlen(slman) + strlen(sec) * 2 + strlen(slash) + 808*6d38604fSBaptiste Daroussin strlen(name) * 2 + strlen(sglob) + 1); 809*6d38604fSBaptiste Daroussin glob_esc(&cp, paths->paths[ipath], slman); 810*6d38604fSBaptiste Daroussin glob_esc(&cp, sec, slash); 811*6d38604fSBaptiste Daroussin glob_esc(&cp, name, sglob); 812*6d38604fSBaptiste Daroussin *cp = '\0'; 81361d06d6bSBaptiste Daroussin globres = glob(file, 0, NULL, &globinfo); 81461d06d6bSBaptiste Daroussin if (globres != 0 && globres != GLOB_NOMATCH) 81545a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GLOB, 0, 0, 81645a5aec3SBaptiste Daroussin "%s: %s", file, strerror(errno)); 81761d06d6bSBaptiste Daroussin free(file); 818*6d38604fSBaptiste Daroussin file = NULL; 81961d06d6bSBaptiste Daroussin if (globres == 0) 82061d06d6bSBaptiste Daroussin goto found; 821*6d38604fSBaptiste Daroussin globfree(&globinfo); 822*6d38604fSBaptiste Daroussin 823*6d38604fSBaptiste Daroussin if (sec[1] != '\0' && *ressz == 0) { 824*6d38604fSBaptiste Daroussin secnum[0] = sec[0]; 825*6d38604fSBaptiste Daroussin secnum[1] = '\0'; 826*6d38604fSBaptiste Daroussin cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 + 827*6d38604fSBaptiste Daroussin strlen(slman) + strlen(secnum) * 2 + strlen(slash) + 828*6d38604fSBaptiste Daroussin strlen(name) * 2 + strlen(dot) + 829*6d38604fSBaptiste Daroussin strlen(sec) * 2 + strlen(aster) + 1); 830*6d38604fSBaptiste Daroussin glob_esc(&cp, paths->paths[ipath], slman); 831*6d38604fSBaptiste Daroussin glob_esc(&cp, secnum, slash); 832*6d38604fSBaptiste Daroussin glob_esc(&cp, name, dot); 833*6d38604fSBaptiste Daroussin glob_esc(&cp, sec, aster); 834*6d38604fSBaptiste Daroussin *cp = '\0'; 835*6d38604fSBaptiste Daroussin globres = glob(file, 0, NULL, &globinfo); 836*6d38604fSBaptiste Daroussin if (globres != 0 && globres != GLOB_NOMATCH) 837*6d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_GLOB, 0, 0, 838*6d38604fSBaptiste Daroussin "%s: %s", file, strerror(errno)); 83945a5aec3SBaptiste Daroussin free(file); 840*6d38604fSBaptiste Daroussin file = NULL; 841*6d38604fSBaptiste Daroussin if (globres == 0) 842*6d38604fSBaptiste Daroussin goto found; 843*6d38604fSBaptiste Daroussin globfree(&globinfo); 84445a5aec3SBaptiste Daroussin } 845*6d38604fSBaptiste Daroussin 84661d06d6bSBaptiste Daroussin if (res != NULL || ipath + 1 != paths->sz) 84745a5aec3SBaptiste Daroussin return -1; 84861d06d6bSBaptiste Daroussin 84961d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s.%s", name, sec); 85045a5aec3SBaptiste Daroussin globres = stat(file, &sb); 85161d06d6bSBaptiste Daroussin free(file); 85245a5aec3SBaptiste Daroussin return globres; 85361d06d6bSBaptiste Daroussin 85461d06d6bSBaptiste Daroussin found: 85561d06d6bSBaptiste Daroussin warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", 85661d06d6bSBaptiste Daroussin name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); 857*6d38604fSBaptiste Daroussin if (res == NULL) 85861d06d6bSBaptiste Daroussin free(file); 859*6d38604fSBaptiste Daroussin else if (file == NULL) 860*6d38604fSBaptiste Daroussin fs_append(globinfo.gl_pathv, globinfo.gl_pathc, 1, 861*6d38604fSBaptiste Daroussin ipath, sec, form, res, ressz); 862*6d38604fSBaptiste Daroussin else 863*6d38604fSBaptiste Daroussin fs_append(&file, 1, 0, ipath, sec, form, res, ressz); 864*6d38604fSBaptiste Daroussin globfree(&globinfo); 86545a5aec3SBaptiste Daroussin return 0; 86661d06d6bSBaptiste Daroussin } 86761d06d6bSBaptiste Daroussin 86861d06d6bSBaptiste Daroussin static int 86961d06d6bSBaptiste Daroussin fs_search(const struct mansearch *cfg, const struct manpaths *paths, 870*6d38604fSBaptiste Daroussin const char *name, struct manpage **res, size_t *ressz) 87161d06d6bSBaptiste Daroussin { 87261d06d6bSBaptiste Daroussin const char *const sections[] = 87361d06d6bSBaptiste Daroussin {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; 87461d06d6bSBaptiste Daroussin const size_t nsec = sizeof(sections)/sizeof(sections[0]); 87561d06d6bSBaptiste Daroussin 876*6d38604fSBaptiste Daroussin size_t ipath, isec; 87761d06d6bSBaptiste Daroussin 87861d06d6bSBaptiste Daroussin assert(cfg->argmode == ARG_NAME); 87961d06d6bSBaptiste Daroussin if (res != NULL) 88061d06d6bSBaptiste Daroussin *res = NULL; 881*6d38604fSBaptiste Daroussin *ressz = 0; 88261d06d6bSBaptiste Daroussin for (ipath = 0; ipath < paths->sz; ipath++) { 88361d06d6bSBaptiste Daroussin if (cfg->sec != NULL) { 884*6d38604fSBaptiste Daroussin if (fs_lookup(paths, ipath, cfg->sec, cfg->arch, 885*6d38604fSBaptiste Daroussin name, res, ressz) != -1 && cfg->firstmatch) 88645a5aec3SBaptiste Daroussin return 0; 887*6d38604fSBaptiste Daroussin } else { 888*6d38604fSBaptiste Daroussin for (isec = 0; isec < nsec; isec++) 88961d06d6bSBaptiste Daroussin if (fs_lookup(paths, ipath, sections[isec], 890*6d38604fSBaptiste Daroussin cfg->arch, name, res, ressz) != -1 && 89161d06d6bSBaptiste Daroussin cfg->firstmatch) 89245a5aec3SBaptiste Daroussin return 0; 89361d06d6bSBaptiste Daroussin } 89461d06d6bSBaptiste Daroussin } 89545a5aec3SBaptiste Daroussin return -1; 89661d06d6bSBaptiste Daroussin } 89761d06d6bSBaptiste Daroussin 89861d06d6bSBaptiste Daroussin static void 899*6d38604fSBaptiste Daroussin process_onefile(struct mparse *mp, struct manpage *resp, int startdir, 900*6d38604fSBaptiste Daroussin struct outstate *outst, struct manconf *conf) 90161d06d6bSBaptiste Daroussin { 902*6d38604fSBaptiste Daroussin int fd; 903*6d38604fSBaptiste Daroussin 904*6d38604fSBaptiste Daroussin /* 905*6d38604fSBaptiste Daroussin * Changing directories is not needed in ARG_FILE mode. 906*6d38604fSBaptiste Daroussin * Do it on a best-effort basis. Even in case of 907*6d38604fSBaptiste Daroussin * failure, some functionality may still work. 908*6d38604fSBaptiste Daroussin */ 909*6d38604fSBaptiste Daroussin if (resp->ipath != SIZE_MAX) 910*6d38604fSBaptiste Daroussin (void)chdir(conf->manpath.paths[resp->ipath]); 911*6d38604fSBaptiste Daroussin else if (startdir != -1) 912*6d38604fSBaptiste Daroussin (void)fchdir(startdir); 913*6d38604fSBaptiste Daroussin 914*6d38604fSBaptiste Daroussin mandoc_msg_setinfilename(resp->file); 915*6d38604fSBaptiste Daroussin if (resp->file != NULL) { 916*6d38604fSBaptiste Daroussin if ((fd = mparse_open(mp, resp->file)) == -1) { 917*6d38604fSBaptiste Daroussin mandoc_msg(resp->ipath == SIZE_MAX ? 918*6d38604fSBaptiste Daroussin MANDOCERR_BADARG_BAD : MANDOCERR_OPEN, 919*6d38604fSBaptiste Daroussin 0, 0, "%s", strerror(errno)); 920*6d38604fSBaptiste Daroussin mandoc_msg_setinfilename(NULL); 921*6d38604fSBaptiste Daroussin return; 922*6d38604fSBaptiste Daroussin } 923*6d38604fSBaptiste Daroussin } else 924*6d38604fSBaptiste Daroussin fd = STDIN_FILENO; 925*6d38604fSBaptiste Daroussin 926*6d38604fSBaptiste Daroussin if (outst->use_pager) { 927*6d38604fSBaptiste Daroussin outst->use_pager = 0; 928*6d38604fSBaptiste Daroussin outst->tag_files = term_tag_init(conf->output.outfilename, 929*6d38604fSBaptiste Daroussin outst->outtype == OUTT_HTML ? ".html" : "", 930*6d38604fSBaptiste Daroussin conf->output.tagfilename); 931*6d38604fSBaptiste Daroussin #if HAVE_PLEDGE 932*6d38604fSBaptiste Daroussin if ((conf->output.outfilename != NULL || 933*6d38604fSBaptiste Daroussin conf->output.tagfilename != NULL) && 934*6d38604fSBaptiste Daroussin pledge("stdio rpath cpath", NULL) == -1) { 935*6d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, 936*6d38604fSBaptiste Daroussin "%s", strerror(errno)); 937*6d38604fSBaptiste Daroussin exit(mandoc_msg_getrc()); 938*6d38604fSBaptiste Daroussin } 939*6d38604fSBaptiste Daroussin #endif 940*6d38604fSBaptiste Daroussin } 941*6d38604fSBaptiste Daroussin if (outst->had_output && outst->outtype <= OUTT_UTF8) { 942*6d38604fSBaptiste Daroussin if (outst->outdata == NULL) 943*6d38604fSBaptiste Daroussin outdata_alloc(outst, &conf->output); 944*6d38604fSBaptiste Daroussin terminal_sepline(outst->outdata); 945*6d38604fSBaptiste Daroussin } 946*6d38604fSBaptiste Daroussin 947*6d38604fSBaptiste Daroussin if (resp->form == FORM_SRC) 948*6d38604fSBaptiste Daroussin parse(mp, fd, resp->file, outst, conf); 949*6d38604fSBaptiste Daroussin else { 950*6d38604fSBaptiste Daroussin passthrough(fd, conf->output.synopsisonly); 951*6d38604fSBaptiste Daroussin outst->had_output = 1; 952*6d38604fSBaptiste Daroussin } 953*6d38604fSBaptiste Daroussin 954*6d38604fSBaptiste Daroussin if (ferror(stdout)) { 955*6d38604fSBaptiste Daroussin if (outst->tag_files != NULL) { 956*6d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s: %s", 957*6d38604fSBaptiste Daroussin outst->tag_files->ofn, strerror(errno)); 958*6d38604fSBaptiste Daroussin term_tag_unlink(); 959*6d38604fSBaptiste Daroussin outst->tag_files = NULL; 960*6d38604fSBaptiste Daroussin } else 961*6d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s", 962*6d38604fSBaptiste Daroussin strerror(errno)); 963*6d38604fSBaptiste Daroussin } 964*6d38604fSBaptiste Daroussin mandoc_msg_setinfilename(NULL); 965*6d38604fSBaptiste Daroussin } 966*6d38604fSBaptiste Daroussin 967*6d38604fSBaptiste Daroussin static void 968*6d38604fSBaptiste Daroussin parse(struct mparse *mp, int fd, const char *file, 969*6d38604fSBaptiste Daroussin struct outstate *outst, struct manconf *conf) 970*6d38604fSBaptiste Daroussin { 971*6d38604fSBaptiste Daroussin static struct manpaths basepaths; 972*6d38604fSBaptiste Daroussin static int previous; 9737295610fSBaptiste Daroussin struct roff_meta *meta; 97461d06d6bSBaptiste Daroussin 97561d06d6bSBaptiste Daroussin assert(fd >= 0); 976*6d38604fSBaptiste Daroussin if (file == NULL) 977*6d38604fSBaptiste Daroussin file = "<stdin>"; 97861d06d6bSBaptiste Daroussin 979*6d38604fSBaptiste Daroussin if (previous) 980*6d38604fSBaptiste Daroussin mparse_reset(mp); 981*6d38604fSBaptiste Daroussin else 982*6d38604fSBaptiste Daroussin previous = 1; 983*6d38604fSBaptiste Daroussin 984*6d38604fSBaptiste Daroussin mparse_readfd(mp, fd, file); 98561d06d6bSBaptiste Daroussin if (fd != STDIN_FILENO) 98661d06d6bSBaptiste Daroussin close(fd); 98761d06d6bSBaptiste Daroussin 98861d06d6bSBaptiste Daroussin /* 98961d06d6bSBaptiste Daroussin * With -Wstop and warnings or errors of at least the requested 99061d06d6bSBaptiste Daroussin * level, do not produce output. 99161d06d6bSBaptiste Daroussin */ 99261d06d6bSBaptiste Daroussin 993*6d38604fSBaptiste Daroussin if (outst->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) 99461d06d6bSBaptiste Daroussin return; 99561d06d6bSBaptiste Daroussin 996*6d38604fSBaptiste Daroussin if (outst->outdata == NULL) 997*6d38604fSBaptiste Daroussin outdata_alloc(outst, &conf->output); 998*6d38604fSBaptiste Daroussin else if (outst->outtype == OUTT_HTML) 999*6d38604fSBaptiste Daroussin html_reset(outst->outdata); 100061d06d6bSBaptiste Daroussin 10017295610fSBaptiste Daroussin mandoc_xr_reset(); 1002*6d38604fSBaptiste Daroussin meta = mparse_result(mp); 100361d06d6bSBaptiste Daroussin 100461d06d6bSBaptiste Daroussin /* Execute the out device, if it exists. */ 100561d06d6bSBaptiste Daroussin 1006*6d38604fSBaptiste Daroussin outst->had_output = 1; 10077295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MDOC) { 1008*6d38604fSBaptiste Daroussin switch (outst->outtype) { 100961d06d6bSBaptiste Daroussin case OUTT_HTML: 1010*6d38604fSBaptiste Daroussin html_mdoc(outst->outdata, meta); 101161d06d6bSBaptiste Daroussin break; 101261d06d6bSBaptiste Daroussin case OUTT_TREE: 1013*6d38604fSBaptiste Daroussin tree_mdoc(outst->outdata, meta); 101461d06d6bSBaptiste Daroussin break; 101561d06d6bSBaptiste Daroussin case OUTT_MAN: 1016*6d38604fSBaptiste Daroussin man_mdoc(outst->outdata, meta); 101761d06d6bSBaptiste Daroussin break; 101861d06d6bSBaptiste Daroussin case OUTT_PDF: 101961d06d6bSBaptiste Daroussin case OUTT_ASCII: 102061d06d6bSBaptiste Daroussin case OUTT_UTF8: 102161d06d6bSBaptiste Daroussin case OUTT_LOCALE: 102261d06d6bSBaptiste Daroussin case OUTT_PS: 1023*6d38604fSBaptiste Daroussin terminal_mdoc(outst->outdata, meta); 102461d06d6bSBaptiste Daroussin break; 102561d06d6bSBaptiste Daroussin case OUTT_MARKDOWN: 1026*6d38604fSBaptiste Daroussin markdown_mdoc(outst->outdata, meta); 102761d06d6bSBaptiste Daroussin break; 102861d06d6bSBaptiste Daroussin default: 102961d06d6bSBaptiste Daroussin break; 103061d06d6bSBaptiste Daroussin } 103161d06d6bSBaptiste Daroussin } 10327295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MAN) { 1033*6d38604fSBaptiste Daroussin switch (outst->outtype) { 103461d06d6bSBaptiste Daroussin case OUTT_HTML: 1035*6d38604fSBaptiste Daroussin html_man(outst->outdata, meta); 103661d06d6bSBaptiste Daroussin break; 103761d06d6bSBaptiste Daroussin case OUTT_TREE: 1038*6d38604fSBaptiste Daroussin tree_man(outst->outdata, meta); 103961d06d6bSBaptiste Daroussin break; 104061d06d6bSBaptiste Daroussin case OUTT_MAN: 1041*6d38604fSBaptiste Daroussin mparse_copy(mp); 104261d06d6bSBaptiste Daroussin break; 104361d06d6bSBaptiste Daroussin case OUTT_PDF: 104461d06d6bSBaptiste Daroussin case OUTT_ASCII: 104561d06d6bSBaptiste Daroussin case OUTT_UTF8: 104661d06d6bSBaptiste Daroussin case OUTT_LOCALE: 104761d06d6bSBaptiste Daroussin case OUTT_PS: 1048*6d38604fSBaptiste Daroussin terminal_man(outst->outdata, meta); 1049*6d38604fSBaptiste Daroussin break; 1050*6d38604fSBaptiste Daroussin case OUTT_MARKDOWN: 1051*6d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_MAN_TMARKDOWN, 0, 0, NULL); 105261d06d6bSBaptiste Daroussin break; 105361d06d6bSBaptiste Daroussin default: 105461d06d6bSBaptiste Daroussin break; 105561d06d6bSBaptiste Daroussin } 105661d06d6bSBaptiste Daroussin } 1057*6d38604fSBaptiste Daroussin if (conf->output.tag != NULL && conf->output.tag_found == 0 && 1058*6d38604fSBaptiste Daroussin tag_exists(conf->output.tag)) 1059*6d38604fSBaptiste Daroussin conf->output.tag_found = 1; 1060*6d38604fSBaptiste Daroussin 1061*6d38604fSBaptiste Daroussin if (mandoc_msg_getmin() < MANDOCERR_STYLE) { 1062*6d38604fSBaptiste Daroussin if (basepaths.sz == 0) 1063*6d38604fSBaptiste Daroussin manpath_base(&basepaths); 1064*6d38604fSBaptiste Daroussin check_xr(&basepaths); 1065*6d38604fSBaptiste Daroussin } else if (mandoc_msg_getmin() < MANDOCERR_WARNING) 1066*6d38604fSBaptiste Daroussin check_xr(&conf->manpath); 106761d06d6bSBaptiste Daroussin } 106861d06d6bSBaptiste Daroussin 106961d06d6bSBaptiste Daroussin static void 1070*6d38604fSBaptiste Daroussin check_xr(struct manpaths *paths) 107161d06d6bSBaptiste Daroussin { 107261d06d6bSBaptiste Daroussin struct mansearch search; 107361d06d6bSBaptiste Daroussin struct mandoc_xr *xr; 107461d06d6bSBaptiste Daroussin size_t sz; 107561d06d6bSBaptiste Daroussin 107661d06d6bSBaptiste Daroussin for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { 107761d06d6bSBaptiste Daroussin if (xr->line == -1) 107861d06d6bSBaptiste Daroussin continue; 107961d06d6bSBaptiste Daroussin search.arch = NULL; 108061d06d6bSBaptiste Daroussin search.sec = xr->sec; 108161d06d6bSBaptiste Daroussin search.outkey = NULL; 108261d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 108361d06d6bSBaptiste Daroussin search.firstmatch = 1; 1084*6d38604fSBaptiste Daroussin if (mansearch(&search, paths, 1, &xr->name, NULL, &sz)) 108561d06d6bSBaptiste Daroussin continue; 1086*6d38604fSBaptiste Daroussin if (fs_search(&search, paths, xr->name, NULL, &sz) != -1) 108761d06d6bSBaptiste Daroussin continue; 108861d06d6bSBaptiste Daroussin if (xr->count == 1) 10897295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_XR_BAD, xr->line, 10907295610fSBaptiste Daroussin xr->pos + 1, "Xr %s %s", xr->name, xr->sec); 109161d06d6bSBaptiste Daroussin else 10927295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_XR_BAD, xr->line, 10937295610fSBaptiste Daroussin xr->pos + 1, "Xr %s %s (%d times)", 109461d06d6bSBaptiste Daroussin xr->name, xr->sec, xr->count); 109561d06d6bSBaptiste Daroussin } 109661d06d6bSBaptiste Daroussin } 109761d06d6bSBaptiste Daroussin 109861d06d6bSBaptiste Daroussin static void 1099*6d38604fSBaptiste Daroussin outdata_alloc(struct outstate *outst, struct manoutput *outconf) 110061d06d6bSBaptiste Daroussin { 1101*6d38604fSBaptiste Daroussin switch (outst->outtype) { 110261d06d6bSBaptiste Daroussin case OUTT_HTML: 1103*6d38604fSBaptiste Daroussin outst->outdata = html_alloc(outconf); 110461d06d6bSBaptiste Daroussin break; 110561d06d6bSBaptiste Daroussin case OUTT_UTF8: 1106*6d38604fSBaptiste Daroussin outst->outdata = utf8_alloc(outconf); 110761d06d6bSBaptiste Daroussin break; 110861d06d6bSBaptiste Daroussin case OUTT_LOCALE: 1109*6d38604fSBaptiste Daroussin outst->outdata = locale_alloc(outconf); 111061d06d6bSBaptiste Daroussin break; 111161d06d6bSBaptiste Daroussin case OUTT_ASCII: 1112*6d38604fSBaptiste Daroussin outst->outdata = ascii_alloc(outconf); 111361d06d6bSBaptiste Daroussin break; 111461d06d6bSBaptiste Daroussin case OUTT_PDF: 1115*6d38604fSBaptiste Daroussin outst->outdata = pdf_alloc(outconf); 111661d06d6bSBaptiste Daroussin break; 111761d06d6bSBaptiste Daroussin case OUTT_PS: 1118*6d38604fSBaptiste Daroussin outst->outdata = ps_alloc(outconf); 111961d06d6bSBaptiste Daroussin break; 112061d06d6bSBaptiste Daroussin default: 112161d06d6bSBaptiste Daroussin break; 112261d06d6bSBaptiste Daroussin } 112361d06d6bSBaptiste Daroussin } 112461d06d6bSBaptiste Daroussin 112561d06d6bSBaptiste Daroussin static void 112645a5aec3SBaptiste Daroussin passthrough(int fd, int synopsis_only) 112761d06d6bSBaptiste Daroussin { 112861d06d6bSBaptiste Daroussin const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; 112961d06d6bSBaptiste Daroussin const char synr[] = "SYNOPSIS"; 113061d06d6bSBaptiste Daroussin 113161d06d6bSBaptiste Daroussin FILE *stream; 113261d06d6bSBaptiste Daroussin char *line, *cp; 113361d06d6bSBaptiste Daroussin size_t linesz; 113461d06d6bSBaptiste Daroussin ssize_t len, written; 113545a5aec3SBaptiste Daroussin int lno, print; 113661d06d6bSBaptiste Daroussin 113745a5aec3SBaptiste Daroussin stream = NULL; 113861d06d6bSBaptiste Daroussin line = NULL; 113961d06d6bSBaptiste Daroussin linesz = 0; 114061d06d6bSBaptiste Daroussin 114161d06d6bSBaptiste Daroussin if (fflush(stdout) == EOF) { 114245a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FFLUSH, 0, 0, "%s", strerror(errno)); 114345a5aec3SBaptiste Daroussin goto done; 114461d06d6bSBaptiste Daroussin } 114561d06d6bSBaptiste Daroussin if ((stream = fdopen(fd, "r")) == NULL) { 114661d06d6bSBaptiste Daroussin close(fd); 114745a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); 114845a5aec3SBaptiste Daroussin goto done; 114961d06d6bSBaptiste Daroussin } 115061d06d6bSBaptiste Daroussin 115145a5aec3SBaptiste Daroussin lno = print = 0; 115261d06d6bSBaptiste Daroussin while ((len = getline(&line, &linesz, stream)) != -1) { 115345a5aec3SBaptiste Daroussin lno++; 115461d06d6bSBaptiste Daroussin cp = line; 115561d06d6bSBaptiste Daroussin if (synopsis_only) { 115661d06d6bSBaptiste Daroussin if (print) { 115761d06d6bSBaptiste Daroussin if ( ! isspace((unsigned char)*cp)) 115861d06d6bSBaptiste Daroussin goto done; 115961d06d6bSBaptiste Daroussin while (isspace((unsigned char)*cp)) { 116061d06d6bSBaptiste Daroussin cp++; 116161d06d6bSBaptiste Daroussin len--; 116261d06d6bSBaptiste Daroussin } 116361d06d6bSBaptiste Daroussin } else { 116461d06d6bSBaptiste Daroussin if (strcmp(cp, synb) == 0 || 116561d06d6bSBaptiste Daroussin strcmp(cp, synr) == 0) 116661d06d6bSBaptiste Daroussin print = 1; 116761d06d6bSBaptiste Daroussin continue; 116861d06d6bSBaptiste Daroussin } 116961d06d6bSBaptiste Daroussin } 117061d06d6bSBaptiste Daroussin for (; len > 0; len -= written) { 117145a5aec3SBaptiste Daroussin if ((written = write(STDOUT_FILENO, cp, len)) == -1) { 117245a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_WRITE, 0, 0, 117345a5aec3SBaptiste Daroussin "%s", strerror(errno)); 117445a5aec3SBaptiste Daroussin goto done; 117561d06d6bSBaptiste Daroussin } 117661d06d6bSBaptiste Daroussin } 117761d06d6bSBaptiste Daroussin } 117845a5aec3SBaptiste Daroussin if (ferror(stream)) 117945a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GETLINE, lno, 0, "%s", strerror(errno)); 118061d06d6bSBaptiste Daroussin 118161d06d6bSBaptiste Daroussin done: 118261d06d6bSBaptiste Daroussin free(line); 118345a5aec3SBaptiste Daroussin if (stream != NULL) 118461d06d6bSBaptiste Daroussin fclose(stream); 118561d06d6bSBaptiste Daroussin } 118661d06d6bSBaptiste Daroussin 118761d06d6bSBaptiste Daroussin static int 1188*6d38604fSBaptiste Daroussin woptions(char *arg, enum mandoc_os *os_e, int *wstop) 118961d06d6bSBaptiste Daroussin { 119061d06d6bSBaptiste Daroussin char *v, *o; 119161d06d6bSBaptiste Daroussin const char *toks[11]; 119261d06d6bSBaptiste Daroussin 119361d06d6bSBaptiste Daroussin toks[0] = "stop"; 119461d06d6bSBaptiste Daroussin toks[1] = "all"; 119561d06d6bSBaptiste Daroussin toks[2] = "base"; 119661d06d6bSBaptiste Daroussin toks[3] = "style"; 119761d06d6bSBaptiste Daroussin toks[4] = "warning"; 119861d06d6bSBaptiste Daroussin toks[5] = "error"; 119961d06d6bSBaptiste Daroussin toks[6] = "unsupp"; 120061d06d6bSBaptiste Daroussin toks[7] = "fatal"; 120161d06d6bSBaptiste Daroussin toks[8] = "openbsd"; 120261d06d6bSBaptiste Daroussin toks[9] = "netbsd"; 120361d06d6bSBaptiste Daroussin toks[10] = NULL; 120461d06d6bSBaptiste Daroussin 120561d06d6bSBaptiste Daroussin while (*arg) { 120661d06d6bSBaptiste Daroussin o = arg; 120761d06d6bSBaptiste Daroussin switch (getsubopt(&arg, (char * const *)toks, &v)) { 120861d06d6bSBaptiste Daroussin case 0: 1209*6d38604fSBaptiste Daroussin *wstop = 1; 121061d06d6bSBaptiste Daroussin break; 121161d06d6bSBaptiste Daroussin case 1: 121261d06d6bSBaptiste Daroussin case 2: 12137295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 121461d06d6bSBaptiste Daroussin break; 121561d06d6bSBaptiste Daroussin case 3: 12167295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_STYLE); 121761d06d6bSBaptiste Daroussin break; 121861d06d6bSBaptiste Daroussin case 4: 12197295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_WARNING); 122061d06d6bSBaptiste Daroussin break; 122161d06d6bSBaptiste Daroussin case 5: 12227295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_ERROR); 122361d06d6bSBaptiste Daroussin break; 122461d06d6bSBaptiste Daroussin case 6: 12257295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_UNSUPP); 122661d06d6bSBaptiste Daroussin break; 122761d06d6bSBaptiste Daroussin case 7: 122845a5aec3SBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BADARG); 122961d06d6bSBaptiste Daroussin break; 123061d06d6bSBaptiste Daroussin case 8: 12317295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 1232*6d38604fSBaptiste Daroussin *os_e = MANDOC_OS_OPENBSD; 123361d06d6bSBaptiste Daroussin break; 123461d06d6bSBaptiste Daroussin case 9: 12357295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 1236*6d38604fSBaptiste Daroussin *os_e = MANDOC_OS_NETBSD; 123761d06d6bSBaptiste Daroussin break; 123861d06d6bSBaptiste Daroussin default: 123945a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-W %s", o); 124045a5aec3SBaptiste Daroussin return -1; 124145a5aec3SBaptiste Daroussin } 124245a5aec3SBaptiste Daroussin } 124361d06d6bSBaptiste Daroussin return 0; 124461d06d6bSBaptiste Daroussin } 124561d06d6bSBaptiste Daroussin 1246*6d38604fSBaptiste Daroussin /* 1247*6d38604fSBaptiste Daroussin * Wait until moved to the foreground, 1248*6d38604fSBaptiste Daroussin * then fork the pager and wait for the user to close it. 1249*6d38604fSBaptiste Daroussin */ 1250*6d38604fSBaptiste Daroussin static void 1251*6d38604fSBaptiste Daroussin run_pager(struct outstate *outst, char *tag_target) 1252*6d38604fSBaptiste Daroussin { 1253*6d38604fSBaptiste Daroussin int signum, status; 1254*6d38604fSBaptiste Daroussin pid_t man_pgid, tc_pgid; 1255*6d38604fSBaptiste Daroussin pid_t pager_pid, wait_pid; 1256*6d38604fSBaptiste Daroussin 1257*6d38604fSBaptiste Daroussin man_pgid = getpgid(0); 1258*6d38604fSBaptiste Daroussin outst->tag_files->tcpgid = 1259*6d38604fSBaptiste Daroussin man_pgid == getpid() ? getpgid(getppid()) : man_pgid; 1260*6d38604fSBaptiste Daroussin pager_pid = 0; 1261*6d38604fSBaptiste Daroussin signum = SIGSTOP; 1262*6d38604fSBaptiste Daroussin 1263*6d38604fSBaptiste Daroussin for (;;) { 1264*6d38604fSBaptiste Daroussin /* Stop here until moved to the foreground. */ 1265*6d38604fSBaptiste Daroussin 1266*6d38604fSBaptiste Daroussin tc_pgid = tcgetpgrp(STDOUT_FILENO); 1267*6d38604fSBaptiste Daroussin if (tc_pgid != man_pgid) { 1268*6d38604fSBaptiste Daroussin if (tc_pgid == pager_pid) { 1269*6d38604fSBaptiste Daroussin (void)tcsetpgrp(STDOUT_FILENO, man_pgid); 1270*6d38604fSBaptiste Daroussin if (signum == SIGTTIN) 1271*6d38604fSBaptiste Daroussin continue; 1272*6d38604fSBaptiste Daroussin } else 1273*6d38604fSBaptiste Daroussin outst->tag_files->tcpgid = tc_pgid; 1274*6d38604fSBaptiste Daroussin kill(0, signum); 1275*6d38604fSBaptiste Daroussin continue; 1276*6d38604fSBaptiste Daroussin } 1277*6d38604fSBaptiste Daroussin 1278*6d38604fSBaptiste Daroussin /* Once in the foreground, activate the pager. */ 1279*6d38604fSBaptiste Daroussin 1280*6d38604fSBaptiste Daroussin if (pager_pid) { 1281*6d38604fSBaptiste Daroussin (void)tcsetpgrp(STDOUT_FILENO, pager_pid); 1282*6d38604fSBaptiste Daroussin kill(pager_pid, SIGCONT); 1283*6d38604fSBaptiste Daroussin } else 1284*6d38604fSBaptiste Daroussin pager_pid = spawn_pager(outst, tag_target); 1285*6d38604fSBaptiste Daroussin 1286*6d38604fSBaptiste Daroussin /* Wait for the pager to stop or exit. */ 1287*6d38604fSBaptiste Daroussin 1288*6d38604fSBaptiste Daroussin while ((wait_pid = waitpid(pager_pid, &status, 1289*6d38604fSBaptiste Daroussin WUNTRACED)) == -1 && errno == EINTR) 1290*6d38604fSBaptiste Daroussin continue; 1291*6d38604fSBaptiste Daroussin 1292*6d38604fSBaptiste Daroussin if (wait_pid == -1) { 1293*6d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_WAIT, 0, 0, 1294*6d38604fSBaptiste Daroussin "%s", strerror(errno)); 1295*6d38604fSBaptiste Daroussin break; 1296*6d38604fSBaptiste Daroussin } 1297*6d38604fSBaptiste Daroussin if (!WIFSTOPPED(status)) 1298*6d38604fSBaptiste Daroussin break; 1299*6d38604fSBaptiste Daroussin 1300*6d38604fSBaptiste Daroussin signum = WSTOPSIG(status); 1301*6d38604fSBaptiste Daroussin } 1302*6d38604fSBaptiste Daroussin } 1303*6d38604fSBaptiste Daroussin 130461d06d6bSBaptiste Daroussin static pid_t 1305*6d38604fSBaptiste Daroussin spawn_pager(struct outstate *outst, char *tag_target) 130661d06d6bSBaptiste Daroussin { 130761d06d6bSBaptiste Daroussin const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ 130861d06d6bSBaptiste Daroussin #define MAX_PAGER_ARGS 16 130961d06d6bSBaptiste Daroussin char *argv[MAX_PAGER_ARGS]; 131061d06d6bSBaptiste Daroussin const char *pager; 131161d06d6bSBaptiste Daroussin char *cp; 13127295610fSBaptiste Daroussin #if HAVE_LESS_T 131361d06d6bSBaptiste Daroussin size_t cmdlen; 13147295610fSBaptiste Daroussin #endif 13157295610fSBaptiste Daroussin int argc, use_ofn; 131661d06d6bSBaptiste Daroussin pid_t pager_pid; 131761d06d6bSBaptiste Daroussin 1318*6d38604fSBaptiste Daroussin assert(outst->tag_files->ofd == -1); 1319*6d38604fSBaptiste Daroussin assert(outst->tag_files->tfs == NULL); 1320*6d38604fSBaptiste Daroussin 132161d06d6bSBaptiste Daroussin pager = getenv("MANPAGER"); 132261d06d6bSBaptiste Daroussin if (pager == NULL || *pager == '\0') 132361d06d6bSBaptiste Daroussin pager = getenv("PAGER"); 132461d06d6bSBaptiste Daroussin if (pager == NULL || *pager == '\0') 1325*6d38604fSBaptiste Daroussin pager = BINM_PAGER; 132661d06d6bSBaptiste Daroussin cp = mandoc_strdup(pager); 132761d06d6bSBaptiste Daroussin 132861d06d6bSBaptiste Daroussin /* 132961d06d6bSBaptiste Daroussin * Parse the pager command into words. 133061d06d6bSBaptiste Daroussin * Intentionally do not do anything fancy here. 133161d06d6bSBaptiste Daroussin */ 133261d06d6bSBaptiste Daroussin 133361d06d6bSBaptiste Daroussin argc = 0; 13347295610fSBaptiste Daroussin while (argc + 5 < MAX_PAGER_ARGS) { 133561d06d6bSBaptiste Daroussin argv[argc++] = cp; 133661d06d6bSBaptiste Daroussin cp = strchr(cp, ' '); 133761d06d6bSBaptiste Daroussin if (cp == NULL) 133861d06d6bSBaptiste Daroussin break; 133961d06d6bSBaptiste Daroussin *cp++ = '\0'; 134061d06d6bSBaptiste Daroussin while (*cp == ' ') 134161d06d6bSBaptiste Daroussin cp++; 134261d06d6bSBaptiste Daroussin if (*cp == '\0') 134361d06d6bSBaptiste Daroussin break; 134461d06d6bSBaptiste Daroussin } 134561d06d6bSBaptiste Daroussin 134661d06d6bSBaptiste Daroussin /* For less(1), use the tag file. */ 134761d06d6bSBaptiste Daroussin 13487295610fSBaptiste Daroussin use_ofn = 1; 13497295610fSBaptiste Daroussin #if HAVE_LESS_T 1350*6d38604fSBaptiste Daroussin if (*outst->tag_files->tfn != '\0' && 1351*6d38604fSBaptiste Daroussin (cmdlen = strlen(argv[0])) >= 4) { 135261d06d6bSBaptiste Daroussin cp = argv[0] + cmdlen - 4; 135361d06d6bSBaptiste Daroussin if (strcmp(cp, "less") == 0) { 135461d06d6bSBaptiste Daroussin argv[argc++] = mandoc_strdup("-T"); 1355*6d38604fSBaptiste Daroussin argv[argc++] = outst->tag_files->tfn; 1356*6d38604fSBaptiste Daroussin if (tag_target != NULL) { 13577295610fSBaptiste Daroussin argv[argc++] = mandoc_strdup("-t"); 1358*6d38604fSBaptiste Daroussin argv[argc++] = tag_target; 13597295610fSBaptiste Daroussin use_ofn = 0; 136061d06d6bSBaptiste Daroussin } 136161d06d6bSBaptiste Daroussin } 13627295610fSBaptiste Daroussin } 13637295610fSBaptiste Daroussin #endif 1364*6d38604fSBaptiste Daroussin if (use_ofn) { 1365*6d38604fSBaptiste Daroussin if (outst->outtype == OUTT_HTML && tag_target != NULL) 1366*6d38604fSBaptiste Daroussin mandoc_asprintf(&argv[argc], "file://%s#%s", 1367*6d38604fSBaptiste Daroussin outst->tag_files->ofn, tag_target); 1368*6d38604fSBaptiste Daroussin else 1369*6d38604fSBaptiste Daroussin argv[argc] = outst->tag_files->ofn; 1370*6d38604fSBaptiste Daroussin argc++; 1371*6d38604fSBaptiste Daroussin } 137261d06d6bSBaptiste Daroussin argv[argc] = NULL; 137361d06d6bSBaptiste Daroussin 137461d06d6bSBaptiste Daroussin switch (pager_pid = fork()) { 137561d06d6bSBaptiste Daroussin case -1: 137645a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FORK, 0, 0, "%s", strerror(errno)); 137745a5aec3SBaptiste Daroussin exit(mandoc_msg_getrc()); 137861d06d6bSBaptiste Daroussin case 0: 137961d06d6bSBaptiste Daroussin break; 138061d06d6bSBaptiste Daroussin default: 138161d06d6bSBaptiste Daroussin (void)setpgid(pager_pid, 0); 1382*6d38604fSBaptiste Daroussin (void)tcsetpgrp(STDOUT_FILENO, pager_pid); 138361d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 138445a5aec3SBaptiste Daroussin if (pledge("stdio rpath tmppath tty proc", NULL) == -1) { 138545a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, 138645a5aec3SBaptiste Daroussin "%s", strerror(errno)); 138745a5aec3SBaptiste Daroussin exit(mandoc_msg_getrc()); 138845a5aec3SBaptiste Daroussin } 138961d06d6bSBaptiste Daroussin #endif 1390*6d38604fSBaptiste Daroussin outst->tag_files->pager_pid = pager_pid; 139161d06d6bSBaptiste Daroussin return pager_pid; 139261d06d6bSBaptiste Daroussin } 139361d06d6bSBaptiste Daroussin 1394*6d38604fSBaptiste Daroussin /* 1395*6d38604fSBaptiste Daroussin * The child process becomes the pager. 1396*6d38604fSBaptiste Daroussin * Do not start it before controlling the terminal. 1397*6d38604fSBaptiste Daroussin */ 139861d06d6bSBaptiste Daroussin 139961d06d6bSBaptiste Daroussin while (tcgetpgrp(STDOUT_FILENO) != getpid()) 140061d06d6bSBaptiste Daroussin nanosleep(&timeout, NULL); 140161d06d6bSBaptiste Daroussin 140261d06d6bSBaptiste Daroussin execvp(argv[0], argv); 140345a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", argv[0], strerror(errno)); 140445a5aec3SBaptiste Daroussin _exit(mandoc_msg_getrc()); 140561d06d6bSBaptiste Daroussin } 1406