1*c1c95addSBrooks Davis /* $Id: main.c,v 1.361 2022/04/14 16:43:43 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 36d38604fSBaptiste 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. 186d38604fSBaptiste Daroussin * 196d38604fSBaptiste 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> 376d38604fSBaptiste 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" 586d38604fSBaptiste 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 846d38604fSBaptiste Daroussin struct outstate { 856d38604fSBaptiste Daroussin struct tag_files *tag_files; /* Tagging state variables. */ 8661d06d6bSBaptiste Daroussin void *outdata; /* data for output */ 876d38604fSBaptiste Daroussin int use_pager; 8861d06d6bSBaptiste Daroussin int wstop; /* stop after a file with a warning */ 896d38604fSBaptiste 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 966d38604fSBaptiste Daroussin static void check_xr(struct manpaths *); 976d38604fSBaptiste Daroussin static void fs_append(char **, size_t, int, 986d38604fSBaptiste Daroussin size_t, const char *, enum form, 996d38604fSBaptiste Daroussin struct manpage **, size_t *); 1006d38604fSBaptiste Daroussin static int fs_lookup(const struct manpaths *, size_t, 1016d38604fSBaptiste Daroussin const char *, const char *, const char *, 10261d06d6bSBaptiste Daroussin struct manpage **, size_t *); 10361d06d6bSBaptiste Daroussin static int fs_search(const struct mansearch *, 1046d38604fSBaptiste Daroussin const struct manpaths *, const char *, 10561d06d6bSBaptiste Daroussin struct manpage **, size_t *); 1066d38604fSBaptiste Daroussin static void glob_esc(char **, const char *, const char *); 1076d38604fSBaptiste Daroussin static void outdata_alloc(struct outstate *, struct manoutput *); 1086d38604fSBaptiste Daroussin static void parse(struct mparse *, int, const char *, 1096d38604fSBaptiste Daroussin struct outstate *, struct manconf *); 11045a5aec3SBaptiste Daroussin static void passthrough(int, int); 1116d38604fSBaptiste Daroussin static void process_onefile(struct mparse *, struct manpage *, 1126d38604fSBaptiste Daroussin int, struct outstate *, struct manconf *); 1136d38604fSBaptiste Daroussin static void run_pager(struct outstate *, char *); 1146d38604fSBaptiste Daroussin static pid_t spawn_pager(struct outstate *, char *); 11561d06d6bSBaptiste Daroussin static void usage(enum argmode) __attribute__((__noreturn__)); 1166d38604fSBaptiste 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 { 1266d38604fSBaptiste Daroussin struct manconf conf; /* Manpaths and output options. */ 1276d38604fSBaptiste Daroussin struct outstate outst; /* Output state. */ 1286d38604fSBaptiste Daroussin struct winsize ws; /* Result of ioctl(TIOCGWINSZ). */ 1296d38604fSBaptiste Daroussin struct mansearch search; /* Search options. */ 1306d38604fSBaptiste Daroussin struct manpage *res; /* Complete list of search results. */ 1316d38604fSBaptiste Daroussin struct manpage *resn; /* Search results for one name. */ 1326d38604fSBaptiste Daroussin struct mparse *mp; /* Opaque parser object. */ 1336d38604fSBaptiste Daroussin const char *conf_file; /* -C: alternate config file. */ 1346d38604fSBaptiste Daroussin const char *os_s; /* -I: Operating system for display. */ 1356d38604fSBaptiste Daroussin const char *progname, *sec, *ep; 1366d38604fSBaptiste Daroussin char *defpaths; /* -M: override manpaths. */ 1376d38604fSBaptiste Daroussin char *auxpaths; /* -m: additional manpaths. */ 1386d38604fSBaptiste Daroussin char *oarg; /* -O: output option string. */ 1396d38604fSBaptiste Daroussin char *tagarg; /* -O tag: default value. */ 14061d06d6bSBaptiste Daroussin unsigned char *uc; 1416d38604fSBaptiste Daroussin size_t ressz; /* Number of elements in res[]. */ 1426d38604fSBaptiste Daroussin size_t resnsz; /* Number of elements in resn[]. */ 1436d38604fSBaptiste Daroussin size_t i, ib, ssz; 1446d38604fSBaptiste Daroussin int options; /* Parser options. */ 1456d38604fSBaptiste Daroussin int show_usage; /* Invalid argument: give up. */ 14661d06d6bSBaptiste Daroussin int prio, best_prio; 1476d38604fSBaptiste Daroussin int startdir; 14861d06d6bSBaptiste Daroussin int c; 1496d38604fSBaptiste Daroussin enum mandoc_os os_e; /* Check base system conventions. */ 1506d38604fSBaptiste Daroussin enum outmode outmode; /* According to command line. */ 15161d06d6bSBaptiste Daroussin 152*c1c95addSBrooks Davis #if DEBUG_MEMORY 153*c1c95addSBrooks Davis mandoc_dbg_init(argc, argv); 154*c1c95addSBrooks Davis #endif 15561d06d6bSBaptiste Daroussin #if HAVE_PROGNAME 15661d06d6bSBaptiste Daroussin progname = getprogname(); 15761d06d6bSBaptiste Daroussin #else 15861d06d6bSBaptiste Daroussin if (argc < 1) 159*c1c95addSBrooks Davis progname = "mandoc"; 16061d06d6bSBaptiste Daroussin else if ((progname = strrchr(argv[0], '/')) == NULL) 16161d06d6bSBaptiste Daroussin progname = argv[0]; 16261d06d6bSBaptiste Daroussin else 16361d06d6bSBaptiste Daroussin ++progname; 16461d06d6bSBaptiste Daroussin setprogname(progname); 16561d06d6bSBaptiste Daroussin #endif 16661d06d6bSBaptiste Daroussin 1677295610fSBaptiste Daroussin mandoc_msg_setoutfile(stderr); 16861d06d6bSBaptiste Daroussin if (strncmp(progname, "mandocdb", 8) == 0 || 16961d06d6bSBaptiste Daroussin strcmp(progname, BINM_MAKEWHATIS) == 0) 17061d06d6bSBaptiste Daroussin return mandocdb(argc, argv); 17161d06d6bSBaptiste Daroussin 17261d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 1736d38604fSBaptiste Daroussin if (pledge("stdio rpath wpath cpath tmppath tty proc exec", NULL) == -1) { 17445a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); 17545a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 17645a5aec3SBaptiste Daroussin } 17761d06d6bSBaptiste Daroussin #endif 17861d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT 17961d06d6bSBaptiste Daroussin if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) 18061d06d6bSBaptiste Daroussin errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); 18161d06d6bSBaptiste Daroussin #endif 18261d06d6bSBaptiste Daroussin 18361d06d6bSBaptiste Daroussin /* Search options. */ 18461d06d6bSBaptiste Daroussin 18561d06d6bSBaptiste Daroussin memset(&conf, 0, sizeof(conf)); 1866d38604fSBaptiste Daroussin conf_file = NULL; 1876d38604fSBaptiste Daroussin defpaths = auxpaths = NULL; 18861d06d6bSBaptiste Daroussin 18961d06d6bSBaptiste Daroussin memset(&search, 0, sizeof(struct mansearch)); 19061d06d6bSBaptiste Daroussin search.outkey = "Nd"; 19161d06d6bSBaptiste Daroussin oarg = NULL; 19261d06d6bSBaptiste Daroussin 19361d06d6bSBaptiste Daroussin if (strcmp(progname, BINM_MAN) == 0) 19461d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 19561d06d6bSBaptiste Daroussin else if (strcmp(progname, BINM_APROPOS) == 0) 19661d06d6bSBaptiste Daroussin search.argmode = ARG_EXPR; 19761d06d6bSBaptiste Daroussin else if (strcmp(progname, BINM_WHATIS) == 0) 19861d06d6bSBaptiste Daroussin search.argmode = ARG_WORD; 19961d06d6bSBaptiste Daroussin else if (strncmp(progname, "help", 4) == 0) 20061d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 20161d06d6bSBaptiste Daroussin else 20261d06d6bSBaptiste Daroussin search.argmode = ARG_FILE; 20361d06d6bSBaptiste Daroussin 2046d38604fSBaptiste Daroussin /* Parser options. */ 20561d06d6bSBaptiste Daroussin 20661d06d6bSBaptiste Daroussin options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; 2076d38604fSBaptiste Daroussin os_e = MANDOC_OS_OTHER; 2086d38604fSBaptiste Daroussin os_s = NULL; 20961d06d6bSBaptiste Daroussin 2106d38604fSBaptiste Daroussin /* Formatter options. */ 2116d38604fSBaptiste Daroussin 2126d38604fSBaptiste Daroussin memset(&outst, 0, sizeof(outst)); 2136d38604fSBaptiste Daroussin outst.tag_files = NULL; 2146d38604fSBaptiste Daroussin outst.outtype = OUTT_LOCALE; 2156d38604fSBaptiste Daroussin outst.use_pager = 1; 2166d38604fSBaptiste Daroussin 21761d06d6bSBaptiste Daroussin show_usage = 0; 21861d06d6bSBaptiste Daroussin outmode = OUTMODE_DEF; 21961d06d6bSBaptiste Daroussin 22061d06d6bSBaptiste Daroussin while ((c = getopt(argc, argv, 22161d06d6bSBaptiste Daroussin "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) { 22261d06d6bSBaptiste Daroussin if (c == 'i' && search.argmode == ARG_EXPR) { 22361d06d6bSBaptiste Daroussin optind--; 22461d06d6bSBaptiste Daroussin break; 22561d06d6bSBaptiste Daroussin } 22661d06d6bSBaptiste Daroussin switch (c) { 22761d06d6bSBaptiste Daroussin case 'a': 22861d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 22961d06d6bSBaptiste Daroussin break; 23061d06d6bSBaptiste Daroussin case 'C': 23161d06d6bSBaptiste Daroussin conf_file = optarg; 23261d06d6bSBaptiste Daroussin break; 23361d06d6bSBaptiste Daroussin case 'c': 2346d38604fSBaptiste Daroussin outst.use_pager = 0; 23561d06d6bSBaptiste Daroussin break; 23661d06d6bSBaptiste Daroussin case 'f': 23761d06d6bSBaptiste Daroussin search.argmode = ARG_WORD; 23861d06d6bSBaptiste Daroussin break; 23961d06d6bSBaptiste Daroussin case 'h': 24061d06d6bSBaptiste Daroussin conf.output.synopsisonly = 1; 2416d38604fSBaptiste Daroussin outst.use_pager = 0; 24261d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 24361d06d6bSBaptiste Daroussin break; 24461d06d6bSBaptiste Daroussin case 'I': 24545a5aec3SBaptiste Daroussin if (strncmp(optarg, "os=", 3) != 0) { 24645a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, 24745a5aec3SBaptiste Daroussin "-I %s", optarg); 24845a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 24961d06d6bSBaptiste Daroussin } 2506d38604fSBaptiste Daroussin if (os_s != NULL) { 25145a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_DUPE, 0, 0, 25245a5aec3SBaptiste Daroussin "-I %s", optarg); 25345a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 25461d06d6bSBaptiste Daroussin } 2556d38604fSBaptiste Daroussin os_s = optarg + 3; 25661d06d6bSBaptiste Daroussin break; 25761d06d6bSBaptiste Daroussin case 'K': 25845a5aec3SBaptiste Daroussin options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); 25945a5aec3SBaptiste Daroussin if (strcmp(optarg, "utf-8") == 0) 26045a5aec3SBaptiste Daroussin options |= MPARSE_UTF8; 26145a5aec3SBaptiste Daroussin else if (strcmp(optarg, "iso-8859-1") == 0) 26245a5aec3SBaptiste Daroussin options |= MPARSE_LATIN1; 26345a5aec3SBaptiste Daroussin else if (strcmp(optarg, "us-ascii") != 0) { 26445a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, 26545a5aec3SBaptiste Daroussin "-K %s", optarg); 26645a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 26745a5aec3SBaptiste Daroussin } 26861d06d6bSBaptiste Daroussin break; 26961d06d6bSBaptiste Daroussin case 'k': 27061d06d6bSBaptiste Daroussin search.argmode = ARG_EXPR; 27161d06d6bSBaptiste Daroussin break; 27261d06d6bSBaptiste Daroussin case 'l': 27361d06d6bSBaptiste Daroussin search.argmode = ARG_FILE; 27461d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 27561d06d6bSBaptiste Daroussin break; 27661d06d6bSBaptiste Daroussin case 'M': 277a10034cbSYuri Pankov #ifdef __FreeBSD__ 278a10034cbSYuri Pankov defpaths = strdup(optarg); 279a10034cbSYuri Pankov if (defpaths == NULL) 280a10034cbSYuri Pankov err(1, "strdup"); 281a10034cbSYuri Pankov #else 28261d06d6bSBaptiste Daroussin defpaths = optarg; 283a10034cbSYuri Pankov #endif 28461d06d6bSBaptiste Daroussin break; 28561d06d6bSBaptiste Daroussin case 'm': 28661d06d6bSBaptiste Daroussin auxpaths = optarg; 28761d06d6bSBaptiste Daroussin break; 28861d06d6bSBaptiste Daroussin case 'O': 28961d06d6bSBaptiste Daroussin oarg = optarg; 29061d06d6bSBaptiste Daroussin break; 29161d06d6bSBaptiste Daroussin case 'S': 29261d06d6bSBaptiste Daroussin search.arch = optarg; 29361d06d6bSBaptiste Daroussin break; 29461d06d6bSBaptiste Daroussin case 's': 29561d06d6bSBaptiste Daroussin search.sec = optarg; 29661d06d6bSBaptiste Daroussin break; 29761d06d6bSBaptiste Daroussin case 'T': 29845a5aec3SBaptiste Daroussin if (strcmp(optarg, "ascii") == 0) 2996d38604fSBaptiste Daroussin outst.outtype = OUTT_ASCII; 30045a5aec3SBaptiste Daroussin else if (strcmp(optarg, "lint") == 0) { 3016d38604fSBaptiste Daroussin outst.outtype = OUTT_LINT; 30245a5aec3SBaptiste Daroussin mandoc_msg_setoutfile(stdout); 30345a5aec3SBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 30445a5aec3SBaptiste Daroussin } else if (strcmp(optarg, "tree") == 0) 3056d38604fSBaptiste Daroussin outst.outtype = OUTT_TREE; 30645a5aec3SBaptiste Daroussin else if (strcmp(optarg, "man") == 0) 3076d38604fSBaptiste Daroussin outst.outtype = OUTT_MAN; 30845a5aec3SBaptiste Daroussin else if (strcmp(optarg, "html") == 0) 3096d38604fSBaptiste Daroussin outst.outtype = OUTT_HTML; 31045a5aec3SBaptiste Daroussin else if (strcmp(optarg, "markdown") == 0) 3116d38604fSBaptiste Daroussin outst.outtype = OUTT_MARKDOWN; 31245a5aec3SBaptiste Daroussin else if (strcmp(optarg, "utf8") == 0) 3136d38604fSBaptiste Daroussin outst.outtype = OUTT_UTF8; 31445a5aec3SBaptiste Daroussin else if (strcmp(optarg, "locale") == 0) 3156d38604fSBaptiste Daroussin outst.outtype = OUTT_LOCALE; 31645a5aec3SBaptiste Daroussin else if (strcmp(optarg, "ps") == 0) 3176d38604fSBaptiste Daroussin outst.outtype = OUTT_PS; 31845a5aec3SBaptiste Daroussin else if (strcmp(optarg, "pdf") == 0) 3196d38604fSBaptiste Daroussin outst.outtype = OUTT_PDF; 32045a5aec3SBaptiste Daroussin else { 32145a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, 32245a5aec3SBaptiste Daroussin "-T %s", optarg); 32345a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 32445a5aec3SBaptiste Daroussin } 32561d06d6bSBaptiste Daroussin break; 32661d06d6bSBaptiste Daroussin case 'W': 3276d38604fSBaptiste Daroussin if (woptions(optarg, &os_e, &outst.wstop) == -1) 32845a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 32961d06d6bSBaptiste Daroussin break; 33061d06d6bSBaptiste Daroussin case 'w': 33161d06d6bSBaptiste Daroussin outmode = OUTMODE_FLN; 33261d06d6bSBaptiste Daroussin break; 33361d06d6bSBaptiste Daroussin default: 33461d06d6bSBaptiste Daroussin show_usage = 1; 33561d06d6bSBaptiste Daroussin break; 33661d06d6bSBaptiste Daroussin } 33761d06d6bSBaptiste Daroussin } 33861d06d6bSBaptiste Daroussin 33961d06d6bSBaptiste Daroussin if (show_usage) 34061d06d6bSBaptiste Daroussin usage(search.argmode); 34161d06d6bSBaptiste Daroussin 34261d06d6bSBaptiste Daroussin /* Postprocess options. */ 34361d06d6bSBaptiste Daroussin 3446d38604fSBaptiste Daroussin switch (outmode) { 3456d38604fSBaptiste Daroussin case OUTMODE_DEF: 34661d06d6bSBaptiste Daroussin switch (search.argmode) { 34761d06d6bSBaptiste Daroussin case ARG_FILE: 34861d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 3496d38604fSBaptiste Daroussin outst.use_pager = 0; 35061d06d6bSBaptiste Daroussin break; 35161d06d6bSBaptiste Daroussin case ARG_NAME: 35261d06d6bSBaptiste Daroussin outmode = OUTMODE_ONE; 35361d06d6bSBaptiste Daroussin break; 35461d06d6bSBaptiste Daroussin default: 35561d06d6bSBaptiste Daroussin outmode = OUTMODE_LST; 35661d06d6bSBaptiste Daroussin break; 35761d06d6bSBaptiste Daroussin } 3586d38604fSBaptiste Daroussin break; 3596d38604fSBaptiste Daroussin case OUTMODE_FLN: 3606d38604fSBaptiste Daroussin if (search.argmode == ARG_FILE) 3616d38604fSBaptiste Daroussin outmode = OUTMODE_ALL; 3626d38604fSBaptiste Daroussin break; 3636d38604fSBaptiste Daroussin case OUTMODE_ALL: 3646d38604fSBaptiste Daroussin break; 3656d38604fSBaptiste Daroussin case OUTMODE_LST: 3666d38604fSBaptiste Daroussin case OUTMODE_ONE: 3676d38604fSBaptiste Daroussin abort(); 36861d06d6bSBaptiste Daroussin } 36961d06d6bSBaptiste Daroussin 37061d06d6bSBaptiste Daroussin if (oarg != NULL) { 37161d06d6bSBaptiste Daroussin if (outmode == OUTMODE_LST) 37261d06d6bSBaptiste Daroussin search.outkey = oarg; 37361d06d6bSBaptiste Daroussin else { 37461d06d6bSBaptiste Daroussin while (oarg != NULL) { 37561d06d6bSBaptiste Daroussin if (manconf_output(&conf.output, 37645a5aec3SBaptiste Daroussin strsep(&oarg, ","), 0) == -1) 37745a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 37861d06d6bSBaptiste Daroussin } 37961d06d6bSBaptiste Daroussin } 38061d06d6bSBaptiste Daroussin } 38161d06d6bSBaptiste Daroussin 3826d38604fSBaptiste Daroussin if (outst.outtype != OUTT_TREE || conf.output.noval == 0) 3837295610fSBaptiste Daroussin options |= MPARSE_VALIDATE; 3847295610fSBaptiste Daroussin 38561d06d6bSBaptiste Daroussin if (outmode == OUTMODE_FLN || 38661d06d6bSBaptiste Daroussin outmode == OUTMODE_LST || 3876d38604fSBaptiste Daroussin (conf.output.outfilename == NULL && 3886d38604fSBaptiste Daroussin conf.output.tagfilename == NULL && 3896d38604fSBaptiste Daroussin isatty(STDOUT_FILENO) == 0)) 3906d38604fSBaptiste Daroussin outst.use_pager = 0; 39161d06d6bSBaptiste Daroussin 3926d38604fSBaptiste Daroussin if (outst.use_pager && 39361d06d6bSBaptiste Daroussin (conf.output.width == 0 || conf.output.indent == 0) && 39461d06d6bSBaptiste Daroussin ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && 39561d06d6bSBaptiste Daroussin ws.ws_col > 1) { 39661d06d6bSBaptiste Daroussin if (conf.output.width == 0 && ws.ws_col < 79) 39761d06d6bSBaptiste Daroussin conf.output.width = ws.ws_col - 1; 39861d06d6bSBaptiste Daroussin if (conf.output.indent == 0 && ws.ws_col < 66) 39961d06d6bSBaptiste Daroussin conf.output.indent = 3; 40061d06d6bSBaptiste Daroussin } 40161d06d6bSBaptiste Daroussin 40261d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 4036d38604fSBaptiste Daroussin if (outst.use_pager == 0) 4046d38604fSBaptiste Daroussin c = pledge("stdio rpath", NULL); 4056d38604fSBaptiste Daroussin else if (conf.output.outfilename != NULL || 4066d38604fSBaptiste Daroussin conf.output.tagfilename != NULL) 4076d38604fSBaptiste Daroussin c = pledge("stdio rpath wpath cpath", NULL); 4086d38604fSBaptiste Daroussin else 4096d38604fSBaptiste Daroussin c = pledge("stdio rpath tmppath tty proc exec", NULL); 4106d38604fSBaptiste Daroussin if (c == -1) { 4116d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); 41245a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 41345a5aec3SBaptiste Daroussin } 41461d06d6bSBaptiste Daroussin #endif 41561d06d6bSBaptiste Daroussin 41661d06d6bSBaptiste Daroussin /* Parse arguments. */ 41761d06d6bSBaptiste Daroussin 41861d06d6bSBaptiste Daroussin if (argc > 0) { 41961d06d6bSBaptiste Daroussin argc -= optind; 42061d06d6bSBaptiste Daroussin argv += optind; 42161d06d6bSBaptiste Daroussin } 42261d06d6bSBaptiste Daroussin 42361d06d6bSBaptiste Daroussin /* 4246d38604fSBaptiste Daroussin * Quirks for help(1) and man(1), 4256d38604fSBaptiste Daroussin * in particular for a section argument without -s. 42661d06d6bSBaptiste Daroussin */ 42761d06d6bSBaptiste Daroussin 42861d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME) { 42961d06d6bSBaptiste Daroussin if (*progname == 'h') { 43061d06d6bSBaptiste Daroussin if (argc == 0) { 43161d06d6bSBaptiste Daroussin argv = help_argv; 43261d06d6bSBaptiste Daroussin argc = 1; 43361d06d6bSBaptiste Daroussin } 43461d06d6bSBaptiste Daroussin } else if (argc > 1 && 43561d06d6bSBaptiste Daroussin ((uc = (unsigned char *)argv[0]) != NULL) && 43661d06d6bSBaptiste Daroussin ((isdigit(uc[0]) && (uc[1] == '\0' || 43745a5aec3SBaptiste Daroussin isalpha(uc[1]))) || 43861d06d6bSBaptiste Daroussin (uc[0] == 'n' && uc[1] == '\0'))) { 43961d06d6bSBaptiste Daroussin search.sec = (char *)uc; 44061d06d6bSBaptiste Daroussin argv++; 44161d06d6bSBaptiste Daroussin argc--; 44261d06d6bSBaptiste Daroussin } 44361d06d6bSBaptiste Daroussin if (search.arch == NULL) 44461d06d6bSBaptiste Daroussin search.arch = getenv("MACHINE"); 44561d06d6bSBaptiste Daroussin #ifdef MACHINE 44661d06d6bSBaptiste Daroussin if (search.arch == NULL) 44761d06d6bSBaptiste Daroussin search.arch = MACHINE; 44861d06d6bSBaptiste Daroussin #endif 4496d38604fSBaptiste Daroussin if (outmode == OUTMODE_ONE) 4506d38604fSBaptiste Daroussin search.firstmatch = 1; 45161d06d6bSBaptiste Daroussin } 45261d06d6bSBaptiste Daroussin 4537295610fSBaptiste Daroussin /* 4547295610fSBaptiste Daroussin * Use the first argument for -O tag in addition to 4557295610fSBaptiste Daroussin * using it as a search term for man(1) or apropos(1). 4567295610fSBaptiste Daroussin */ 4577295610fSBaptiste Daroussin 4587295610fSBaptiste Daroussin if (conf.output.tag != NULL && *conf.output.tag == '\0') { 4597295610fSBaptiste Daroussin tagarg = argc > 0 && search.argmode == ARG_EXPR ? 4607295610fSBaptiste Daroussin strchr(*argv, '=') : NULL; 4617295610fSBaptiste Daroussin conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; 4627295610fSBaptiste Daroussin } 46361d06d6bSBaptiste Daroussin 4646d38604fSBaptiste Daroussin if (search.argmode != ARG_FILE || 4656d38604fSBaptiste Daroussin mandoc_msg_getmin() == MANDOCERR_STYLE) 466a10034cbSYuri Pankov #ifdef __FreeBSD__ 4676d38604fSBaptiste Daroussin { 468a10034cbSYuri Pankov /* 469a10034cbSYuri Pankov * Use manpath(1) to populate defpaths if -M is not specified. 470a10034cbSYuri Pankov * Don't treat any failures as fatal. 471a10034cbSYuri Pankov */ 472a10034cbSYuri Pankov if (defpaths == NULL) { 473a10034cbSYuri Pankov FILE *fp; 474a10034cbSYuri Pankov size_t linecap = 0; 475a10034cbSYuri Pankov ssize_t linelen; 476a10034cbSYuri Pankov 477a10034cbSYuri Pankov if ((fp = popen("/usr/bin/manpath -q", "r")) != NULL) { 478a10034cbSYuri Pankov if ((linelen = getline(&defpaths, 479a10034cbSYuri Pankov &linecap, fp)) > 0) { 480a10034cbSYuri Pankov /* Strip trailing newline */ 481a10034cbSYuri Pankov defpaths[linelen - 1] = '\0'; 482a10034cbSYuri Pankov } 483a10034cbSYuri Pankov pclose(fp); 484a10034cbSYuri Pankov } 485a10034cbSYuri Pankov } 486a10034cbSYuri Pankov #endif 4876d38604fSBaptiste Daroussin /* Read the configuration file. */ 48861d06d6bSBaptiste Daroussin manconf_parse(&conf, conf_file, defpaths, auxpaths); 489a10034cbSYuri Pankov #ifdef __FreeBSD__ 490a10034cbSYuri Pankov free(defpaths); 4916d38604fSBaptiste Daroussin } 492a10034cbSYuri Pankov #endif 493a10034cbSYuri Pankov 4946d38604fSBaptiste Daroussin /* man(1): Resolve each name individually. */ 49561d06d6bSBaptiste Daroussin 49661d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME) { 4976d38604fSBaptiste Daroussin if (argc < 1) { 4986d38604fSBaptiste Daroussin if (outmode != OUTMODE_FLN) 4996d38604fSBaptiste Daroussin usage(ARG_NAME); 5006d38604fSBaptiste Daroussin if (conf.manpath.sz == 0) { 5016d38604fSBaptiste Daroussin warnx("The manpath is empty."); 5026d38604fSBaptiste Daroussin mandoc_msg_setrc(MANDOCLEVEL_BADARG); 5036d38604fSBaptiste Daroussin } else { 5046d38604fSBaptiste Daroussin for (i = 0; i + 1 < conf.manpath.sz; i++) 5056d38604fSBaptiste Daroussin printf("%s:", conf.manpath.paths[i]); 5066d38604fSBaptiste Daroussin printf("%s\n", conf.manpath.paths[i]); 5076d38604fSBaptiste Daroussin } 5086d38604fSBaptiste Daroussin manconf_free(&conf); 5096d38604fSBaptiste Daroussin return (int)mandoc_msg_getrc(); 5106d38604fSBaptiste Daroussin } 5116d38604fSBaptiste Daroussin for (res = NULL, ressz = 0; argc > 0; argc--, argv++) { 5126d38604fSBaptiste Daroussin (void)mansearch(&search, &conf.manpath, 5136d38604fSBaptiste Daroussin 1, argv, &resn, &resnsz); 5146d38604fSBaptiste Daroussin if (resnsz == 0) 5156d38604fSBaptiste Daroussin (void)fs_search(&search, &conf.manpath, 5166d38604fSBaptiste Daroussin *argv, &resn, &resnsz); 5176d38604fSBaptiste Daroussin if (resnsz == 0 && strchr(*argv, '/') == NULL) { 5186d38604fSBaptiste Daroussin if (search.arch != NULL && 5196d38604fSBaptiste Daroussin arch_valid(search.arch, OSENUM) == 0) 5206d38604fSBaptiste Daroussin warnx("Unknown architecture \"%s\".", 5216d38604fSBaptiste Daroussin search.arch); 5226d38604fSBaptiste Daroussin else if (search.sec != NULL) 5236d38604fSBaptiste Daroussin warnx("No entry for %s in " 5246d38604fSBaptiste Daroussin "section %s of the manual.", 5256d38604fSBaptiste Daroussin *argv, search.sec); 5266d38604fSBaptiste Daroussin else 5276d38604fSBaptiste Daroussin warnx("No entry for %s in " 5286d38604fSBaptiste Daroussin "the manual.", *argv); 5296d38604fSBaptiste Daroussin mandoc_msg_setrc(MANDOCLEVEL_BADARG); 53061d06d6bSBaptiste Daroussin continue; 5316d38604fSBaptiste Daroussin } 5326d38604fSBaptiste Daroussin if (resnsz == 0) { 5336d38604fSBaptiste Daroussin if (access(*argv, R_OK) == -1) { 5346d38604fSBaptiste Daroussin mandoc_msg_setinfilename(*argv); 53545a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 53645a5aec3SBaptiste Daroussin 0, 0, "%s", strerror(errno)); 53745a5aec3SBaptiste Daroussin mandoc_msg_setinfilename(NULL); 53861d06d6bSBaptiste Daroussin continue; 53961d06d6bSBaptiste Daroussin } 5406d38604fSBaptiste Daroussin resnsz = 1; 5416d38604fSBaptiste Daroussin resn = mandoc_calloc(resnsz, sizeof(*res)); 5426d38604fSBaptiste Daroussin resn->file = mandoc_strdup(*argv); 5436d38604fSBaptiste Daroussin resn->ipath = SIZE_MAX; 5446d38604fSBaptiste Daroussin resn->form = FORM_SRC; 5456d38604fSBaptiste Daroussin } 5466d38604fSBaptiste Daroussin if (outmode != OUTMODE_ONE || resnsz == 1) { 54761d06d6bSBaptiste Daroussin res = mandoc_reallocarray(res, 5486d38604fSBaptiste Daroussin ressz + resnsz, sizeof(*res)); 5496d38604fSBaptiste Daroussin memcpy(res + ressz, resn, 5506d38604fSBaptiste Daroussin sizeof(*resn) * resnsz); 5516d38604fSBaptiste Daroussin ressz += resnsz; 552*c1c95addSBrooks Davis free(resn); 553*c1c95addSBrooks Davis resn = NULL; 554*c1c95addSBrooks Davis resnsz = 0; 5556d38604fSBaptiste Daroussin continue; 55661d06d6bSBaptiste Daroussin } 55761d06d6bSBaptiste Daroussin 55861d06d6bSBaptiste Daroussin /* Search for the best section. */ 5596d38604fSBaptiste Daroussin 5606d38604fSBaptiste Daroussin best_prio = 40; 5616d38604fSBaptiste Daroussin for (ib = i = 0; i < resnsz; i++) { 5626d38604fSBaptiste Daroussin sec = resn[i].file; 56361d06d6bSBaptiste Daroussin sec += strcspn(sec, "123456789"); 56461d06d6bSBaptiste Daroussin if (sec[0] == '\0') 56545a5aec3SBaptiste Daroussin continue; /* No section at all. */ 56661d06d6bSBaptiste Daroussin prio = sec_prios[sec[0] - '1']; 56745a5aec3SBaptiste Daroussin if (search.sec != NULL) { 56845a5aec3SBaptiste Daroussin ssz = strlen(search.sec); 56945a5aec3SBaptiste Daroussin if (strncmp(sec, search.sec, ssz) == 0) 57045a5aec3SBaptiste Daroussin sec += ssz; 57145a5aec3SBaptiste Daroussin } else 57245a5aec3SBaptiste Daroussin sec++; /* Prefer without suffix. */ 57345a5aec3SBaptiste Daroussin if (*sec != '/') 57445a5aec3SBaptiste Daroussin prio += 10; /* Wrong dir name. */ 5756d38604fSBaptiste Daroussin if (search.sec != NULL) { 5766d38604fSBaptiste Daroussin ep = strchr(sec, '\0'); 5776d38604fSBaptiste Daroussin if (ep - sec > 3 && 5786d38604fSBaptiste Daroussin strncmp(ep - 3, ".gz", 3) == 0) 5796d38604fSBaptiste Daroussin ep -= 3; 5806d38604fSBaptiste Daroussin if ((size_t)(ep - sec) < ssz + 3 || 5816d38604fSBaptiste Daroussin strncmp(ep - ssz, search.sec, 5826d38604fSBaptiste Daroussin ssz) != 0) /* Wrong file */ 5836d38604fSBaptiste Daroussin prio += 20; /* extension. */ 5846d38604fSBaptiste Daroussin } 58561d06d6bSBaptiste Daroussin if (prio >= best_prio) 58661d06d6bSBaptiste Daroussin continue; 58761d06d6bSBaptiste Daroussin best_prio = prio; 5886d38604fSBaptiste Daroussin ib = i; 58961d06d6bSBaptiste Daroussin } 5906d38604fSBaptiste Daroussin res = mandoc_reallocarray(res, ressz + 1, 5916d38604fSBaptiste Daroussin sizeof(*res)); 5926d38604fSBaptiste Daroussin memcpy(res + ressz++, resn + ib, sizeof(*resn)); 593*c1c95addSBrooks Davis memset(resn + ib, 0, sizeof(*resn)); 594*c1c95addSBrooks Davis mansearch_free(resn, resnsz); 595*c1c95addSBrooks Davis resn = NULL; 596*c1c95addSBrooks Davis resnsz = 0; 59761d06d6bSBaptiste Daroussin } 59861d06d6bSBaptiste Daroussin 5996d38604fSBaptiste Daroussin /* apropos(1), whatis(1): Process the full search expression. */ 60061d06d6bSBaptiste Daroussin 6016d38604fSBaptiste Daroussin } else if (search.argmode != ARG_FILE) { 6026d38604fSBaptiste Daroussin if (mansearch(&search, &conf.manpath, 6036d38604fSBaptiste Daroussin argc, argv, &res, &ressz) == 0) 6046d38604fSBaptiste Daroussin usage(search.argmode); 6056d38604fSBaptiste Daroussin 6066d38604fSBaptiste Daroussin if (ressz == 0) { 6076d38604fSBaptiste Daroussin warnx("nothing appropriate"); 6086d38604fSBaptiste Daroussin mandoc_msg_setrc(MANDOCLEVEL_BADARG); 60961d06d6bSBaptiste Daroussin goto out; 61061d06d6bSBaptiste Daroussin } 61161d06d6bSBaptiste Daroussin 6126d38604fSBaptiste Daroussin /* mandoc(1): Take command line arguments as file names. */ 61361d06d6bSBaptiste Daroussin 61461d06d6bSBaptiste Daroussin } else { 6156d38604fSBaptiste Daroussin ressz = argc > 0 ? argc : 1; 6166d38604fSBaptiste Daroussin res = mandoc_calloc(ressz, sizeof(*res)); 6176d38604fSBaptiste Daroussin for (i = 0; i < ressz; i++) { 6186d38604fSBaptiste Daroussin if (argc > 0) 6196d38604fSBaptiste Daroussin res[i].file = mandoc_strdup(argv[i]); 6206d38604fSBaptiste Daroussin res[i].ipath = SIZE_MAX; 6216d38604fSBaptiste Daroussin res[i].form = FORM_SRC; 62245a5aec3SBaptiste Daroussin } 62361d06d6bSBaptiste Daroussin } 6246d38604fSBaptiste Daroussin 6256d38604fSBaptiste Daroussin switch (outmode) { 6266d38604fSBaptiste Daroussin case OUTMODE_FLN: 6276d38604fSBaptiste Daroussin for (i = 0; i < ressz; i++) 6286d38604fSBaptiste Daroussin puts(res[i].file); 6296d38604fSBaptiste Daroussin goto out; 6306d38604fSBaptiste Daroussin case OUTMODE_LST: 6316d38604fSBaptiste Daroussin for (i = 0; i < ressz; i++) 6326d38604fSBaptiste Daroussin printf("%s - %s\n", res[i].names, 6336d38604fSBaptiste Daroussin res[i].output == NULL ? "" : 6346d38604fSBaptiste Daroussin res[i].output); 6356d38604fSBaptiste Daroussin goto out; 6366d38604fSBaptiste Daroussin default: 6376d38604fSBaptiste Daroussin break; 6386d38604fSBaptiste Daroussin } 63961d06d6bSBaptiste Daroussin 64045a5aec3SBaptiste Daroussin if (search.argmode == ARG_FILE && auxpaths != NULL) { 64145a5aec3SBaptiste Daroussin if (strcmp(auxpaths, "doc") == 0) 64245a5aec3SBaptiste Daroussin options |= MPARSE_MDOC; 64345a5aec3SBaptiste Daroussin else if (strcmp(auxpaths, "an") == 0) 64445a5aec3SBaptiste Daroussin options |= MPARSE_MAN; 64545a5aec3SBaptiste Daroussin } 64661d06d6bSBaptiste Daroussin 64761d06d6bSBaptiste Daroussin mchars_alloc(); 6486d38604fSBaptiste Daroussin mp = mparse_alloc(options, os_e, os_s); 64961d06d6bSBaptiste Daroussin 65061d06d6bSBaptiste Daroussin /* 65161d06d6bSBaptiste Daroussin * Remember the original working directory, if possible. 65261d06d6bSBaptiste Daroussin * This will be needed if some names on the command line 65361d06d6bSBaptiste Daroussin * are page names and some are relative file names. 65461d06d6bSBaptiste Daroussin * Do not error out if the current directory is not 65561d06d6bSBaptiste Daroussin * readable: Maybe it won't be needed after all. 65661d06d6bSBaptiste Daroussin */ 65761d06d6bSBaptiste Daroussin startdir = open(".", O_RDONLY | O_DIRECTORY); 6586d38604fSBaptiste Daroussin for (i = 0; i < ressz; i++) { 6596d38604fSBaptiste Daroussin process_onefile(mp, res + i, startdir, &outst, &conf); 6606d38604fSBaptiste Daroussin if (outst.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) 66161d06d6bSBaptiste Daroussin break; 66261d06d6bSBaptiste Daroussin } 66361d06d6bSBaptiste Daroussin if (startdir != -1) { 66461d06d6bSBaptiste Daroussin (void)fchdir(startdir); 66561d06d6bSBaptiste Daroussin close(startdir); 66661d06d6bSBaptiste Daroussin } 6676d38604fSBaptiste Daroussin if (conf.output.tag != NULL && conf.output.tag_found == 0) { 6686d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", conf.output.tag); 6696d38604fSBaptiste Daroussin conf.output.tag = NULL; 6706d38604fSBaptiste Daroussin } 6716d38604fSBaptiste Daroussin if (outst.outdata != NULL) { 6726d38604fSBaptiste Daroussin switch (outst.outtype) { 67361d06d6bSBaptiste Daroussin case OUTT_HTML: 6746d38604fSBaptiste Daroussin html_free(outst.outdata); 67561d06d6bSBaptiste Daroussin break; 67661d06d6bSBaptiste Daroussin case OUTT_UTF8: 67761d06d6bSBaptiste Daroussin case OUTT_LOCALE: 67861d06d6bSBaptiste Daroussin case OUTT_ASCII: 6796d38604fSBaptiste Daroussin ascii_free(outst.outdata); 68061d06d6bSBaptiste Daroussin break; 68161d06d6bSBaptiste Daroussin case OUTT_PDF: 68261d06d6bSBaptiste Daroussin case OUTT_PS: 6836d38604fSBaptiste Daroussin pspdf_free(outst.outdata); 68461d06d6bSBaptiste Daroussin break; 68561d06d6bSBaptiste Daroussin default: 68661d06d6bSBaptiste Daroussin break; 68761d06d6bSBaptiste Daroussin } 68861d06d6bSBaptiste Daroussin } 68961d06d6bSBaptiste Daroussin mandoc_xr_free(); 6906d38604fSBaptiste Daroussin mparse_free(mp); 69161d06d6bSBaptiste Daroussin mchars_free(); 69261d06d6bSBaptiste Daroussin 69361d06d6bSBaptiste Daroussin out: 6946d38604fSBaptiste Daroussin mansearch_free(res, ressz); 6956d38604fSBaptiste Daroussin if (search.argmode != ARG_FILE) 69661d06d6bSBaptiste Daroussin manconf_free(&conf); 69761d06d6bSBaptiste Daroussin 6986d38604fSBaptiste Daroussin if (outst.tag_files != NULL) { 6996d38604fSBaptiste Daroussin if (term_tag_close() != -1 && 7006d38604fSBaptiste Daroussin conf.output.outfilename == NULL && 7016d38604fSBaptiste Daroussin conf.output.tagfilename == NULL) 7026d38604fSBaptiste Daroussin run_pager(&outst, conf.output.tag); 7036d38604fSBaptiste Daroussin term_tag_unlink(); 7046d38604fSBaptiste Daroussin } else if (outst.had_output && outst.outtype != OUTT_LINT) 70545a5aec3SBaptiste Daroussin mandoc_msg_summary(); 70645a5aec3SBaptiste Daroussin 707*c1c95addSBrooks Davis #if DEBUG_MEMORY 708*c1c95addSBrooks Davis mandoc_dbg_finish(); 709*c1c95addSBrooks Davis #endif 7107295610fSBaptiste Daroussin return (int)mandoc_msg_getrc(); 71161d06d6bSBaptiste Daroussin } 71261d06d6bSBaptiste Daroussin 71361d06d6bSBaptiste Daroussin static void 71461d06d6bSBaptiste Daroussin usage(enum argmode argmode) 71561d06d6bSBaptiste Daroussin { 71661d06d6bSBaptiste Daroussin switch (argmode) { 71761d06d6bSBaptiste Daroussin case ARG_FILE: 71861d06d6bSBaptiste Daroussin fputs("usage: mandoc [-ac] [-I os=name] " 71961d06d6bSBaptiste Daroussin "[-K encoding] [-mdoc | -man] [-O options]\n" 72061d06d6bSBaptiste Daroussin "\t [-T output] [-W level] [file ...]\n", stderr); 72161d06d6bSBaptiste Daroussin break; 72261d06d6bSBaptiste Daroussin case ARG_NAME: 72361d06d6bSBaptiste Daroussin fputs("usage: man [-acfhklw] [-C file] [-M path] " 72461d06d6bSBaptiste Daroussin "[-m path] [-S subsection]\n" 72561d06d6bSBaptiste Daroussin "\t [[-s] section] name ...\n", stderr); 72661d06d6bSBaptiste Daroussin break; 72761d06d6bSBaptiste Daroussin case ARG_WORD: 72861d06d6bSBaptiste Daroussin fputs("usage: whatis [-afk] [-C file] " 72961d06d6bSBaptiste Daroussin "[-M path] [-m path] [-O outkey] [-S arch]\n" 73061d06d6bSBaptiste Daroussin "\t [-s section] name ...\n", stderr); 73161d06d6bSBaptiste Daroussin break; 73261d06d6bSBaptiste Daroussin case ARG_EXPR: 73361d06d6bSBaptiste Daroussin fputs("usage: apropos [-afk] [-C file] " 73461d06d6bSBaptiste Daroussin "[-M path] [-m path] [-O outkey] [-S arch]\n" 73561d06d6bSBaptiste Daroussin "\t [-s section] expression ...\n", stderr); 73661d06d6bSBaptiste Daroussin break; 73761d06d6bSBaptiste Daroussin } 73861d06d6bSBaptiste Daroussin exit((int)MANDOCLEVEL_BADARG); 73961d06d6bSBaptiste Daroussin } 74061d06d6bSBaptiste Daroussin 7416d38604fSBaptiste Daroussin static void 7426d38604fSBaptiste Daroussin glob_esc(char **dst, const char *src, const char *suffix) 7436d38604fSBaptiste Daroussin { 7446d38604fSBaptiste Daroussin while (*src != '\0') { 7456d38604fSBaptiste Daroussin if (strchr("*?[", *src) != NULL) 7466d38604fSBaptiste Daroussin *(*dst)++ = '\\'; 7476d38604fSBaptiste Daroussin *(*dst)++ = *src++; 7486d38604fSBaptiste Daroussin } 7496d38604fSBaptiste Daroussin while (*suffix != '\0') 7506d38604fSBaptiste Daroussin *(*dst)++ = *suffix++; 7516d38604fSBaptiste Daroussin } 7526d38604fSBaptiste Daroussin 7536d38604fSBaptiste Daroussin static void 7546d38604fSBaptiste Daroussin fs_append(char **file, size_t filesz, int copy, size_t ipath, 7556d38604fSBaptiste Daroussin const char *sec, enum form form, struct manpage **res, size_t *ressz) 7566d38604fSBaptiste Daroussin { 7576d38604fSBaptiste Daroussin struct manpage *page; 7586d38604fSBaptiste Daroussin 7596d38604fSBaptiste Daroussin *res = mandoc_reallocarray(*res, *ressz + filesz, sizeof(**res)); 7606d38604fSBaptiste Daroussin page = *res + *ressz; 7616d38604fSBaptiste Daroussin *ressz += filesz; 7626d38604fSBaptiste Daroussin for (;;) { 7636d38604fSBaptiste Daroussin page->file = copy ? mandoc_strdup(*file) : *file; 7646d38604fSBaptiste Daroussin page->names = NULL; 7656d38604fSBaptiste Daroussin page->output = NULL; 7666d38604fSBaptiste Daroussin page->bits = NAME_FILE & NAME_MASK; 7676d38604fSBaptiste Daroussin page->ipath = ipath; 7686d38604fSBaptiste Daroussin page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; 7696d38604fSBaptiste Daroussin page->form = form; 7706d38604fSBaptiste Daroussin if (--filesz == 0) 7716d38604fSBaptiste Daroussin break; 7726d38604fSBaptiste Daroussin file++; 7736d38604fSBaptiste Daroussin page++; 7746d38604fSBaptiste Daroussin } 7756d38604fSBaptiste Daroussin } 7766d38604fSBaptiste Daroussin 77761d06d6bSBaptiste Daroussin static int 77861d06d6bSBaptiste Daroussin fs_lookup(const struct manpaths *paths, size_t ipath, 77961d06d6bSBaptiste Daroussin const char *sec, const char *arch, const char *name, 78061d06d6bSBaptiste Daroussin struct manpage **res, size_t *ressz) 78161d06d6bSBaptiste Daroussin { 78245a5aec3SBaptiste Daroussin struct stat sb; 78361d06d6bSBaptiste Daroussin glob_t globinfo; 7846d38604fSBaptiste Daroussin char *file, *cp, secnum[2]; 78561d06d6bSBaptiste Daroussin int globres; 78661d06d6bSBaptiste Daroussin enum form form; 78761d06d6bSBaptiste Daroussin 7886d38604fSBaptiste Daroussin const char *const slman = "/man"; 7896d38604fSBaptiste Daroussin const char *const slash = "/"; 7906d38604fSBaptiste Daroussin const char *const sglob = ".[01-9]*"; 7916d38604fSBaptiste Daroussin const char *const dot = "."; 7926d38604fSBaptiste Daroussin const char *const aster = "*"; 7936d38604fSBaptiste Daroussin 7946d38604fSBaptiste Daroussin memset(&globinfo, 0, sizeof(globinfo)); 79561d06d6bSBaptiste Daroussin form = FORM_SRC; 7966d38604fSBaptiste Daroussin 79761d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s.%s", 79861d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name, sec); 79945a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) 80061d06d6bSBaptiste Daroussin goto found; 80161d06d6bSBaptiste Daroussin free(file); 80261d06d6bSBaptiste Daroussin 80361d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/cat%s/%s.0", 80461d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name); 80545a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) { 80661d06d6bSBaptiste Daroussin form = FORM_CAT; 80761d06d6bSBaptiste Daroussin goto found; 80861d06d6bSBaptiste Daroussin } 80961d06d6bSBaptiste Daroussin free(file); 81061d06d6bSBaptiste Daroussin 81161d06d6bSBaptiste Daroussin if (arch != NULL) { 81261d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", 81361d06d6bSBaptiste Daroussin paths->paths[ipath], sec, arch, name, sec); 81445a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) 81561d06d6bSBaptiste Daroussin goto found; 81661d06d6bSBaptiste Daroussin free(file); 81761d06d6bSBaptiste Daroussin } 81861d06d6bSBaptiste Daroussin 8196d38604fSBaptiste Daroussin cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 + 8206d38604fSBaptiste Daroussin strlen(slman) + strlen(sec) * 2 + strlen(slash) + 8216d38604fSBaptiste Daroussin strlen(name) * 2 + strlen(sglob) + 1); 8226d38604fSBaptiste Daroussin glob_esc(&cp, paths->paths[ipath], slman); 8236d38604fSBaptiste Daroussin glob_esc(&cp, sec, slash); 8246d38604fSBaptiste Daroussin glob_esc(&cp, name, sglob); 8256d38604fSBaptiste Daroussin *cp = '\0'; 82661d06d6bSBaptiste Daroussin globres = glob(file, 0, NULL, &globinfo); 82761d06d6bSBaptiste Daroussin if (globres != 0 && globres != GLOB_NOMATCH) 82845a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GLOB, 0, 0, 82945a5aec3SBaptiste Daroussin "%s: %s", file, strerror(errno)); 83061d06d6bSBaptiste Daroussin free(file); 8316d38604fSBaptiste Daroussin file = NULL; 83261d06d6bSBaptiste Daroussin if (globres == 0) 83361d06d6bSBaptiste Daroussin goto found; 8346d38604fSBaptiste Daroussin globfree(&globinfo); 8356d38604fSBaptiste Daroussin 8366d38604fSBaptiste Daroussin if (sec[1] != '\0' && *ressz == 0) { 8376d38604fSBaptiste Daroussin secnum[0] = sec[0]; 8386d38604fSBaptiste Daroussin secnum[1] = '\0'; 8396d38604fSBaptiste Daroussin cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 + 8406d38604fSBaptiste Daroussin strlen(slman) + strlen(secnum) * 2 + strlen(slash) + 8416d38604fSBaptiste Daroussin strlen(name) * 2 + strlen(dot) + 8426d38604fSBaptiste Daroussin strlen(sec) * 2 + strlen(aster) + 1); 8436d38604fSBaptiste Daroussin glob_esc(&cp, paths->paths[ipath], slman); 8446d38604fSBaptiste Daroussin glob_esc(&cp, secnum, slash); 8456d38604fSBaptiste Daroussin glob_esc(&cp, name, dot); 8466d38604fSBaptiste Daroussin glob_esc(&cp, sec, aster); 8476d38604fSBaptiste Daroussin *cp = '\0'; 8486d38604fSBaptiste Daroussin globres = glob(file, 0, NULL, &globinfo); 8496d38604fSBaptiste Daroussin if (globres != 0 && globres != GLOB_NOMATCH) 8506d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_GLOB, 0, 0, 8516d38604fSBaptiste Daroussin "%s: %s", file, strerror(errno)); 85245a5aec3SBaptiste Daroussin free(file); 8536d38604fSBaptiste Daroussin file = NULL; 8546d38604fSBaptiste Daroussin if (globres == 0) 8556d38604fSBaptiste Daroussin goto found; 8566d38604fSBaptiste Daroussin globfree(&globinfo); 85745a5aec3SBaptiste Daroussin } 8586d38604fSBaptiste Daroussin 85961d06d6bSBaptiste Daroussin if (res != NULL || ipath + 1 != paths->sz) 86045a5aec3SBaptiste Daroussin return -1; 86161d06d6bSBaptiste Daroussin 86261d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s.%s", name, sec); 86345a5aec3SBaptiste Daroussin globres = stat(file, &sb); 86461d06d6bSBaptiste Daroussin free(file); 86545a5aec3SBaptiste Daroussin return globres; 86661d06d6bSBaptiste Daroussin 86761d06d6bSBaptiste Daroussin found: 86861d06d6bSBaptiste Daroussin warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", 86961d06d6bSBaptiste Daroussin name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); 8706d38604fSBaptiste Daroussin if (res == NULL) 87161d06d6bSBaptiste Daroussin free(file); 8726d38604fSBaptiste Daroussin else if (file == NULL) 8736d38604fSBaptiste Daroussin fs_append(globinfo.gl_pathv, globinfo.gl_pathc, 1, 8746d38604fSBaptiste Daroussin ipath, sec, form, res, ressz); 8756d38604fSBaptiste Daroussin else 8766d38604fSBaptiste Daroussin fs_append(&file, 1, 0, ipath, sec, form, res, ressz); 8776d38604fSBaptiste Daroussin globfree(&globinfo); 87845a5aec3SBaptiste Daroussin return 0; 87961d06d6bSBaptiste Daroussin } 88061d06d6bSBaptiste Daroussin 88161d06d6bSBaptiste Daroussin static int 88261d06d6bSBaptiste Daroussin fs_search(const struct mansearch *cfg, const struct manpaths *paths, 8836d38604fSBaptiste Daroussin const char *name, struct manpage **res, size_t *ressz) 88461d06d6bSBaptiste Daroussin { 88561d06d6bSBaptiste Daroussin const char *const sections[] = 88661d06d6bSBaptiste Daroussin {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; 88761d06d6bSBaptiste Daroussin const size_t nsec = sizeof(sections)/sizeof(sections[0]); 88861d06d6bSBaptiste Daroussin 8896d38604fSBaptiste Daroussin size_t ipath, isec; 89061d06d6bSBaptiste Daroussin 89161d06d6bSBaptiste Daroussin assert(cfg->argmode == ARG_NAME); 89261d06d6bSBaptiste Daroussin if (res != NULL) 89361d06d6bSBaptiste Daroussin *res = NULL; 8946d38604fSBaptiste Daroussin *ressz = 0; 89561d06d6bSBaptiste Daroussin for (ipath = 0; ipath < paths->sz; ipath++) { 89661d06d6bSBaptiste Daroussin if (cfg->sec != NULL) { 8976d38604fSBaptiste Daroussin if (fs_lookup(paths, ipath, cfg->sec, cfg->arch, 8986d38604fSBaptiste Daroussin name, res, ressz) != -1 && cfg->firstmatch) 89945a5aec3SBaptiste Daroussin return 0; 9006d38604fSBaptiste Daroussin } else { 9016d38604fSBaptiste Daroussin for (isec = 0; isec < nsec; isec++) 90261d06d6bSBaptiste Daroussin if (fs_lookup(paths, ipath, sections[isec], 9036d38604fSBaptiste Daroussin cfg->arch, name, res, ressz) != -1 && 90461d06d6bSBaptiste Daroussin cfg->firstmatch) 90545a5aec3SBaptiste Daroussin return 0; 90661d06d6bSBaptiste Daroussin } 90761d06d6bSBaptiste Daroussin } 90845a5aec3SBaptiste Daroussin return -1; 90961d06d6bSBaptiste Daroussin } 91061d06d6bSBaptiste Daroussin 91161d06d6bSBaptiste Daroussin static void 9126d38604fSBaptiste Daroussin process_onefile(struct mparse *mp, struct manpage *resp, int startdir, 9136d38604fSBaptiste Daroussin struct outstate *outst, struct manconf *conf) 91461d06d6bSBaptiste Daroussin { 9156d38604fSBaptiste Daroussin int fd; 9166d38604fSBaptiste Daroussin 9176d38604fSBaptiste Daroussin /* 9186d38604fSBaptiste Daroussin * Changing directories is not needed in ARG_FILE mode. 9196d38604fSBaptiste Daroussin * Do it on a best-effort basis. Even in case of 9206d38604fSBaptiste Daroussin * failure, some functionality may still work. 9216d38604fSBaptiste Daroussin */ 9226d38604fSBaptiste Daroussin if (resp->ipath != SIZE_MAX) 9236d38604fSBaptiste Daroussin (void)chdir(conf->manpath.paths[resp->ipath]); 9246d38604fSBaptiste Daroussin else if (startdir != -1) 9256d38604fSBaptiste Daroussin (void)fchdir(startdir); 9266d38604fSBaptiste Daroussin 9276d38604fSBaptiste Daroussin mandoc_msg_setinfilename(resp->file); 9286d38604fSBaptiste Daroussin if (resp->file != NULL) { 9296d38604fSBaptiste Daroussin if ((fd = mparse_open(mp, resp->file)) == -1) { 9306d38604fSBaptiste Daroussin mandoc_msg(resp->ipath == SIZE_MAX ? 9316d38604fSBaptiste Daroussin MANDOCERR_BADARG_BAD : MANDOCERR_OPEN, 9326d38604fSBaptiste Daroussin 0, 0, "%s", strerror(errno)); 9336d38604fSBaptiste Daroussin mandoc_msg_setinfilename(NULL); 9346d38604fSBaptiste Daroussin return; 9356d38604fSBaptiste Daroussin } 9366d38604fSBaptiste Daroussin } else 9376d38604fSBaptiste Daroussin fd = STDIN_FILENO; 9386d38604fSBaptiste Daroussin 9396d38604fSBaptiste Daroussin if (outst->use_pager) { 9406d38604fSBaptiste Daroussin outst->use_pager = 0; 9416d38604fSBaptiste Daroussin outst->tag_files = term_tag_init(conf->output.outfilename, 9426d38604fSBaptiste Daroussin outst->outtype == OUTT_HTML ? ".html" : "", 9436d38604fSBaptiste Daroussin conf->output.tagfilename); 9446d38604fSBaptiste Daroussin #if HAVE_PLEDGE 9456d38604fSBaptiste Daroussin if ((conf->output.outfilename != NULL || 9466d38604fSBaptiste Daroussin conf->output.tagfilename != NULL) && 9476d38604fSBaptiste Daroussin pledge("stdio rpath cpath", NULL) == -1) { 9486d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, 9496d38604fSBaptiste Daroussin "%s", strerror(errno)); 9506d38604fSBaptiste Daroussin exit(mandoc_msg_getrc()); 9516d38604fSBaptiste Daroussin } 9526d38604fSBaptiste Daroussin #endif 9536d38604fSBaptiste Daroussin } 9546d38604fSBaptiste Daroussin if (outst->had_output && outst->outtype <= OUTT_UTF8) { 9556d38604fSBaptiste Daroussin if (outst->outdata == NULL) 9566d38604fSBaptiste Daroussin outdata_alloc(outst, &conf->output); 9576d38604fSBaptiste Daroussin terminal_sepline(outst->outdata); 9586d38604fSBaptiste Daroussin } 9596d38604fSBaptiste Daroussin 9606d38604fSBaptiste Daroussin if (resp->form == FORM_SRC) 9616d38604fSBaptiste Daroussin parse(mp, fd, resp->file, outst, conf); 9626d38604fSBaptiste Daroussin else { 9636d38604fSBaptiste Daroussin passthrough(fd, conf->output.synopsisonly); 9646d38604fSBaptiste Daroussin outst->had_output = 1; 9656d38604fSBaptiste Daroussin } 9666d38604fSBaptiste Daroussin 9676d38604fSBaptiste Daroussin if (ferror(stdout)) { 9686d38604fSBaptiste Daroussin if (outst->tag_files != NULL) { 9696d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s: %s", 9706d38604fSBaptiste Daroussin outst->tag_files->ofn, strerror(errno)); 9716d38604fSBaptiste Daroussin term_tag_unlink(); 9726d38604fSBaptiste Daroussin outst->tag_files = NULL; 9736d38604fSBaptiste Daroussin } else 9746d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s", 9756d38604fSBaptiste Daroussin strerror(errno)); 9766d38604fSBaptiste Daroussin } 9776d38604fSBaptiste Daroussin mandoc_msg_setinfilename(NULL); 9786d38604fSBaptiste Daroussin } 9796d38604fSBaptiste Daroussin 9806d38604fSBaptiste Daroussin static void 9816d38604fSBaptiste Daroussin parse(struct mparse *mp, int fd, const char *file, 9826d38604fSBaptiste Daroussin struct outstate *outst, struct manconf *conf) 9836d38604fSBaptiste Daroussin { 9846d38604fSBaptiste Daroussin static struct manpaths basepaths; 9856d38604fSBaptiste Daroussin static int previous; 9867295610fSBaptiste Daroussin struct roff_meta *meta; 98761d06d6bSBaptiste Daroussin 98861d06d6bSBaptiste Daroussin assert(fd >= 0); 9896d38604fSBaptiste Daroussin if (file == NULL) 9906d38604fSBaptiste Daroussin file = "<stdin>"; 99161d06d6bSBaptiste Daroussin 9926d38604fSBaptiste Daroussin if (previous) 9936d38604fSBaptiste Daroussin mparse_reset(mp); 9946d38604fSBaptiste Daroussin else 9956d38604fSBaptiste Daroussin previous = 1; 9966d38604fSBaptiste Daroussin 9976d38604fSBaptiste Daroussin mparse_readfd(mp, fd, file); 99861d06d6bSBaptiste Daroussin if (fd != STDIN_FILENO) 99961d06d6bSBaptiste Daroussin close(fd); 100061d06d6bSBaptiste Daroussin 100161d06d6bSBaptiste Daroussin /* 100261d06d6bSBaptiste Daroussin * With -Wstop and warnings or errors of at least the requested 100361d06d6bSBaptiste Daroussin * level, do not produce output. 100461d06d6bSBaptiste Daroussin */ 100561d06d6bSBaptiste Daroussin 10066d38604fSBaptiste Daroussin if (outst->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) 100761d06d6bSBaptiste Daroussin return; 100861d06d6bSBaptiste Daroussin 10096d38604fSBaptiste Daroussin if (outst->outdata == NULL) 10106d38604fSBaptiste Daroussin outdata_alloc(outst, &conf->output); 10116d38604fSBaptiste Daroussin else if (outst->outtype == OUTT_HTML) 10126d38604fSBaptiste Daroussin html_reset(outst->outdata); 101361d06d6bSBaptiste Daroussin 10147295610fSBaptiste Daroussin mandoc_xr_reset(); 10156d38604fSBaptiste Daroussin meta = mparse_result(mp); 101661d06d6bSBaptiste Daroussin 101761d06d6bSBaptiste Daroussin /* Execute the out device, if it exists. */ 101861d06d6bSBaptiste Daroussin 10196d38604fSBaptiste Daroussin outst->had_output = 1; 10207295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MDOC) { 10216d38604fSBaptiste Daroussin switch (outst->outtype) { 102261d06d6bSBaptiste Daroussin case OUTT_HTML: 10236d38604fSBaptiste Daroussin html_mdoc(outst->outdata, meta); 102461d06d6bSBaptiste Daroussin break; 102561d06d6bSBaptiste Daroussin case OUTT_TREE: 10266d38604fSBaptiste Daroussin tree_mdoc(outst->outdata, meta); 102761d06d6bSBaptiste Daroussin break; 102861d06d6bSBaptiste Daroussin case OUTT_MAN: 10296d38604fSBaptiste Daroussin man_mdoc(outst->outdata, meta); 103061d06d6bSBaptiste Daroussin break; 103161d06d6bSBaptiste Daroussin case OUTT_PDF: 103261d06d6bSBaptiste Daroussin case OUTT_ASCII: 103361d06d6bSBaptiste Daroussin case OUTT_UTF8: 103461d06d6bSBaptiste Daroussin case OUTT_LOCALE: 103561d06d6bSBaptiste Daroussin case OUTT_PS: 10366d38604fSBaptiste Daroussin terminal_mdoc(outst->outdata, meta); 103761d06d6bSBaptiste Daroussin break; 103861d06d6bSBaptiste Daroussin case OUTT_MARKDOWN: 10396d38604fSBaptiste Daroussin markdown_mdoc(outst->outdata, meta); 104061d06d6bSBaptiste Daroussin break; 104161d06d6bSBaptiste Daroussin default: 104261d06d6bSBaptiste Daroussin break; 104361d06d6bSBaptiste Daroussin } 104461d06d6bSBaptiste Daroussin } 10457295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MAN) { 10466d38604fSBaptiste Daroussin switch (outst->outtype) { 104761d06d6bSBaptiste Daroussin case OUTT_HTML: 10486d38604fSBaptiste Daroussin html_man(outst->outdata, meta); 104961d06d6bSBaptiste Daroussin break; 105061d06d6bSBaptiste Daroussin case OUTT_TREE: 10516d38604fSBaptiste Daroussin tree_man(outst->outdata, meta); 105261d06d6bSBaptiste Daroussin break; 105361d06d6bSBaptiste Daroussin case OUTT_MAN: 10546d38604fSBaptiste Daroussin mparse_copy(mp); 105561d06d6bSBaptiste Daroussin break; 105661d06d6bSBaptiste Daroussin case OUTT_PDF: 105761d06d6bSBaptiste Daroussin case OUTT_ASCII: 105861d06d6bSBaptiste Daroussin case OUTT_UTF8: 105961d06d6bSBaptiste Daroussin case OUTT_LOCALE: 106061d06d6bSBaptiste Daroussin case OUTT_PS: 10616d38604fSBaptiste Daroussin terminal_man(outst->outdata, meta); 10626d38604fSBaptiste Daroussin break; 10636d38604fSBaptiste Daroussin case OUTT_MARKDOWN: 10646d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_MAN_TMARKDOWN, 0, 0, NULL); 106561d06d6bSBaptiste Daroussin break; 106661d06d6bSBaptiste Daroussin default: 106761d06d6bSBaptiste Daroussin break; 106861d06d6bSBaptiste Daroussin } 106961d06d6bSBaptiste Daroussin } 10706d38604fSBaptiste Daroussin if (conf->output.tag != NULL && conf->output.tag_found == 0 && 10716d38604fSBaptiste Daroussin tag_exists(conf->output.tag)) 10726d38604fSBaptiste Daroussin conf->output.tag_found = 1; 10736d38604fSBaptiste Daroussin 10746d38604fSBaptiste Daroussin if (mandoc_msg_getmin() < MANDOCERR_STYLE) { 10756d38604fSBaptiste Daroussin if (basepaths.sz == 0) 10766d38604fSBaptiste Daroussin manpath_base(&basepaths); 10776d38604fSBaptiste Daroussin check_xr(&basepaths); 10786d38604fSBaptiste Daroussin } else if (mandoc_msg_getmin() < MANDOCERR_WARNING) 10796d38604fSBaptiste Daroussin check_xr(&conf->manpath); 108061d06d6bSBaptiste Daroussin } 108161d06d6bSBaptiste Daroussin 108261d06d6bSBaptiste Daroussin static void 10836d38604fSBaptiste Daroussin check_xr(struct manpaths *paths) 108461d06d6bSBaptiste Daroussin { 108561d06d6bSBaptiste Daroussin struct mansearch search; 108661d06d6bSBaptiste Daroussin struct mandoc_xr *xr; 108761d06d6bSBaptiste Daroussin size_t sz; 108861d06d6bSBaptiste Daroussin 108961d06d6bSBaptiste Daroussin for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { 109061d06d6bSBaptiste Daroussin if (xr->line == -1) 109161d06d6bSBaptiste Daroussin continue; 109261d06d6bSBaptiste Daroussin search.arch = NULL; 109361d06d6bSBaptiste Daroussin search.sec = xr->sec; 109461d06d6bSBaptiste Daroussin search.outkey = NULL; 109561d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 109661d06d6bSBaptiste Daroussin search.firstmatch = 1; 10976d38604fSBaptiste Daroussin if (mansearch(&search, paths, 1, &xr->name, NULL, &sz)) 109861d06d6bSBaptiste Daroussin continue; 10996d38604fSBaptiste Daroussin if (fs_search(&search, paths, xr->name, NULL, &sz) != -1) 110061d06d6bSBaptiste Daroussin continue; 110161d06d6bSBaptiste Daroussin if (xr->count == 1) 11027295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_XR_BAD, xr->line, 11037295610fSBaptiste Daroussin xr->pos + 1, "Xr %s %s", xr->name, xr->sec); 110461d06d6bSBaptiste Daroussin else 11057295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_XR_BAD, xr->line, 11067295610fSBaptiste Daroussin xr->pos + 1, "Xr %s %s (%d times)", 110761d06d6bSBaptiste Daroussin xr->name, xr->sec, xr->count); 110861d06d6bSBaptiste Daroussin } 110961d06d6bSBaptiste Daroussin } 111061d06d6bSBaptiste Daroussin 111161d06d6bSBaptiste Daroussin static void 11126d38604fSBaptiste Daroussin outdata_alloc(struct outstate *outst, struct manoutput *outconf) 111361d06d6bSBaptiste Daroussin { 11146d38604fSBaptiste Daroussin switch (outst->outtype) { 111561d06d6bSBaptiste Daroussin case OUTT_HTML: 11166d38604fSBaptiste Daroussin outst->outdata = html_alloc(outconf); 111761d06d6bSBaptiste Daroussin break; 111861d06d6bSBaptiste Daroussin case OUTT_UTF8: 11196d38604fSBaptiste Daroussin outst->outdata = utf8_alloc(outconf); 112061d06d6bSBaptiste Daroussin break; 112161d06d6bSBaptiste Daroussin case OUTT_LOCALE: 11226d38604fSBaptiste Daroussin outst->outdata = locale_alloc(outconf); 112361d06d6bSBaptiste Daroussin break; 112461d06d6bSBaptiste Daroussin case OUTT_ASCII: 11256d38604fSBaptiste Daroussin outst->outdata = ascii_alloc(outconf); 112661d06d6bSBaptiste Daroussin break; 112761d06d6bSBaptiste Daroussin case OUTT_PDF: 11286d38604fSBaptiste Daroussin outst->outdata = pdf_alloc(outconf); 112961d06d6bSBaptiste Daroussin break; 113061d06d6bSBaptiste Daroussin case OUTT_PS: 11316d38604fSBaptiste Daroussin outst->outdata = ps_alloc(outconf); 113261d06d6bSBaptiste Daroussin break; 113361d06d6bSBaptiste Daroussin default: 113461d06d6bSBaptiste Daroussin break; 113561d06d6bSBaptiste Daroussin } 113661d06d6bSBaptiste Daroussin } 113761d06d6bSBaptiste Daroussin 113861d06d6bSBaptiste Daroussin static void 113945a5aec3SBaptiste Daroussin passthrough(int fd, int synopsis_only) 114061d06d6bSBaptiste Daroussin { 114161d06d6bSBaptiste Daroussin const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; 114261d06d6bSBaptiste Daroussin const char synr[] = "SYNOPSIS"; 114361d06d6bSBaptiste Daroussin 114461d06d6bSBaptiste Daroussin FILE *stream; 114561d06d6bSBaptiste Daroussin char *line, *cp; 114661d06d6bSBaptiste Daroussin size_t linesz; 114761d06d6bSBaptiste Daroussin ssize_t len, written; 114845a5aec3SBaptiste Daroussin int lno, print; 114961d06d6bSBaptiste Daroussin 115045a5aec3SBaptiste Daroussin stream = NULL; 115161d06d6bSBaptiste Daroussin line = NULL; 115261d06d6bSBaptiste Daroussin linesz = 0; 115361d06d6bSBaptiste Daroussin 115461d06d6bSBaptiste Daroussin if (fflush(stdout) == EOF) { 115545a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FFLUSH, 0, 0, "%s", strerror(errno)); 115645a5aec3SBaptiste Daroussin goto done; 115761d06d6bSBaptiste Daroussin } 115861d06d6bSBaptiste Daroussin if ((stream = fdopen(fd, "r")) == NULL) { 115961d06d6bSBaptiste Daroussin close(fd); 116045a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); 116145a5aec3SBaptiste Daroussin goto done; 116261d06d6bSBaptiste Daroussin } 116361d06d6bSBaptiste Daroussin 116445a5aec3SBaptiste Daroussin lno = print = 0; 116561d06d6bSBaptiste Daroussin while ((len = getline(&line, &linesz, stream)) != -1) { 116645a5aec3SBaptiste Daroussin lno++; 116761d06d6bSBaptiste Daroussin cp = line; 116861d06d6bSBaptiste Daroussin if (synopsis_only) { 116961d06d6bSBaptiste Daroussin if (print) { 117061d06d6bSBaptiste Daroussin if ( ! isspace((unsigned char)*cp)) 117161d06d6bSBaptiste Daroussin goto done; 117261d06d6bSBaptiste Daroussin while (isspace((unsigned char)*cp)) { 117361d06d6bSBaptiste Daroussin cp++; 117461d06d6bSBaptiste Daroussin len--; 117561d06d6bSBaptiste Daroussin } 117661d06d6bSBaptiste Daroussin } else { 117761d06d6bSBaptiste Daroussin if (strcmp(cp, synb) == 0 || 117861d06d6bSBaptiste Daroussin strcmp(cp, synr) == 0) 117961d06d6bSBaptiste Daroussin print = 1; 118061d06d6bSBaptiste Daroussin continue; 118161d06d6bSBaptiste Daroussin } 118261d06d6bSBaptiste Daroussin } 118361d06d6bSBaptiste Daroussin for (; len > 0; len -= written) { 118445a5aec3SBaptiste Daroussin if ((written = write(STDOUT_FILENO, cp, len)) == -1) { 118545a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_WRITE, 0, 0, 118645a5aec3SBaptiste Daroussin "%s", strerror(errno)); 118745a5aec3SBaptiste Daroussin goto done; 118861d06d6bSBaptiste Daroussin } 118961d06d6bSBaptiste Daroussin } 119061d06d6bSBaptiste Daroussin } 119145a5aec3SBaptiste Daroussin if (ferror(stream)) 119245a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GETLINE, lno, 0, "%s", strerror(errno)); 119361d06d6bSBaptiste Daroussin 119461d06d6bSBaptiste Daroussin done: 119561d06d6bSBaptiste Daroussin free(line); 119645a5aec3SBaptiste Daroussin if (stream != NULL) 119761d06d6bSBaptiste Daroussin fclose(stream); 119861d06d6bSBaptiste Daroussin } 119961d06d6bSBaptiste Daroussin 120061d06d6bSBaptiste Daroussin static int 12016d38604fSBaptiste Daroussin woptions(char *arg, enum mandoc_os *os_e, int *wstop) 120261d06d6bSBaptiste Daroussin { 120361d06d6bSBaptiste Daroussin char *v, *o; 120461d06d6bSBaptiste Daroussin const char *toks[11]; 120561d06d6bSBaptiste Daroussin 120661d06d6bSBaptiste Daroussin toks[0] = "stop"; 120761d06d6bSBaptiste Daroussin toks[1] = "all"; 120861d06d6bSBaptiste Daroussin toks[2] = "base"; 120961d06d6bSBaptiste Daroussin toks[3] = "style"; 121061d06d6bSBaptiste Daroussin toks[4] = "warning"; 121161d06d6bSBaptiste Daroussin toks[5] = "error"; 121261d06d6bSBaptiste Daroussin toks[6] = "unsupp"; 121361d06d6bSBaptiste Daroussin toks[7] = "fatal"; 121461d06d6bSBaptiste Daroussin toks[8] = "openbsd"; 121561d06d6bSBaptiste Daroussin toks[9] = "netbsd"; 121661d06d6bSBaptiste Daroussin toks[10] = NULL; 121761d06d6bSBaptiste Daroussin 121861d06d6bSBaptiste Daroussin while (*arg) { 121961d06d6bSBaptiste Daroussin o = arg; 122061d06d6bSBaptiste Daroussin switch (getsubopt(&arg, (char * const *)toks, &v)) { 122161d06d6bSBaptiste Daroussin case 0: 12226d38604fSBaptiste Daroussin *wstop = 1; 122361d06d6bSBaptiste Daroussin break; 122461d06d6bSBaptiste Daroussin case 1: 122561d06d6bSBaptiste Daroussin case 2: 12267295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 122761d06d6bSBaptiste Daroussin break; 122861d06d6bSBaptiste Daroussin case 3: 12297295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_STYLE); 123061d06d6bSBaptiste Daroussin break; 123161d06d6bSBaptiste Daroussin case 4: 12327295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_WARNING); 123361d06d6bSBaptiste Daroussin break; 123461d06d6bSBaptiste Daroussin case 5: 12357295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_ERROR); 123661d06d6bSBaptiste Daroussin break; 123761d06d6bSBaptiste Daroussin case 6: 12387295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_UNSUPP); 123961d06d6bSBaptiste Daroussin break; 124061d06d6bSBaptiste Daroussin case 7: 124145a5aec3SBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BADARG); 124261d06d6bSBaptiste Daroussin break; 124361d06d6bSBaptiste Daroussin case 8: 12447295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 12456d38604fSBaptiste Daroussin *os_e = MANDOC_OS_OPENBSD; 124661d06d6bSBaptiste Daroussin break; 124761d06d6bSBaptiste Daroussin case 9: 12487295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 12496d38604fSBaptiste Daroussin *os_e = MANDOC_OS_NETBSD; 125061d06d6bSBaptiste Daroussin break; 125161d06d6bSBaptiste Daroussin default: 125245a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-W %s", o); 125345a5aec3SBaptiste Daroussin return -1; 125445a5aec3SBaptiste Daroussin } 125545a5aec3SBaptiste Daroussin } 125661d06d6bSBaptiste Daroussin return 0; 125761d06d6bSBaptiste Daroussin } 125861d06d6bSBaptiste Daroussin 12596d38604fSBaptiste Daroussin /* 12606d38604fSBaptiste Daroussin * Wait until moved to the foreground, 12616d38604fSBaptiste Daroussin * then fork the pager and wait for the user to close it. 12626d38604fSBaptiste Daroussin */ 12636d38604fSBaptiste Daroussin static void 12646d38604fSBaptiste Daroussin run_pager(struct outstate *outst, char *tag_target) 12656d38604fSBaptiste Daroussin { 12666d38604fSBaptiste Daroussin int signum, status; 12676d38604fSBaptiste Daroussin pid_t man_pgid, tc_pgid; 12686d38604fSBaptiste Daroussin pid_t pager_pid, wait_pid; 12696d38604fSBaptiste Daroussin 12706d38604fSBaptiste Daroussin man_pgid = getpgid(0); 12716d38604fSBaptiste Daroussin outst->tag_files->tcpgid = 12726d38604fSBaptiste Daroussin man_pgid == getpid() ? getpgid(getppid()) : man_pgid; 12736d38604fSBaptiste Daroussin pager_pid = 0; 12746d38604fSBaptiste Daroussin signum = SIGSTOP; 12756d38604fSBaptiste Daroussin 12766d38604fSBaptiste Daroussin for (;;) { 12776d38604fSBaptiste Daroussin /* Stop here until moved to the foreground. */ 12786d38604fSBaptiste Daroussin 12796d38604fSBaptiste Daroussin tc_pgid = tcgetpgrp(STDOUT_FILENO); 12806d38604fSBaptiste Daroussin if (tc_pgid != man_pgid) { 12816d38604fSBaptiste Daroussin if (tc_pgid == pager_pid) { 12826d38604fSBaptiste Daroussin (void)tcsetpgrp(STDOUT_FILENO, man_pgid); 12836d38604fSBaptiste Daroussin if (signum == SIGTTIN) 12846d38604fSBaptiste Daroussin continue; 12856d38604fSBaptiste Daroussin } else 12866d38604fSBaptiste Daroussin outst->tag_files->tcpgid = tc_pgid; 12876d38604fSBaptiste Daroussin kill(0, signum); 12886d38604fSBaptiste Daroussin continue; 12896d38604fSBaptiste Daroussin } 12906d38604fSBaptiste Daroussin 12916d38604fSBaptiste Daroussin /* Once in the foreground, activate the pager. */ 12926d38604fSBaptiste Daroussin 12936d38604fSBaptiste Daroussin if (pager_pid) { 12946d38604fSBaptiste Daroussin (void)tcsetpgrp(STDOUT_FILENO, pager_pid); 12956d38604fSBaptiste Daroussin kill(pager_pid, SIGCONT); 12966d38604fSBaptiste Daroussin } else 12976d38604fSBaptiste Daroussin pager_pid = spawn_pager(outst, tag_target); 12986d38604fSBaptiste Daroussin 12996d38604fSBaptiste Daroussin /* Wait for the pager to stop or exit. */ 13006d38604fSBaptiste Daroussin 13016d38604fSBaptiste Daroussin while ((wait_pid = waitpid(pager_pid, &status, 13026d38604fSBaptiste Daroussin WUNTRACED)) == -1 && errno == EINTR) 13036d38604fSBaptiste Daroussin continue; 13046d38604fSBaptiste Daroussin 13056d38604fSBaptiste Daroussin if (wait_pid == -1) { 13066d38604fSBaptiste Daroussin mandoc_msg(MANDOCERR_WAIT, 0, 0, 13076d38604fSBaptiste Daroussin "%s", strerror(errno)); 13086d38604fSBaptiste Daroussin break; 13096d38604fSBaptiste Daroussin } 13106d38604fSBaptiste Daroussin if (!WIFSTOPPED(status)) 13116d38604fSBaptiste Daroussin break; 13126d38604fSBaptiste Daroussin 13136d38604fSBaptiste Daroussin signum = WSTOPSIG(status); 13146d38604fSBaptiste Daroussin } 13156d38604fSBaptiste Daroussin } 13166d38604fSBaptiste Daroussin 131761d06d6bSBaptiste Daroussin static pid_t 13186d38604fSBaptiste Daroussin spawn_pager(struct outstate *outst, char *tag_target) 131961d06d6bSBaptiste Daroussin { 132061d06d6bSBaptiste Daroussin const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ 132161d06d6bSBaptiste Daroussin #define MAX_PAGER_ARGS 16 132261d06d6bSBaptiste Daroussin char *argv[MAX_PAGER_ARGS]; 132361d06d6bSBaptiste Daroussin const char *pager; 132461d06d6bSBaptiste Daroussin char *cp; 1325*c1c95addSBrooks Davis size_t wordlen; 13267295610fSBaptiste Daroussin #if HAVE_LESS_T 132761d06d6bSBaptiste Daroussin size_t cmdlen; 13287295610fSBaptiste Daroussin #endif 13297295610fSBaptiste Daroussin int argc, use_ofn; 133061d06d6bSBaptiste Daroussin pid_t pager_pid; 133161d06d6bSBaptiste Daroussin 13326d38604fSBaptiste Daroussin assert(outst->tag_files->ofd == -1); 13336d38604fSBaptiste Daroussin assert(outst->tag_files->tfs == NULL); 13346d38604fSBaptiste Daroussin 133561d06d6bSBaptiste Daroussin pager = getenv("MANPAGER"); 133661d06d6bSBaptiste Daroussin if (pager == NULL || *pager == '\0') 133761d06d6bSBaptiste Daroussin pager = getenv("PAGER"); 133861d06d6bSBaptiste Daroussin if (pager == NULL || *pager == '\0') 13396d38604fSBaptiste Daroussin pager = BINM_PAGER; 134061d06d6bSBaptiste Daroussin 134161d06d6bSBaptiste Daroussin /* 134261d06d6bSBaptiste Daroussin * Parse the pager command into words. 134361d06d6bSBaptiste Daroussin * Intentionally do not do anything fancy here. 134461d06d6bSBaptiste Daroussin */ 134561d06d6bSBaptiste Daroussin 134661d06d6bSBaptiste Daroussin argc = 0; 1347*c1c95addSBrooks Davis while (*pager != '\0' && argc + 5 < MAX_PAGER_ARGS) { 1348*c1c95addSBrooks Davis wordlen = strcspn(pager, " "); 1349*c1c95addSBrooks Davis argv[argc++] = mandoc_strndup(pager, wordlen); 1350*c1c95addSBrooks Davis pager += wordlen; 1351*c1c95addSBrooks Davis while (*pager == ' ') 1352*c1c95addSBrooks Davis pager++; 135361d06d6bSBaptiste Daroussin } 135461d06d6bSBaptiste Daroussin 135561d06d6bSBaptiste Daroussin /* For less(1), use the tag file. */ 135661d06d6bSBaptiste Daroussin 13577295610fSBaptiste Daroussin use_ofn = 1; 13587295610fSBaptiste Daroussin #if HAVE_LESS_T 13596d38604fSBaptiste Daroussin if (*outst->tag_files->tfn != '\0' && 13606d38604fSBaptiste Daroussin (cmdlen = strlen(argv[0])) >= 4) { 136161d06d6bSBaptiste Daroussin cp = argv[0] + cmdlen - 4; 136261d06d6bSBaptiste Daroussin if (strcmp(cp, "less") == 0) { 136361d06d6bSBaptiste Daroussin argv[argc++] = mandoc_strdup("-T"); 1364*c1c95addSBrooks Davis argv[argc++] = mandoc_strdup(outst->tag_files->tfn); 13656d38604fSBaptiste Daroussin if (tag_target != NULL) { 13667295610fSBaptiste Daroussin argv[argc++] = mandoc_strdup("-t"); 1367*c1c95addSBrooks Davis argv[argc++] = mandoc_strdup(tag_target); 13687295610fSBaptiste Daroussin use_ofn = 0; 136961d06d6bSBaptiste Daroussin } 137061d06d6bSBaptiste Daroussin } 13717295610fSBaptiste Daroussin } 13727295610fSBaptiste Daroussin #endif 13736d38604fSBaptiste Daroussin if (use_ofn) { 13746d38604fSBaptiste Daroussin if (outst->outtype == OUTT_HTML && tag_target != NULL) 13756d38604fSBaptiste Daroussin mandoc_asprintf(&argv[argc], "file://%s#%s", 13766d38604fSBaptiste Daroussin outst->tag_files->ofn, tag_target); 13776d38604fSBaptiste Daroussin else 1378*c1c95addSBrooks Davis argv[argc] = mandoc_strdup(outst->tag_files->ofn); 13796d38604fSBaptiste Daroussin argc++; 13806d38604fSBaptiste Daroussin } 138161d06d6bSBaptiste Daroussin argv[argc] = NULL; 138261d06d6bSBaptiste Daroussin 138361d06d6bSBaptiste Daroussin switch (pager_pid = fork()) { 138461d06d6bSBaptiste Daroussin case -1: 138545a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FORK, 0, 0, "%s", strerror(errno)); 138645a5aec3SBaptiste Daroussin exit(mandoc_msg_getrc()); 138761d06d6bSBaptiste Daroussin case 0: 138861d06d6bSBaptiste Daroussin break; 138961d06d6bSBaptiste Daroussin default: 1390*c1c95addSBrooks Davis while (argc > 0) 1391*c1c95addSBrooks Davis free(argv[--argc]); 139261d06d6bSBaptiste Daroussin (void)setpgid(pager_pid, 0); 13936d38604fSBaptiste Daroussin (void)tcsetpgrp(STDOUT_FILENO, pager_pid); 139461d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 139545a5aec3SBaptiste Daroussin if (pledge("stdio rpath tmppath tty proc", NULL) == -1) { 139645a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, 139745a5aec3SBaptiste Daroussin "%s", strerror(errno)); 139845a5aec3SBaptiste Daroussin exit(mandoc_msg_getrc()); 139945a5aec3SBaptiste Daroussin } 140061d06d6bSBaptiste Daroussin #endif 14016d38604fSBaptiste Daroussin outst->tag_files->pager_pid = pager_pid; 140261d06d6bSBaptiste Daroussin return pager_pid; 140361d06d6bSBaptiste Daroussin } 140461d06d6bSBaptiste Daroussin 14056d38604fSBaptiste Daroussin /* 14066d38604fSBaptiste Daroussin * The child process becomes the pager. 14076d38604fSBaptiste Daroussin * Do not start it before controlling the terminal. 14086d38604fSBaptiste Daroussin */ 140961d06d6bSBaptiste Daroussin 141061d06d6bSBaptiste Daroussin while (tcgetpgrp(STDOUT_FILENO) != getpid()) 141161d06d6bSBaptiste Daroussin nanosleep(&timeout, NULL); 141261d06d6bSBaptiste Daroussin 141361d06d6bSBaptiste Daroussin execvp(argv[0], argv); 141445a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", argv[0], strerror(errno)); 141545a5aec3SBaptiste Daroussin _exit(mandoc_msg_getrc()); 141661d06d6bSBaptiste Daroussin } 1417