1*45a5aec3SBaptiste Daroussin /* $Id: main.c,v 1.332 2019/07/19 20:27:25 schwarze Exp $ */ 261d06d6bSBaptiste Daroussin /* 361d06d6bSBaptiste Daroussin * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 47295610fSBaptiste Daroussin * Copyright (c) 2010-2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org> 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. 1861d06d6bSBaptiste Daroussin */ 1961d06d6bSBaptiste Daroussin #include "config.h" 2061d06d6bSBaptiste Daroussin 2161d06d6bSBaptiste Daroussin #include <sys/types.h> 2261d06d6bSBaptiste Daroussin #include <sys/ioctl.h> 2361d06d6bSBaptiste Daroussin #include <sys/param.h> /* MACHINE */ 24*45a5aec3SBaptiste Daroussin #include <sys/stat.h> 2561d06d6bSBaptiste Daroussin #include <sys/wait.h> 2661d06d6bSBaptiste Daroussin 2761d06d6bSBaptiste Daroussin #include <assert.h> 2861d06d6bSBaptiste Daroussin #include <ctype.h> 2961d06d6bSBaptiste Daroussin #if HAVE_ERR 3061d06d6bSBaptiste Daroussin #include <err.h> 3161d06d6bSBaptiste Daroussin #endif 3261d06d6bSBaptiste Daroussin #include <errno.h> 3361d06d6bSBaptiste Daroussin #include <fcntl.h> 3461d06d6bSBaptiste Daroussin #include <glob.h> 3561d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT 3661d06d6bSBaptiste Daroussin #include <sandbox.h> 3761d06d6bSBaptiste Daroussin #endif 3861d06d6bSBaptiste Daroussin #include <signal.h> 3961d06d6bSBaptiste Daroussin #include <stdio.h> 4061d06d6bSBaptiste Daroussin #include <stdint.h> 4161d06d6bSBaptiste Daroussin #include <stdlib.h> 4261d06d6bSBaptiste Daroussin #include <string.h> 4361d06d6bSBaptiste Daroussin #include <termios.h> 4461d06d6bSBaptiste Daroussin #include <time.h> 4561d06d6bSBaptiste Daroussin #include <unistd.h> 4661d06d6bSBaptiste Daroussin 4761d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 4861d06d6bSBaptiste Daroussin #include "mandoc.h" 4961d06d6bSBaptiste Daroussin #include "mandoc_xr.h" 5061d06d6bSBaptiste Daroussin #include "roff.h" 5161d06d6bSBaptiste Daroussin #include "mdoc.h" 5261d06d6bSBaptiste Daroussin #include "man.h" 537295610fSBaptiste Daroussin #include "mandoc_parse.h" 5461d06d6bSBaptiste Daroussin #include "tag.h" 5561d06d6bSBaptiste Daroussin #include "main.h" 5661d06d6bSBaptiste Daroussin #include "manconf.h" 5761d06d6bSBaptiste Daroussin #include "mansearch.h" 5861d06d6bSBaptiste Daroussin 5961d06d6bSBaptiste Daroussin enum outmode { 6061d06d6bSBaptiste Daroussin OUTMODE_DEF = 0, 6161d06d6bSBaptiste Daroussin OUTMODE_FLN, 6261d06d6bSBaptiste Daroussin OUTMODE_LST, 6361d06d6bSBaptiste Daroussin OUTMODE_ALL, 6461d06d6bSBaptiste Daroussin OUTMODE_ONE 6561d06d6bSBaptiste Daroussin }; 6661d06d6bSBaptiste Daroussin 6761d06d6bSBaptiste Daroussin enum outt { 6861d06d6bSBaptiste Daroussin OUTT_ASCII = 0, /* -Tascii */ 6961d06d6bSBaptiste Daroussin OUTT_LOCALE, /* -Tlocale */ 7061d06d6bSBaptiste Daroussin OUTT_UTF8, /* -Tutf8 */ 7161d06d6bSBaptiste Daroussin OUTT_TREE, /* -Ttree */ 7261d06d6bSBaptiste Daroussin OUTT_MAN, /* -Tman */ 7361d06d6bSBaptiste Daroussin OUTT_HTML, /* -Thtml */ 7461d06d6bSBaptiste Daroussin OUTT_MARKDOWN, /* -Tmarkdown */ 7561d06d6bSBaptiste Daroussin OUTT_LINT, /* -Tlint */ 7661d06d6bSBaptiste Daroussin OUTT_PS, /* -Tps */ 7761d06d6bSBaptiste Daroussin OUTT_PDF /* -Tpdf */ 7861d06d6bSBaptiste Daroussin }; 7961d06d6bSBaptiste Daroussin 8061d06d6bSBaptiste Daroussin struct curparse { 8161d06d6bSBaptiste Daroussin struct mparse *mp; 8261d06d6bSBaptiste Daroussin struct manoutput *outopts; /* output options */ 8361d06d6bSBaptiste Daroussin void *outdata; /* data for output */ 8461d06d6bSBaptiste Daroussin char *os_s; /* operating system for display */ 8561d06d6bSBaptiste Daroussin int wstop; /* stop after a file with a warning */ 8661d06d6bSBaptiste Daroussin enum mandoc_os os_e; /* check base system conventions */ 8761d06d6bSBaptiste Daroussin enum outt outtype; /* which output to use */ 8861d06d6bSBaptiste Daroussin }; 8961d06d6bSBaptiste Daroussin 9061d06d6bSBaptiste Daroussin 9161d06d6bSBaptiste Daroussin int mandocdb(int, char *[]); 9261d06d6bSBaptiste Daroussin 937295610fSBaptiste Daroussin static void check_xr(void); 9461d06d6bSBaptiste Daroussin static int fs_lookup(const struct manpaths *, 9561d06d6bSBaptiste Daroussin size_t ipath, const char *, 9661d06d6bSBaptiste Daroussin const char *, const char *, 9761d06d6bSBaptiste Daroussin struct manpage **, size_t *); 9861d06d6bSBaptiste Daroussin static int fs_search(const struct mansearch *, 9961d06d6bSBaptiste Daroussin const struct manpaths *, int, char**, 10061d06d6bSBaptiste Daroussin struct manpage **, size_t *); 10161d06d6bSBaptiste Daroussin static void outdata_alloc(struct curparse *); 10261d06d6bSBaptiste Daroussin static void parse(struct curparse *, int, const char *); 103*45a5aec3SBaptiste Daroussin static void passthrough(int, int); 10461d06d6bSBaptiste Daroussin static pid_t spawn_pager(struct tag_files *); 10561d06d6bSBaptiste Daroussin static void usage(enum argmode) __attribute__((__noreturn__)); 10661d06d6bSBaptiste Daroussin static int woptions(struct curparse *, char *); 10761d06d6bSBaptiste Daroussin 10861d06d6bSBaptiste Daroussin static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; 10961d06d6bSBaptiste Daroussin static char help_arg[] = "help"; 11061d06d6bSBaptiste Daroussin static char *help_argv[] = {help_arg, NULL}; 11161d06d6bSBaptiste Daroussin 11261d06d6bSBaptiste Daroussin 11361d06d6bSBaptiste Daroussin int 11461d06d6bSBaptiste Daroussin main(int argc, char *argv[]) 11561d06d6bSBaptiste Daroussin { 11661d06d6bSBaptiste Daroussin struct manconf conf; 11761d06d6bSBaptiste Daroussin struct mansearch search; 11861d06d6bSBaptiste Daroussin struct curparse curp; 11961d06d6bSBaptiste Daroussin struct winsize ws; 12061d06d6bSBaptiste Daroussin struct tag_files *tag_files; 12161d06d6bSBaptiste Daroussin struct manpage *res, *resp; 12261d06d6bSBaptiste Daroussin const char *progname, *sec, *thisarg; 12361d06d6bSBaptiste Daroussin char *conf_file, *defpaths, *auxpaths; 1247295610fSBaptiste Daroussin char *oarg, *tagarg; 12561d06d6bSBaptiste Daroussin unsigned char *uc; 126*45a5aec3SBaptiste Daroussin size_t i, sz, ssz; 12761d06d6bSBaptiste Daroussin int prio, best_prio; 12861d06d6bSBaptiste Daroussin enum outmode outmode; 12961d06d6bSBaptiste Daroussin int fd, startdir; 13061d06d6bSBaptiste Daroussin int show_usage; 13161d06d6bSBaptiste Daroussin int options; 13261d06d6bSBaptiste Daroussin int use_pager; 13361d06d6bSBaptiste Daroussin int status, signum; 13461d06d6bSBaptiste Daroussin int c; 13561d06d6bSBaptiste Daroussin pid_t pager_pid, tc_pgid, man_pgid, pid; 13661d06d6bSBaptiste Daroussin 13761d06d6bSBaptiste Daroussin #if HAVE_PROGNAME 13861d06d6bSBaptiste Daroussin progname = getprogname(); 13961d06d6bSBaptiste Daroussin #else 14061d06d6bSBaptiste Daroussin if (argc < 1) 14161d06d6bSBaptiste Daroussin progname = mandoc_strdup("mandoc"); 14261d06d6bSBaptiste Daroussin else if ((progname = strrchr(argv[0], '/')) == NULL) 14361d06d6bSBaptiste Daroussin progname = argv[0]; 14461d06d6bSBaptiste Daroussin else 14561d06d6bSBaptiste Daroussin ++progname; 14661d06d6bSBaptiste Daroussin setprogname(progname); 14761d06d6bSBaptiste Daroussin #endif 14861d06d6bSBaptiste Daroussin 1497295610fSBaptiste Daroussin mandoc_msg_setoutfile(stderr); 15061d06d6bSBaptiste Daroussin if (strncmp(progname, "mandocdb", 8) == 0 || 15161d06d6bSBaptiste Daroussin strcmp(progname, BINM_MAKEWHATIS) == 0) 15261d06d6bSBaptiste Daroussin return mandocdb(argc, argv); 15361d06d6bSBaptiste Daroussin 15461d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 155*45a5aec3SBaptiste Daroussin if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) { 156*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); 157*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 158*45a5aec3SBaptiste Daroussin } 15961d06d6bSBaptiste Daroussin #endif 16061d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT 16161d06d6bSBaptiste Daroussin if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) 16261d06d6bSBaptiste Daroussin errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); 16361d06d6bSBaptiste Daroussin #endif 16461d06d6bSBaptiste Daroussin 16561d06d6bSBaptiste Daroussin /* Search options. */ 16661d06d6bSBaptiste Daroussin 16761d06d6bSBaptiste Daroussin memset(&conf, 0, sizeof(conf)); 16861d06d6bSBaptiste Daroussin conf_file = defpaths = NULL; 16961d06d6bSBaptiste Daroussin auxpaths = NULL; 17061d06d6bSBaptiste Daroussin 17161d06d6bSBaptiste Daroussin memset(&search, 0, sizeof(struct mansearch)); 17261d06d6bSBaptiste Daroussin search.outkey = "Nd"; 17361d06d6bSBaptiste Daroussin oarg = NULL; 17461d06d6bSBaptiste Daroussin 17561d06d6bSBaptiste Daroussin if (strcmp(progname, BINM_MAN) == 0) 17661d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 17761d06d6bSBaptiste Daroussin else if (strcmp(progname, BINM_APROPOS) == 0) 17861d06d6bSBaptiste Daroussin search.argmode = ARG_EXPR; 17961d06d6bSBaptiste Daroussin else if (strcmp(progname, BINM_WHATIS) == 0) 18061d06d6bSBaptiste Daroussin search.argmode = ARG_WORD; 18161d06d6bSBaptiste Daroussin else if (strncmp(progname, "help", 4) == 0) 18261d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 18361d06d6bSBaptiste Daroussin else 18461d06d6bSBaptiste Daroussin search.argmode = ARG_FILE; 18561d06d6bSBaptiste Daroussin 18661d06d6bSBaptiste Daroussin /* Parser and formatter options. */ 18761d06d6bSBaptiste Daroussin 18861d06d6bSBaptiste Daroussin memset(&curp, 0, sizeof(struct curparse)); 18961d06d6bSBaptiste Daroussin curp.outtype = OUTT_LOCALE; 19061d06d6bSBaptiste Daroussin curp.outopts = &conf.output; 19161d06d6bSBaptiste Daroussin options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; 19261d06d6bSBaptiste Daroussin 19361d06d6bSBaptiste Daroussin use_pager = 1; 19461d06d6bSBaptiste Daroussin tag_files = NULL; 19561d06d6bSBaptiste Daroussin show_usage = 0; 19661d06d6bSBaptiste Daroussin outmode = OUTMODE_DEF; 19761d06d6bSBaptiste Daroussin 19861d06d6bSBaptiste Daroussin while ((c = getopt(argc, argv, 19961d06d6bSBaptiste Daroussin "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) { 20061d06d6bSBaptiste Daroussin if (c == 'i' && search.argmode == ARG_EXPR) { 20161d06d6bSBaptiste Daroussin optind--; 20261d06d6bSBaptiste Daroussin break; 20361d06d6bSBaptiste Daroussin } 20461d06d6bSBaptiste Daroussin switch (c) { 20561d06d6bSBaptiste Daroussin case 'a': 20661d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 20761d06d6bSBaptiste Daroussin break; 20861d06d6bSBaptiste Daroussin case 'C': 20961d06d6bSBaptiste Daroussin conf_file = optarg; 21061d06d6bSBaptiste Daroussin break; 21161d06d6bSBaptiste Daroussin case 'c': 21261d06d6bSBaptiste Daroussin use_pager = 0; 21361d06d6bSBaptiste Daroussin break; 21461d06d6bSBaptiste Daroussin case 'f': 21561d06d6bSBaptiste Daroussin search.argmode = ARG_WORD; 21661d06d6bSBaptiste Daroussin break; 21761d06d6bSBaptiste Daroussin case 'h': 21861d06d6bSBaptiste Daroussin conf.output.synopsisonly = 1; 21961d06d6bSBaptiste Daroussin use_pager = 0; 22061d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 22161d06d6bSBaptiste Daroussin break; 22261d06d6bSBaptiste Daroussin case 'I': 223*45a5aec3SBaptiste Daroussin if (strncmp(optarg, "os=", 3) != 0) { 224*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, 225*45a5aec3SBaptiste Daroussin "-I %s", optarg); 226*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 22761d06d6bSBaptiste Daroussin } 22861d06d6bSBaptiste Daroussin if (curp.os_s != NULL) { 229*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_DUPE, 0, 0, 230*45a5aec3SBaptiste Daroussin "-I %s", optarg); 231*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 23261d06d6bSBaptiste Daroussin } 23361d06d6bSBaptiste Daroussin curp.os_s = mandoc_strdup(optarg + 3); 23461d06d6bSBaptiste Daroussin break; 23561d06d6bSBaptiste Daroussin case 'K': 236*45a5aec3SBaptiste Daroussin options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); 237*45a5aec3SBaptiste Daroussin if (strcmp(optarg, "utf-8") == 0) 238*45a5aec3SBaptiste Daroussin options |= MPARSE_UTF8; 239*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "iso-8859-1") == 0) 240*45a5aec3SBaptiste Daroussin options |= MPARSE_LATIN1; 241*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "us-ascii") != 0) { 242*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, 243*45a5aec3SBaptiste Daroussin "-K %s", optarg); 244*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 245*45a5aec3SBaptiste Daroussin } 24661d06d6bSBaptiste Daroussin break; 24761d06d6bSBaptiste Daroussin case 'k': 24861d06d6bSBaptiste Daroussin search.argmode = ARG_EXPR; 24961d06d6bSBaptiste Daroussin break; 25061d06d6bSBaptiste Daroussin case 'l': 25161d06d6bSBaptiste Daroussin search.argmode = ARG_FILE; 25261d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 25361d06d6bSBaptiste Daroussin break; 25461d06d6bSBaptiste Daroussin case 'M': 255a10034cbSYuri Pankov #ifdef __FreeBSD__ 256a10034cbSYuri Pankov defpaths = strdup(optarg); 257a10034cbSYuri Pankov if (defpaths == NULL) 258a10034cbSYuri Pankov err(1, "strdup"); 259a10034cbSYuri Pankov #else 26061d06d6bSBaptiste Daroussin defpaths = optarg; 261a10034cbSYuri Pankov #endif 26261d06d6bSBaptiste Daroussin break; 26361d06d6bSBaptiste Daroussin case 'm': 26461d06d6bSBaptiste Daroussin auxpaths = optarg; 26561d06d6bSBaptiste Daroussin break; 26661d06d6bSBaptiste Daroussin case 'O': 26761d06d6bSBaptiste Daroussin oarg = optarg; 26861d06d6bSBaptiste Daroussin break; 26961d06d6bSBaptiste Daroussin case 'S': 27061d06d6bSBaptiste Daroussin search.arch = optarg; 27161d06d6bSBaptiste Daroussin break; 27261d06d6bSBaptiste Daroussin case 's': 27361d06d6bSBaptiste Daroussin search.sec = optarg; 27461d06d6bSBaptiste Daroussin break; 27561d06d6bSBaptiste Daroussin case 'T': 276*45a5aec3SBaptiste Daroussin if (strcmp(optarg, "ascii") == 0) 277*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_ASCII; 278*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "lint") == 0) { 279*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_LINT; 280*45a5aec3SBaptiste Daroussin mandoc_msg_setoutfile(stdout); 281*45a5aec3SBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 282*45a5aec3SBaptiste Daroussin } else if (strcmp(optarg, "tree") == 0) 283*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_TREE; 284*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "man") == 0) 285*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_MAN; 286*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "html") == 0) 287*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_HTML; 288*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "markdown") == 0) 289*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_MARKDOWN; 290*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "utf8") == 0) 291*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_UTF8; 292*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "locale") == 0) 293*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_LOCALE; 294*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "ps") == 0) 295*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_PS; 296*45a5aec3SBaptiste Daroussin else if (strcmp(optarg, "pdf") == 0) 297*45a5aec3SBaptiste Daroussin curp.outtype = OUTT_PDF; 298*45a5aec3SBaptiste Daroussin else { 299*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, 300*45a5aec3SBaptiste Daroussin "-T %s", optarg); 301*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 302*45a5aec3SBaptiste Daroussin } 30361d06d6bSBaptiste Daroussin break; 30461d06d6bSBaptiste Daroussin case 'W': 305*45a5aec3SBaptiste Daroussin if (woptions(&curp, optarg) == -1) 306*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 30761d06d6bSBaptiste Daroussin break; 30861d06d6bSBaptiste Daroussin case 'w': 30961d06d6bSBaptiste Daroussin outmode = OUTMODE_FLN; 31061d06d6bSBaptiste Daroussin break; 31161d06d6bSBaptiste Daroussin default: 31261d06d6bSBaptiste Daroussin show_usage = 1; 31361d06d6bSBaptiste Daroussin break; 31461d06d6bSBaptiste Daroussin } 31561d06d6bSBaptiste Daroussin } 31661d06d6bSBaptiste Daroussin 31761d06d6bSBaptiste Daroussin if (show_usage) 31861d06d6bSBaptiste Daroussin usage(search.argmode); 31961d06d6bSBaptiste Daroussin 32061d06d6bSBaptiste Daroussin /* Postprocess options. */ 32161d06d6bSBaptiste Daroussin 32261d06d6bSBaptiste Daroussin if (outmode == OUTMODE_DEF) { 32361d06d6bSBaptiste Daroussin switch (search.argmode) { 32461d06d6bSBaptiste Daroussin case ARG_FILE: 32561d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 32661d06d6bSBaptiste Daroussin use_pager = 0; 32761d06d6bSBaptiste Daroussin break; 32861d06d6bSBaptiste Daroussin case ARG_NAME: 32961d06d6bSBaptiste Daroussin outmode = OUTMODE_ONE; 33061d06d6bSBaptiste Daroussin break; 33161d06d6bSBaptiste Daroussin default: 33261d06d6bSBaptiste Daroussin outmode = OUTMODE_LST; 33361d06d6bSBaptiste Daroussin break; 33461d06d6bSBaptiste Daroussin } 33561d06d6bSBaptiste Daroussin } 33661d06d6bSBaptiste Daroussin 33761d06d6bSBaptiste Daroussin if (oarg != NULL) { 33861d06d6bSBaptiste Daroussin if (outmode == OUTMODE_LST) 33961d06d6bSBaptiste Daroussin search.outkey = oarg; 34061d06d6bSBaptiste Daroussin else { 34161d06d6bSBaptiste Daroussin while (oarg != NULL) { 34261d06d6bSBaptiste Daroussin if (manconf_output(&conf.output, 343*45a5aec3SBaptiste Daroussin strsep(&oarg, ","), 0) == -1) 344*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 34561d06d6bSBaptiste Daroussin } 34661d06d6bSBaptiste Daroussin } 34761d06d6bSBaptiste Daroussin } 34861d06d6bSBaptiste Daroussin 3497295610fSBaptiste Daroussin if (curp.outtype != OUTT_TREE || !curp.outopts->noval) 3507295610fSBaptiste Daroussin options |= MPARSE_VALIDATE; 3517295610fSBaptiste Daroussin 35261d06d6bSBaptiste Daroussin if (outmode == OUTMODE_FLN || 35361d06d6bSBaptiste Daroussin outmode == OUTMODE_LST || 35461d06d6bSBaptiste Daroussin !isatty(STDOUT_FILENO)) 35561d06d6bSBaptiste Daroussin use_pager = 0; 35661d06d6bSBaptiste Daroussin 35761d06d6bSBaptiste Daroussin if (use_pager && 35861d06d6bSBaptiste Daroussin (conf.output.width == 0 || conf.output.indent == 0) && 35961d06d6bSBaptiste Daroussin ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && 36061d06d6bSBaptiste Daroussin ws.ws_col > 1) { 36161d06d6bSBaptiste Daroussin if (conf.output.width == 0 && ws.ws_col < 79) 36261d06d6bSBaptiste Daroussin conf.output.width = ws.ws_col - 1; 36361d06d6bSBaptiste Daroussin if (conf.output.indent == 0 && ws.ws_col < 66) 36461d06d6bSBaptiste Daroussin conf.output.indent = 3; 36561d06d6bSBaptiste Daroussin } 36661d06d6bSBaptiste Daroussin 36761d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 368*45a5aec3SBaptiste Daroussin if (use_pager == 0) { 369*45a5aec3SBaptiste Daroussin if (pledge("stdio rpath", NULL) == -1) { 370*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, 371*45a5aec3SBaptiste Daroussin "%s", strerror(errno)); 372*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 373*45a5aec3SBaptiste Daroussin } 374*45a5aec3SBaptiste Daroussin } 37561d06d6bSBaptiste Daroussin #endif 37661d06d6bSBaptiste Daroussin 37761d06d6bSBaptiste Daroussin /* Parse arguments. */ 37861d06d6bSBaptiste Daroussin 37961d06d6bSBaptiste Daroussin if (argc > 0) { 38061d06d6bSBaptiste Daroussin argc -= optind; 38161d06d6bSBaptiste Daroussin argv += optind; 38261d06d6bSBaptiste Daroussin } 38361d06d6bSBaptiste Daroussin resp = NULL; 38461d06d6bSBaptiste Daroussin 38561d06d6bSBaptiste Daroussin /* 38661d06d6bSBaptiste Daroussin * Quirks for help(1) 38761d06d6bSBaptiste Daroussin * and for a man(1) section argument without -s. 38861d06d6bSBaptiste Daroussin */ 38961d06d6bSBaptiste Daroussin 39061d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME) { 39161d06d6bSBaptiste Daroussin if (*progname == 'h') { 39261d06d6bSBaptiste Daroussin if (argc == 0) { 39361d06d6bSBaptiste Daroussin argv = help_argv; 39461d06d6bSBaptiste Daroussin argc = 1; 39561d06d6bSBaptiste Daroussin } 39661d06d6bSBaptiste Daroussin } else if (argc > 1 && 39761d06d6bSBaptiste Daroussin ((uc = (unsigned char *)argv[0]) != NULL) && 39861d06d6bSBaptiste Daroussin ((isdigit(uc[0]) && (uc[1] == '\0' || 399*45a5aec3SBaptiste Daroussin isalpha(uc[1]))) || 40061d06d6bSBaptiste Daroussin (uc[0] == 'n' && uc[1] == '\0'))) { 40161d06d6bSBaptiste Daroussin search.sec = (char *)uc; 40261d06d6bSBaptiste Daroussin argv++; 40361d06d6bSBaptiste Daroussin argc--; 40461d06d6bSBaptiste Daroussin } 40561d06d6bSBaptiste Daroussin if (search.arch == NULL) 40661d06d6bSBaptiste Daroussin search.arch = getenv("MACHINE"); 40761d06d6bSBaptiste Daroussin #ifdef MACHINE 40861d06d6bSBaptiste Daroussin if (search.arch == NULL) 40961d06d6bSBaptiste Daroussin search.arch = MACHINE; 41061d06d6bSBaptiste Daroussin #endif 41161d06d6bSBaptiste Daroussin } 41261d06d6bSBaptiste Daroussin 4137295610fSBaptiste Daroussin /* 4147295610fSBaptiste Daroussin * Use the first argument for -O tag in addition to 4157295610fSBaptiste Daroussin * using it as a search term for man(1) or apropos(1). 4167295610fSBaptiste Daroussin */ 4177295610fSBaptiste Daroussin 4187295610fSBaptiste Daroussin if (conf.output.tag != NULL && *conf.output.tag == '\0') { 4197295610fSBaptiste Daroussin tagarg = argc > 0 && search.argmode == ARG_EXPR ? 4207295610fSBaptiste Daroussin strchr(*argv, '=') : NULL; 4217295610fSBaptiste Daroussin conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; 4227295610fSBaptiste Daroussin } 42361d06d6bSBaptiste Daroussin 42461d06d6bSBaptiste Daroussin /* man(1), whatis(1), apropos(1) */ 42561d06d6bSBaptiste Daroussin 42661d06d6bSBaptiste Daroussin if (search.argmode != ARG_FILE) { 42761d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME && 42861d06d6bSBaptiste Daroussin outmode == OUTMODE_ONE) 42961d06d6bSBaptiste Daroussin search.firstmatch = 1; 43061d06d6bSBaptiste Daroussin 431a10034cbSYuri Pankov #ifdef __FreeBSD__ 432a10034cbSYuri Pankov /* 433a10034cbSYuri Pankov * Use manpath(1) to populate defpaths if -M is not specified. 434a10034cbSYuri Pankov * Don't treat any failures as fatal. 435a10034cbSYuri Pankov */ 436a10034cbSYuri Pankov if (defpaths == NULL) { 437a10034cbSYuri Pankov FILE *fp; 438a10034cbSYuri Pankov size_t linecap = 0; 439a10034cbSYuri Pankov ssize_t linelen; 440a10034cbSYuri Pankov 441a10034cbSYuri Pankov if ((fp = popen("/usr/bin/manpath -q", "r")) != NULL) { 442a10034cbSYuri Pankov if ((linelen = getline(&defpaths, 443a10034cbSYuri Pankov &linecap, fp)) > 0) { 444a10034cbSYuri Pankov /* Strip trailing newline */ 445a10034cbSYuri Pankov defpaths[linelen - 1] = '\0'; 446a10034cbSYuri Pankov } 447a10034cbSYuri Pankov pclose(fp); 448a10034cbSYuri Pankov } 449a10034cbSYuri Pankov } 450a10034cbSYuri Pankov #endif 451a10034cbSYuri Pankov 45261d06d6bSBaptiste Daroussin /* Access the mandoc database. */ 45361d06d6bSBaptiste Daroussin 45461d06d6bSBaptiste Daroussin manconf_parse(&conf, conf_file, defpaths, auxpaths); 455a10034cbSYuri Pankov #ifdef __FreeBSD__ 456a10034cbSYuri Pankov free(defpaths); 457a10034cbSYuri Pankov #endif 458a10034cbSYuri Pankov 45961d06d6bSBaptiste Daroussin if ( ! mansearch(&search, &conf.manpath, 46061d06d6bSBaptiste Daroussin argc, argv, &res, &sz)) 46161d06d6bSBaptiste Daroussin usage(search.argmode); 46261d06d6bSBaptiste Daroussin 46361d06d6bSBaptiste Daroussin if (sz == 0 && search.argmode == ARG_NAME) 464*45a5aec3SBaptiste Daroussin (void)fs_search(&search, &conf.manpath, 46561d06d6bSBaptiste Daroussin argc, argv, &res, &sz); 46661d06d6bSBaptiste Daroussin 46761d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME) { 46861d06d6bSBaptiste Daroussin for (c = 0; c < argc; c++) { 46961d06d6bSBaptiste Daroussin if (strchr(argv[c], '/') == NULL) 47061d06d6bSBaptiste Daroussin continue; 47161d06d6bSBaptiste Daroussin if (access(argv[c], R_OK) == -1) { 472*45a5aec3SBaptiste Daroussin mandoc_msg_setinfilename(argv[c]); 473*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 474*45a5aec3SBaptiste Daroussin 0, 0, "%s", strerror(errno)); 475*45a5aec3SBaptiste Daroussin mandoc_msg_setinfilename(NULL); 47661d06d6bSBaptiste Daroussin continue; 47761d06d6bSBaptiste Daroussin } 47861d06d6bSBaptiste Daroussin res = mandoc_reallocarray(res, 47961d06d6bSBaptiste Daroussin sz + 1, sizeof(*res)); 48061d06d6bSBaptiste Daroussin res[sz].file = mandoc_strdup(argv[c]); 48161d06d6bSBaptiste Daroussin res[sz].names = NULL; 48261d06d6bSBaptiste Daroussin res[sz].output = NULL; 483*45a5aec3SBaptiste Daroussin res[sz].bits = 0; 48461d06d6bSBaptiste Daroussin res[sz].ipath = SIZE_MAX; 48561d06d6bSBaptiste Daroussin res[sz].sec = 10; 48661d06d6bSBaptiste Daroussin res[sz].form = FORM_SRC; 48761d06d6bSBaptiste Daroussin sz++; 48861d06d6bSBaptiste Daroussin } 48961d06d6bSBaptiste Daroussin } 49061d06d6bSBaptiste Daroussin 49161d06d6bSBaptiste Daroussin if (sz == 0) { 49261d06d6bSBaptiste Daroussin if (search.argmode != ARG_NAME) 49361d06d6bSBaptiste Daroussin warnx("nothing appropriate"); 4947295610fSBaptiste Daroussin mandoc_msg_setrc(MANDOCLEVEL_BADARG); 49561d06d6bSBaptiste Daroussin goto out; 49661d06d6bSBaptiste Daroussin } 49761d06d6bSBaptiste Daroussin 49861d06d6bSBaptiste Daroussin /* 49961d06d6bSBaptiste Daroussin * For standard man(1) and -a output mode, 50061d06d6bSBaptiste Daroussin * prepare for copying filename pointers 50161d06d6bSBaptiste Daroussin * into the program parameter array. 50261d06d6bSBaptiste Daroussin */ 50361d06d6bSBaptiste Daroussin 50461d06d6bSBaptiste Daroussin if (outmode == OUTMODE_ONE) { 50561d06d6bSBaptiste Daroussin argc = 1; 506*45a5aec3SBaptiste Daroussin best_prio = 40; 50761d06d6bSBaptiste Daroussin } else if (outmode == OUTMODE_ALL) 50861d06d6bSBaptiste Daroussin argc = (int)sz; 50961d06d6bSBaptiste Daroussin 51061d06d6bSBaptiste Daroussin /* Iterate all matching manuals. */ 51161d06d6bSBaptiste Daroussin 51261d06d6bSBaptiste Daroussin resp = res; 51361d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) { 51461d06d6bSBaptiste Daroussin if (outmode == OUTMODE_FLN) 51561d06d6bSBaptiste Daroussin puts(res[i].file); 51661d06d6bSBaptiste Daroussin else if (outmode == OUTMODE_LST) 51761d06d6bSBaptiste Daroussin printf("%s - %s\n", res[i].names, 51861d06d6bSBaptiste Daroussin res[i].output == NULL ? "" : 51961d06d6bSBaptiste Daroussin res[i].output); 52061d06d6bSBaptiste Daroussin else if (outmode == OUTMODE_ONE) { 52161d06d6bSBaptiste Daroussin /* Search for the best section. */ 52261d06d6bSBaptiste Daroussin sec = res[i].file; 52361d06d6bSBaptiste Daroussin sec += strcspn(sec, "123456789"); 52461d06d6bSBaptiste Daroussin if (sec[0] == '\0') 525*45a5aec3SBaptiste Daroussin continue; /* No section at all. */ 52661d06d6bSBaptiste Daroussin prio = sec_prios[sec[0] - '1']; 527*45a5aec3SBaptiste Daroussin if (search.sec != NULL) { 528*45a5aec3SBaptiste Daroussin ssz = strlen(search.sec); 529*45a5aec3SBaptiste Daroussin if (strncmp(sec, search.sec, ssz) == 0) 530*45a5aec3SBaptiste Daroussin sec += ssz; 531*45a5aec3SBaptiste Daroussin } else 532*45a5aec3SBaptiste Daroussin sec++; /* Prefer without suffix. */ 533*45a5aec3SBaptiste Daroussin if (*sec != '/') 534*45a5aec3SBaptiste Daroussin prio += 10; /* Wrong dir name. */ 535*45a5aec3SBaptiste Daroussin if (search.sec != NULL && 536*45a5aec3SBaptiste Daroussin (strlen(sec) <= ssz + 3 || 537*45a5aec3SBaptiste Daroussin strcmp(sec + strlen(sec) - ssz, 538*45a5aec3SBaptiste Daroussin search.sec) != 0)) 539*45a5aec3SBaptiste Daroussin prio += 20; /* Wrong file ext. */ 54061d06d6bSBaptiste Daroussin if (prio >= best_prio) 54161d06d6bSBaptiste Daroussin continue; 54261d06d6bSBaptiste Daroussin best_prio = prio; 54361d06d6bSBaptiste Daroussin resp = res + i; 54461d06d6bSBaptiste Daroussin } 54561d06d6bSBaptiste Daroussin } 54661d06d6bSBaptiste Daroussin 54761d06d6bSBaptiste Daroussin /* 54861d06d6bSBaptiste Daroussin * For man(1), -a and -i output mode, fall through 54961d06d6bSBaptiste Daroussin * to the main mandoc(1) code iterating files 55061d06d6bSBaptiste Daroussin * and running the parsers on each of them. 55161d06d6bSBaptiste Daroussin */ 55261d06d6bSBaptiste Daroussin 55361d06d6bSBaptiste Daroussin if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) 55461d06d6bSBaptiste Daroussin goto out; 55561d06d6bSBaptiste Daroussin } 55661d06d6bSBaptiste Daroussin 55761d06d6bSBaptiste Daroussin /* mandoc(1) */ 55861d06d6bSBaptiste Daroussin 55961d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 56061d06d6bSBaptiste Daroussin if (use_pager) { 561*45a5aec3SBaptiste Daroussin if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) { 562*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, 563*45a5aec3SBaptiste Daroussin "%s", strerror(errno)); 564*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 565*45a5aec3SBaptiste Daroussin } 56661d06d6bSBaptiste Daroussin } else { 567*45a5aec3SBaptiste Daroussin if (pledge("stdio rpath", NULL) == -1) { 568*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, 569*45a5aec3SBaptiste Daroussin "%s", strerror(errno)); 570*45a5aec3SBaptiste Daroussin return mandoc_msg_getrc(); 571*45a5aec3SBaptiste Daroussin } 57261d06d6bSBaptiste Daroussin } 57361d06d6bSBaptiste Daroussin #endif 57461d06d6bSBaptiste Daroussin 575*45a5aec3SBaptiste Daroussin if (search.argmode == ARG_FILE && auxpaths != NULL) { 576*45a5aec3SBaptiste Daroussin if (strcmp(auxpaths, "doc") == 0) 577*45a5aec3SBaptiste Daroussin options |= MPARSE_MDOC; 578*45a5aec3SBaptiste Daroussin else if (strcmp(auxpaths, "an") == 0) 579*45a5aec3SBaptiste Daroussin options |= MPARSE_MAN; 580*45a5aec3SBaptiste Daroussin } 58161d06d6bSBaptiste Daroussin 58261d06d6bSBaptiste Daroussin mchars_alloc(); 5837295610fSBaptiste Daroussin curp.mp = mparse_alloc(options, curp.os_e, curp.os_s); 58461d06d6bSBaptiste Daroussin 58561d06d6bSBaptiste Daroussin if (argc < 1) { 5867295610fSBaptiste Daroussin if (use_pager) { 58761d06d6bSBaptiste Daroussin tag_files = tag_init(); 588*45a5aec3SBaptiste Daroussin if (tag_files != NULL) 5897295610fSBaptiste Daroussin tag_files->tagname = conf.output.tag; 5907295610fSBaptiste Daroussin } 5917295610fSBaptiste Daroussin thisarg = "<stdin>"; 5927295610fSBaptiste Daroussin mandoc_msg_setinfilename(thisarg); 5937295610fSBaptiste Daroussin parse(&curp, STDIN_FILENO, thisarg); 5947295610fSBaptiste Daroussin mandoc_msg_setinfilename(NULL); 59561d06d6bSBaptiste Daroussin } 59661d06d6bSBaptiste Daroussin 59761d06d6bSBaptiste Daroussin /* 59861d06d6bSBaptiste Daroussin * Remember the original working directory, if possible. 59961d06d6bSBaptiste Daroussin * This will be needed if some names on the command line 60061d06d6bSBaptiste Daroussin * are page names and some are relative file names. 60161d06d6bSBaptiste Daroussin * Do not error out if the current directory is not 60261d06d6bSBaptiste Daroussin * readable: Maybe it won't be needed after all. 60361d06d6bSBaptiste Daroussin */ 60461d06d6bSBaptiste Daroussin startdir = open(".", O_RDONLY | O_DIRECTORY); 60561d06d6bSBaptiste Daroussin 60661d06d6bSBaptiste Daroussin while (argc > 0) { 60761d06d6bSBaptiste Daroussin 60861d06d6bSBaptiste Daroussin /* 60961d06d6bSBaptiste Daroussin * Changing directories is not needed in ARG_FILE mode. 61061d06d6bSBaptiste Daroussin * Do it on a best-effort basis. Even in case of 61161d06d6bSBaptiste Daroussin * failure, some functionality may still work. 61261d06d6bSBaptiste Daroussin */ 61361d06d6bSBaptiste Daroussin if (resp != NULL) { 61461d06d6bSBaptiste Daroussin if (resp->ipath != SIZE_MAX) 61561d06d6bSBaptiste Daroussin (void)chdir(conf.manpath.paths[resp->ipath]); 61661d06d6bSBaptiste Daroussin else if (startdir != -1) 61761d06d6bSBaptiste Daroussin (void)fchdir(startdir); 6187295610fSBaptiste Daroussin thisarg = resp->file; 6197295610fSBaptiste Daroussin } else 6207295610fSBaptiste Daroussin thisarg = *argv; 62161d06d6bSBaptiste Daroussin 622*45a5aec3SBaptiste Daroussin mandoc_msg_setinfilename(thisarg); 6237295610fSBaptiste Daroussin fd = mparse_open(curp.mp, thisarg); 62461d06d6bSBaptiste Daroussin if (fd != -1) { 62561d06d6bSBaptiste Daroussin if (use_pager) { 62661d06d6bSBaptiste Daroussin use_pager = 0; 6277295610fSBaptiste Daroussin tag_files = tag_init(); 628*45a5aec3SBaptiste Daroussin if (tag_files != NULL) 6297295610fSBaptiste Daroussin tag_files->tagname = conf.output.tag; 63061d06d6bSBaptiste Daroussin } 63161d06d6bSBaptiste Daroussin 6327295610fSBaptiste Daroussin if (resp == NULL || resp->form == FORM_SRC) 6337295610fSBaptiste Daroussin parse(&curp, fd, thisarg); 63461d06d6bSBaptiste Daroussin else 635*45a5aec3SBaptiste Daroussin passthrough(fd, conf.output.synopsisonly); 63661d06d6bSBaptiste Daroussin 63761d06d6bSBaptiste Daroussin if (ferror(stdout)) { 63861d06d6bSBaptiste Daroussin if (tag_files != NULL) { 639*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_WRITE, 0, 0, 640*45a5aec3SBaptiste Daroussin "%s: %s", tag_files->ofn, 641*45a5aec3SBaptiste Daroussin strerror(errno)); 64261d06d6bSBaptiste Daroussin tag_unlink(); 64361d06d6bSBaptiste Daroussin tag_files = NULL; 64461d06d6bSBaptiste Daroussin } else 645*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_WRITE, 0, 0, 646*45a5aec3SBaptiste Daroussin "%s", strerror(errno)); 64761d06d6bSBaptiste Daroussin break; 64861d06d6bSBaptiste Daroussin } 64961d06d6bSBaptiste Daroussin 65061d06d6bSBaptiste Daroussin if (argc > 1 && curp.outtype <= OUTT_UTF8) { 65161d06d6bSBaptiste Daroussin if (curp.outdata == NULL) 65261d06d6bSBaptiste Daroussin outdata_alloc(&curp); 65361d06d6bSBaptiste Daroussin terminal_sepline(curp.outdata); 65461d06d6bSBaptiste Daroussin } 6557295610fSBaptiste Daroussin } else 656*45a5aec3SBaptiste Daroussin mandoc_msg(resp == NULL ? MANDOCERR_BADARG_BAD : 657*45a5aec3SBaptiste Daroussin MANDOCERR_OPEN, 0, 0, "%s", strerror(errno)); 658*45a5aec3SBaptiste Daroussin 659*45a5aec3SBaptiste Daroussin mandoc_msg_setinfilename(NULL); 66061d06d6bSBaptiste Daroussin 6617295610fSBaptiste Daroussin if (curp.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) 66261d06d6bSBaptiste Daroussin break; 66361d06d6bSBaptiste Daroussin 66461d06d6bSBaptiste Daroussin if (resp != NULL) 66561d06d6bSBaptiste Daroussin resp++; 66661d06d6bSBaptiste Daroussin else 66761d06d6bSBaptiste Daroussin argv++; 66861d06d6bSBaptiste Daroussin if (--argc) 66961d06d6bSBaptiste Daroussin mparse_reset(curp.mp); 67061d06d6bSBaptiste Daroussin } 67161d06d6bSBaptiste Daroussin if (startdir != -1) { 67261d06d6bSBaptiste Daroussin (void)fchdir(startdir); 67361d06d6bSBaptiste Daroussin close(startdir); 67461d06d6bSBaptiste Daroussin } 67561d06d6bSBaptiste Daroussin 67661d06d6bSBaptiste Daroussin if (curp.outdata != NULL) { 67761d06d6bSBaptiste Daroussin switch (curp.outtype) { 67861d06d6bSBaptiste Daroussin case OUTT_HTML: 67961d06d6bSBaptiste Daroussin html_free(curp.outdata); 68061d06d6bSBaptiste Daroussin break; 68161d06d6bSBaptiste Daroussin case OUTT_UTF8: 68261d06d6bSBaptiste Daroussin case OUTT_LOCALE: 68361d06d6bSBaptiste Daroussin case OUTT_ASCII: 68461d06d6bSBaptiste Daroussin ascii_free(curp.outdata); 68561d06d6bSBaptiste Daroussin break; 68661d06d6bSBaptiste Daroussin case OUTT_PDF: 68761d06d6bSBaptiste Daroussin case OUTT_PS: 68861d06d6bSBaptiste Daroussin pspdf_free(curp.outdata); 68961d06d6bSBaptiste Daroussin break; 69061d06d6bSBaptiste Daroussin default: 69161d06d6bSBaptiste Daroussin break; 69261d06d6bSBaptiste Daroussin } 69361d06d6bSBaptiste Daroussin } 69461d06d6bSBaptiste Daroussin mandoc_xr_free(); 69561d06d6bSBaptiste Daroussin mparse_free(curp.mp); 69661d06d6bSBaptiste Daroussin mchars_free(); 69761d06d6bSBaptiste Daroussin 69861d06d6bSBaptiste Daroussin out: 69961d06d6bSBaptiste Daroussin if (search.argmode != ARG_FILE) { 70061d06d6bSBaptiste Daroussin manconf_free(&conf); 70161d06d6bSBaptiste Daroussin mansearch_free(res, sz); 70261d06d6bSBaptiste Daroussin } 70361d06d6bSBaptiste Daroussin 70461d06d6bSBaptiste Daroussin free(curp.os_s); 70561d06d6bSBaptiste Daroussin 70661d06d6bSBaptiste Daroussin /* 70761d06d6bSBaptiste Daroussin * When using a pager, finish writing both temporary files, 70861d06d6bSBaptiste Daroussin * fork it, wait for the user to close it, and clean up. 70961d06d6bSBaptiste Daroussin */ 71061d06d6bSBaptiste Daroussin 71161d06d6bSBaptiste Daroussin if (tag_files != NULL) { 71261d06d6bSBaptiste Daroussin fclose(stdout); 71361d06d6bSBaptiste Daroussin tag_write(); 71461d06d6bSBaptiste Daroussin man_pgid = getpgid(0); 71561d06d6bSBaptiste Daroussin tag_files->tcpgid = man_pgid == getpid() ? 71661d06d6bSBaptiste Daroussin getpgid(getppid()) : man_pgid; 71761d06d6bSBaptiste Daroussin pager_pid = 0; 71861d06d6bSBaptiste Daroussin signum = SIGSTOP; 71961d06d6bSBaptiste Daroussin for (;;) { 72061d06d6bSBaptiste Daroussin 72161d06d6bSBaptiste Daroussin /* Stop here until moved to the foreground. */ 72261d06d6bSBaptiste Daroussin 72361d06d6bSBaptiste Daroussin tc_pgid = tcgetpgrp(tag_files->ofd); 72461d06d6bSBaptiste Daroussin if (tc_pgid != man_pgid) { 72561d06d6bSBaptiste Daroussin if (tc_pgid == pager_pid) { 72661d06d6bSBaptiste Daroussin (void)tcsetpgrp(tag_files->ofd, 72761d06d6bSBaptiste Daroussin man_pgid); 72861d06d6bSBaptiste Daroussin if (signum == SIGTTIN) 72961d06d6bSBaptiste Daroussin continue; 73061d06d6bSBaptiste Daroussin } else 73161d06d6bSBaptiste Daroussin tag_files->tcpgid = tc_pgid; 73261d06d6bSBaptiste Daroussin kill(0, signum); 73361d06d6bSBaptiste Daroussin continue; 73461d06d6bSBaptiste Daroussin } 73561d06d6bSBaptiste Daroussin 73661d06d6bSBaptiste Daroussin /* Once in the foreground, activate the pager. */ 73761d06d6bSBaptiste Daroussin 73861d06d6bSBaptiste Daroussin if (pager_pid) { 73961d06d6bSBaptiste Daroussin (void)tcsetpgrp(tag_files->ofd, pager_pid); 74061d06d6bSBaptiste Daroussin kill(pager_pid, SIGCONT); 74161d06d6bSBaptiste Daroussin } else 74261d06d6bSBaptiste Daroussin pager_pid = spawn_pager(tag_files); 74361d06d6bSBaptiste Daroussin 74461d06d6bSBaptiste Daroussin /* Wait for the pager to stop or exit. */ 74561d06d6bSBaptiste Daroussin 74661d06d6bSBaptiste Daroussin while ((pid = waitpid(pager_pid, &status, 74761d06d6bSBaptiste Daroussin WUNTRACED)) == -1 && errno == EINTR) 74861d06d6bSBaptiste Daroussin continue; 74961d06d6bSBaptiste Daroussin 75061d06d6bSBaptiste Daroussin if (pid == -1) { 751*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_WAIT, 0, 0, 752*45a5aec3SBaptiste Daroussin "%s", strerror(errno)); 75361d06d6bSBaptiste Daroussin break; 75461d06d6bSBaptiste Daroussin } 75561d06d6bSBaptiste Daroussin if (!WIFSTOPPED(status)) 75661d06d6bSBaptiste Daroussin break; 75761d06d6bSBaptiste Daroussin 75861d06d6bSBaptiste Daroussin signum = WSTOPSIG(status); 75961d06d6bSBaptiste Daroussin } 76061d06d6bSBaptiste Daroussin tag_unlink(); 761*45a5aec3SBaptiste Daroussin } else if (curp.outtype != OUTT_LINT && 762*45a5aec3SBaptiste Daroussin (search.argmode == ARG_FILE || sz > 0)) 763*45a5aec3SBaptiste Daroussin mandoc_msg_summary(); 764*45a5aec3SBaptiste Daroussin 7657295610fSBaptiste Daroussin return (int)mandoc_msg_getrc(); 76661d06d6bSBaptiste Daroussin } 76761d06d6bSBaptiste Daroussin 76861d06d6bSBaptiste Daroussin static void 76961d06d6bSBaptiste Daroussin usage(enum argmode argmode) 77061d06d6bSBaptiste Daroussin { 77161d06d6bSBaptiste Daroussin switch (argmode) { 77261d06d6bSBaptiste Daroussin case ARG_FILE: 77361d06d6bSBaptiste Daroussin fputs("usage: mandoc [-ac] [-I os=name] " 77461d06d6bSBaptiste Daroussin "[-K encoding] [-mdoc | -man] [-O options]\n" 77561d06d6bSBaptiste Daroussin "\t [-T output] [-W level] [file ...]\n", stderr); 77661d06d6bSBaptiste Daroussin break; 77761d06d6bSBaptiste Daroussin case ARG_NAME: 77861d06d6bSBaptiste Daroussin fputs("usage: man [-acfhklw] [-C file] [-M path] " 77961d06d6bSBaptiste Daroussin "[-m path] [-S subsection]\n" 78061d06d6bSBaptiste Daroussin "\t [[-s] section] name ...\n", stderr); 78161d06d6bSBaptiste Daroussin break; 78261d06d6bSBaptiste Daroussin case ARG_WORD: 78361d06d6bSBaptiste Daroussin fputs("usage: whatis [-afk] [-C file] " 78461d06d6bSBaptiste Daroussin "[-M path] [-m path] [-O outkey] [-S arch]\n" 78561d06d6bSBaptiste Daroussin "\t [-s section] name ...\n", stderr); 78661d06d6bSBaptiste Daroussin break; 78761d06d6bSBaptiste Daroussin case ARG_EXPR: 78861d06d6bSBaptiste Daroussin fputs("usage: apropos [-afk] [-C file] " 78961d06d6bSBaptiste Daroussin "[-M path] [-m path] [-O outkey] [-S arch]\n" 79061d06d6bSBaptiste Daroussin "\t [-s section] expression ...\n", stderr); 79161d06d6bSBaptiste Daroussin break; 79261d06d6bSBaptiste Daroussin } 79361d06d6bSBaptiste Daroussin exit((int)MANDOCLEVEL_BADARG); 79461d06d6bSBaptiste Daroussin } 79561d06d6bSBaptiste Daroussin 79661d06d6bSBaptiste Daroussin static int 79761d06d6bSBaptiste Daroussin fs_lookup(const struct manpaths *paths, size_t ipath, 79861d06d6bSBaptiste Daroussin const char *sec, const char *arch, const char *name, 79961d06d6bSBaptiste Daroussin struct manpage **res, size_t *ressz) 80061d06d6bSBaptiste Daroussin { 801*45a5aec3SBaptiste Daroussin struct stat sb; 80261d06d6bSBaptiste Daroussin glob_t globinfo; 80361d06d6bSBaptiste Daroussin struct manpage *page; 80461d06d6bSBaptiste Daroussin char *file; 80561d06d6bSBaptiste Daroussin int globres; 80661d06d6bSBaptiste Daroussin enum form form; 80761d06d6bSBaptiste Daroussin 80861d06d6bSBaptiste Daroussin form = FORM_SRC; 80961d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s.%s", 81061d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name, sec); 811*45a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) 81261d06d6bSBaptiste Daroussin goto found; 81361d06d6bSBaptiste Daroussin free(file); 81461d06d6bSBaptiste Daroussin 81561d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/cat%s/%s.0", 81661d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name); 817*45a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) { 81861d06d6bSBaptiste Daroussin form = FORM_CAT; 81961d06d6bSBaptiste Daroussin goto found; 82061d06d6bSBaptiste Daroussin } 82161d06d6bSBaptiste Daroussin free(file); 82261d06d6bSBaptiste Daroussin 82361d06d6bSBaptiste Daroussin if (arch != NULL) { 82461d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", 82561d06d6bSBaptiste Daroussin paths->paths[ipath], sec, arch, name, sec); 826*45a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) 82761d06d6bSBaptiste Daroussin goto found; 82861d06d6bSBaptiste Daroussin free(file); 82961d06d6bSBaptiste Daroussin } 83061d06d6bSBaptiste Daroussin 83161d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", 83261d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name); 83361d06d6bSBaptiste Daroussin globres = glob(file, 0, NULL, &globinfo); 83461d06d6bSBaptiste Daroussin if (globres != 0 && globres != GLOB_NOMATCH) 835*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GLOB, 0, 0, 836*45a5aec3SBaptiste Daroussin "%s: %s", file, strerror(errno)); 83761d06d6bSBaptiste Daroussin free(file); 83861d06d6bSBaptiste Daroussin if (globres == 0) 83961d06d6bSBaptiste Daroussin file = mandoc_strdup(*globinfo.gl_pathv); 84061d06d6bSBaptiste Daroussin globfree(&globinfo); 841*45a5aec3SBaptiste Daroussin if (globres == 0) { 842*45a5aec3SBaptiste Daroussin if (stat(file, &sb) != -1) 84361d06d6bSBaptiste Daroussin goto found; 844*45a5aec3SBaptiste Daroussin free(file); 845*45a5aec3SBaptiste Daroussin } 84661d06d6bSBaptiste Daroussin if (res != NULL || ipath + 1 != paths->sz) 847*45a5aec3SBaptiste Daroussin return -1; 84861d06d6bSBaptiste Daroussin 84961d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s.%s", name, sec); 850*45a5aec3SBaptiste Daroussin globres = stat(file, &sb); 85161d06d6bSBaptiste Daroussin free(file); 852*45a5aec3SBaptiste 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]); 85761d06d6bSBaptiste Daroussin if (res == NULL) { 85861d06d6bSBaptiste Daroussin free(file); 859*45a5aec3SBaptiste Daroussin return 0; 86061d06d6bSBaptiste Daroussin } 861*45a5aec3SBaptiste Daroussin *res = mandoc_reallocarray(*res, ++*ressz, sizeof(**res)); 86261d06d6bSBaptiste Daroussin page = *res + (*ressz - 1); 86361d06d6bSBaptiste Daroussin page->file = file; 86461d06d6bSBaptiste Daroussin page->names = NULL; 86561d06d6bSBaptiste Daroussin page->output = NULL; 866*45a5aec3SBaptiste Daroussin page->bits = NAME_FILE & NAME_MASK; 86761d06d6bSBaptiste Daroussin page->ipath = ipath; 86861d06d6bSBaptiste Daroussin page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; 86961d06d6bSBaptiste Daroussin page->form = form; 870*45a5aec3SBaptiste Daroussin return 0; 87161d06d6bSBaptiste Daroussin } 87261d06d6bSBaptiste Daroussin 87361d06d6bSBaptiste Daroussin static int 87461d06d6bSBaptiste Daroussin fs_search(const struct mansearch *cfg, const struct manpaths *paths, 87561d06d6bSBaptiste Daroussin int argc, char **argv, struct manpage **res, size_t *ressz) 87661d06d6bSBaptiste Daroussin { 87761d06d6bSBaptiste Daroussin const char *const sections[] = 87861d06d6bSBaptiste Daroussin {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; 87961d06d6bSBaptiste Daroussin const size_t nsec = sizeof(sections)/sizeof(sections[0]); 88061d06d6bSBaptiste Daroussin 88161d06d6bSBaptiste Daroussin size_t ipath, isec, lastsz; 88261d06d6bSBaptiste Daroussin 88361d06d6bSBaptiste Daroussin assert(cfg->argmode == ARG_NAME); 88461d06d6bSBaptiste Daroussin 88561d06d6bSBaptiste Daroussin if (res != NULL) 88661d06d6bSBaptiste Daroussin *res = NULL; 88761d06d6bSBaptiste Daroussin *ressz = lastsz = 0; 88861d06d6bSBaptiste Daroussin while (argc) { 88961d06d6bSBaptiste Daroussin for (ipath = 0; ipath < paths->sz; ipath++) { 89061d06d6bSBaptiste Daroussin if (cfg->sec != NULL) { 89161d06d6bSBaptiste Daroussin if (fs_lookup(paths, ipath, cfg->sec, 892*45a5aec3SBaptiste Daroussin cfg->arch, *argv, res, ressz) != -1 && 89361d06d6bSBaptiste Daroussin cfg->firstmatch) 894*45a5aec3SBaptiste Daroussin return 0; 89561d06d6bSBaptiste Daroussin } else for (isec = 0; isec < nsec; isec++) 89661d06d6bSBaptiste Daroussin if (fs_lookup(paths, ipath, sections[isec], 897*45a5aec3SBaptiste Daroussin cfg->arch, *argv, res, ressz) != -1 && 89861d06d6bSBaptiste Daroussin cfg->firstmatch) 899*45a5aec3SBaptiste Daroussin return 0; 90061d06d6bSBaptiste Daroussin } 90161d06d6bSBaptiste Daroussin if (res != NULL && *ressz == lastsz && 9027295610fSBaptiste Daroussin strchr(*argv, '/') == NULL) { 9037295610fSBaptiste Daroussin if (cfg->arch != NULL && 9047295610fSBaptiste Daroussin arch_valid(cfg->arch, OSENUM) == 0) 9057295610fSBaptiste Daroussin warnx("Unknown architecture \"%s\".", 9067295610fSBaptiste Daroussin cfg->arch); 9077295610fSBaptiste Daroussin else if (cfg->sec == NULL) 9087295610fSBaptiste Daroussin warnx("No entry for %s in the manual.", 9097295610fSBaptiste Daroussin *argv); 9107295610fSBaptiste Daroussin else 9117295610fSBaptiste Daroussin warnx("No entry for %s in section %s " 9127295610fSBaptiste Daroussin "of the manual.", *argv, cfg->sec); 9137295610fSBaptiste Daroussin } 91461d06d6bSBaptiste Daroussin lastsz = *ressz; 91561d06d6bSBaptiste Daroussin argv++; 91661d06d6bSBaptiste Daroussin argc--; 91761d06d6bSBaptiste Daroussin } 918*45a5aec3SBaptiste Daroussin return -1; 91961d06d6bSBaptiste Daroussin } 92061d06d6bSBaptiste Daroussin 92161d06d6bSBaptiste Daroussin static void 92261d06d6bSBaptiste Daroussin parse(struct curparse *curp, int fd, const char *file) 92361d06d6bSBaptiste Daroussin { 9247295610fSBaptiste Daroussin struct roff_meta *meta; 92561d06d6bSBaptiste Daroussin 92661d06d6bSBaptiste Daroussin /* Begin by parsing the file itself. */ 92761d06d6bSBaptiste Daroussin 92861d06d6bSBaptiste Daroussin assert(file); 92961d06d6bSBaptiste Daroussin assert(fd >= 0); 93061d06d6bSBaptiste Daroussin 9317295610fSBaptiste Daroussin mparse_readfd(curp->mp, fd, file); 93261d06d6bSBaptiste Daroussin if (fd != STDIN_FILENO) 93361d06d6bSBaptiste Daroussin close(fd); 93461d06d6bSBaptiste Daroussin 93561d06d6bSBaptiste Daroussin /* 93661d06d6bSBaptiste Daroussin * With -Wstop and warnings or errors of at least the requested 93761d06d6bSBaptiste Daroussin * level, do not produce output. 93861d06d6bSBaptiste Daroussin */ 93961d06d6bSBaptiste Daroussin 9407295610fSBaptiste Daroussin if (curp->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) 94161d06d6bSBaptiste Daroussin return; 94261d06d6bSBaptiste Daroussin 94361d06d6bSBaptiste Daroussin if (curp->outdata == NULL) 94461d06d6bSBaptiste Daroussin outdata_alloc(curp); 9457295610fSBaptiste Daroussin else if (curp->outtype == OUTT_HTML) 9467295610fSBaptiste Daroussin html_reset(curp); 94761d06d6bSBaptiste Daroussin 9487295610fSBaptiste Daroussin mandoc_xr_reset(); 9497295610fSBaptiste Daroussin meta = mparse_result(curp->mp); 95061d06d6bSBaptiste Daroussin 95161d06d6bSBaptiste Daroussin /* Execute the out device, if it exists. */ 95261d06d6bSBaptiste Daroussin 9537295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MDOC) { 95461d06d6bSBaptiste Daroussin switch (curp->outtype) { 95561d06d6bSBaptiste Daroussin case OUTT_HTML: 9567295610fSBaptiste Daroussin html_mdoc(curp->outdata, meta); 95761d06d6bSBaptiste Daroussin break; 95861d06d6bSBaptiste Daroussin case OUTT_TREE: 9597295610fSBaptiste Daroussin tree_mdoc(curp->outdata, meta); 96061d06d6bSBaptiste Daroussin break; 96161d06d6bSBaptiste Daroussin case OUTT_MAN: 9627295610fSBaptiste Daroussin man_mdoc(curp->outdata, meta); 96361d06d6bSBaptiste Daroussin break; 96461d06d6bSBaptiste Daroussin case OUTT_PDF: 96561d06d6bSBaptiste Daroussin case OUTT_ASCII: 96661d06d6bSBaptiste Daroussin case OUTT_UTF8: 96761d06d6bSBaptiste Daroussin case OUTT_LOCALE: 96861d06d6bSBaptiste Daroussin case OUTT_PS: 9697295610fSBaptiste Daroussin terminal_mdoc(curp->outdata, meta); 97061d06d6bSBaptiste Daroussin break; 97161d06d6bSBaptiste Daroussin case OUTT_MARKDOWN: 9727295610fSBaptiste Daroussin markdown_mdoc(curp->outdata, meta); 97361d06d6bSBaptiste Daroussin break; 97461d06d6bSBaptiste Daroussin default: 97561d06d6bSBaptiste Daroussin break; 97661d06d6bSBaptiste Daroussin } 97761d06d6bSBaptiste Daroussin } 9787295610fSBaptiste Daroussin if (meta->macroset == MACROSET_MAN) { 97961d06d6bSBaptiste Daroussin switch (curp->outtype) { 98061d06d6bSBaptiste Daroussin case OUTT_HTML: 9817295610fSBaptiste Daroussin html_man(curp->outdata, meta); 98261d06d6bSBaptiste Daroussin break; 98361d06d6bSBaptiste Daroussin case OUTT_TREE: 9847295610fSBaptiste Daroussin tree_man(curp->outdata, meta); 98561d06d6bSBaptiste Daroussin break; 98661d06d6bSBaptiste Daroussin case OUTT_MAN: 9877295610fSBaptiste Daroussin mparse_copy(curp->mp); 98861d06d6bSBaptiste Daroussin break; 98961d06d6bSBaptiste Daroussin case OUTT_PDF: 99061d06d6bSBaptiste Daroussin case OUTT_ASCII: 99161d06d6bSBaptiste Daroussin case OUTT_UTF8: 99261d06d6bSBaptiste Daroussin case OUTT_LOCALE: 99361d06d6bSBaptiste Daroussin case OUTT_PS: 9947295610fSBaptiste Daroussin terminal_man(curp->outdata, meta); 99561d06d6bSBaptiste Daroussin break; 99661d06d6bSBaptiste Daroussin default: 99761d06d6bSBaptiste Daroussin break; 99861d06d6bSBaptiste Daroussin } 99961d06d6bSBaptiste Daroussin } 10007295610fSBaptiste Daroussin if (mandoc_msg_getmin() < MANDOCERR_STYLE) 10017295610fSBaptiste Daroussin check_xr(); 100261d06d6bSBaptiste Daroussin } 100361d06d6bSBaptiste Daroussin 100461d06d6bSBaptiste Daroussin static void 10057295610fSBaptiste Daroussin check_xr(void) 100661d06d6bSBaptiste Daroussin { 100761d06d6bSBaptiste Daroussin static struct manpaths paths; 100861d06d6bSBaptiste Daroussin struct mansearch search; 100961d06d6bSBaptiste Daroussin struct mandoc_xr *xr; 101061d06d6bSBaptiste Daroussin size_t sz; 101161d06d6bSBaptiste Daroussin 101261d06d6bSBaptiste Daroussin if (paths.sz == 0) 101361d06d6bSBaptiste Daroussin manpath_base(&paths); 101461d06d6bSBaptiste Daroussin 101561d06d6bSBaptiste Daroussin for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { 101661d06d6bSBaptiste Daroussin if (xr->line == -1) 101761d06d6bSBaptiste Daroussin continue; 101861d06d6bSBaptiste Daroussin search.arch = NULL; 101961d06d6bSBaptiste Daroussin search.sec = xr->sec; 102061d06d6bSBaptiste Daroussin search.outkey = NULL; 102161d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 102261d06d6bSBaptiste Daroussin search.firstmatch = 1; 102361d06d6bSBaptiste Daroussin if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz)) 102461d06d6bSBaptiste Daroussin continue; 1025*45a5aec3SBaptiste Daroussin if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz) != -1) 102661d06d6bSBaptiste Daroussin continue; 102761d06d6bSBaptiste Daroussin if (xr->count == 1) 10287295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_XR_BAD, xr->line, 10297295610fSBaptiste Daroussin xr->pos + 1, "Xr %s %s", xr->name, xr->sec); 103061d06d6bSBaptiste Daroussin else 10317295610fSBaptiste Daroussin mandoc_msg(MANDOCERR_XR_BAD, xr->line, 10327295610fSBaptiste Daroussin xr->pos + 1, "Xr %s %s (%d times)", 103361d06d6bSBaptiste Daroussin xr->name, xr->sec, xr->count); 103461d06d6bSBaptiste Daroussin } 103561d06d6bSBaptiste Daroussin } 103661d06d6bSBaptiste Daroussin 103761d06d6bSBaptiste Daroussin static void 103861d06d6bSBaptiste Daroussin outdata_alloc(struct curparse *curp) 103961d06d6bSBaptiste Daroussin { 104061d06d6bSBaptiste Daroussin switch (curp->outtype) { 104161d06d6bSBaptiste Daroussin case OUTT_HTML: 104261d06d6bSBaptiste Daroussin curp->outdata = html_alloc(curp->outopts); 104361d06d6bSBaptiste Daroussin break; 104461d06d6bSBaptiste Daroussin case OUTT_UTF8: 104561d06d6bSBaptiste Daroussin curp->outdata = utf8_alloc(curp->outopts); 104661d06d6bSBaptiste Daroussin break; 104761d06d6bSBaptiste Daroussin case OUTT_LOCALE: 104861d06d6bSBaptiste Daroussin curp->outdata = locale_alloc(curp->outopts); 104961d06d6bSBaptiste Daroussin break; 105061d06d6bSBaptiste Daroussin case OUTT_ASCII: 105161d06d6bSBaptiste Daroussin curp->outdata = ascii_alloc(curp->outopts); 105261d06d6bSBaptiste Daroussin break; 105361d06d6bSBaptiste Daroussin case OUTT_PDF: 105461d06d6bSBaptiste Daroussin curp->outdata = pdf_alloc(curp->outopts); 105561d06d6bSBaptiste Daroussin break; 105661d06d6bSBaptiste Daroussin case OUTT_PS: 105761d06d6bSBaptiste Daroussin curp->outdata = ps_alloc(curp->outopts); 105861d06d6bSBaptiste Daroussin break; 105961d06d6bSBaptiste Daroussin default: 106061d06d6bSBaptiste Daroussin break; 106161d06d6bSBaptiste Daroussin } 106261d06d6bSBaptiste Daroussin } 106361d06d6bSBaptiste Daroussin 106461d06d6bSBaptiste Daroussin static void 1065*45a5aec3SBaptiste Daroussin passthrough(int fd, int synopsis_only) 106661d06d6bSBaptiste Daroussin { 106761d06d6bSBaptiste Daroussin const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; 106861d06d6bSBaptiste Daroussin const char synr[] = "SYNOPSIS"; 106961d06d6bSBaptiste Daroussin 107061d06d6bSBaptiste Daroussin FILE *stream; 107161d06d6bSBaptiste Daroussin char *line, *cp; 107261d06d6bSBaptiste Daroussin size_t linesz; 107361d06d6bSBaptiste Daroussin ssize_t len, written; 1074*45a5aec3SBaptiste Daroussin int lno, print; 107561d06d6bSBaptiste Daroussin 1076*45a5aec3SBaptiste Daroussin stream = NULL; 107761d06d6bSBaptiste Daroussin line = NULL; 107861d06d6bSBaptiste Daroussin linesz = 0; 107961d06d6bSBaptiste Daroussin 108061d06d6bSBaptiste Daroussin if (fflush(stdout) == EOF) { 1081*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FFLUSH, 0, 0, "%s", strerror(errno)); 1082*45a5aec3SBaptiste Daroussin goto done; 108361d06d6bSBaptiste Daroussin } 108461d06d6bSBaptiste Daroussin if ((stream = fdopen(fd, "r")) == NULL) { 108561d06d6bSBaptiste Daroussin close(fd); 1086*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); 1087*45a5aec3SBaptiste Daroussin goto done; 108861d06d6bSBaptiste Daroussin } 108961d06d6bSBaptiste Daroussin 1090*45a5aec3SBaptiste Daroussin lno = print = 0; 109161d06d6bSBaptiste Daroussin while ((len = getline(&line, &linesz, stream)) != -1) { 1092*45a5aec3SBaptiste Daroussin lno++; 109361d06d6bSBaptiste Daroussin cp = line; 109461d06d6bSBaptiste Daroussin if (synopsis_only) { 109561d06d6bSBaptiste Daroussin if (print) { 109661d06d6bSBaptiste Daroussin if ( ! isspace((unsigned char)*cp)) 109761d06d6bSBaptiste Daroussin goto done; 109861d06d6bSBaptiste Daroussin while (isspace((unsigned char)*cp)) { 109961d06d6bSBaptiste Daroussin cp++; 110061d06d6bSBaptiste Daroussin len--; 110161d06d6bSBaptiste Daroussin } 110261d06d6bSBaptiste Daroussin } else { 110361d06d6bSBaptiste Daroussin if (strcmp(cp, synb) == 0 || 110461d06d6bSBaptiste Daroussin strcmp(cp, synr) == 0) 110561d06d6bSBaptiste Daroussin print = 1; 110661d06d6bSBaptiste Daroussin continue; 110761d06d6bSBaptiste Daroussin } 110861d06d6bSBaptiste Daroussin } 110961d06d6bSBaptiste Daroussin for (; len > 0; len -= written) { 1110*45a5aec3SBaptiste Daroussin if ((written = write(STDOUT_FILENO, cp, len)) == -1) { 1111*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_WRITE, 0, 0, 1112*45a5aec3SBaptiste Daroussin "%s", strerror(errno)); 1113*45a5aec3SBaptiste Daroussin goto done; 111461d06d6bSBaptiste Daroussin } 111561d06d6bSBaptiste Daroussin } 111661d06d6bSBaptiste Daroussin } 1117*45a5aec3SBaptiste Daroussin if (ferror(stream)) 1118*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_GETLINE, lno, 0, "%s", strerror(errno)); 111961d06d6bSBaptiste Daroussin 112061d06d6bSBaptiste Daroussin done: 112161d06d6bSBaptiste Daroussin free(line); 1122*45a5aec3SBaptiste Daroussin if (stream != NULL) 112361d06d6bSBaptiste Daroussin fclose(stream); 112461d06d6bSBaptiste Daroussin } 112561d06d6bSBaptiste Daroussin 112661d06d6bSBaptiste Daroussin static int 112761d06d6bSBaptiste Daroussin woptions(struct curparse *curp, char *arg) 112861d06d6bSBaptiste Daroussin { 112961d06d6bSBaptiste Daroussin char *v, *o; 113061d06d6bSBaptiste Daroussin const char *toks[11]; 113161d06d6bSBaptiste Daroussin 113261d06d6bSBaptiste Daroussin toks[0] = "stop"; 113361d06d6bSBaptiste Daroussin toks[1] = "all"; 113461d06d6bSBaptiste Daroussin toks[2] = "base"; 113561d06d6bSBaptiste Daroussin toks[3] = "style"; 113661d06d6bSBaptiste Daroussin toks[4] = "warning"; 113761d06d6bSBaptiste Daroussin toks[5] = "error"; 113861d06d6bSBaptiste Daroussin toks[6] = "unsupp"; 113961d06d6bSBaptiste Daroussin toks[7] = "fatal"; 114061d06d6bSBaptiste Daroussin toks[8] = "openbsd"; 114161d06d6bSBaptiste Daroussin toks[9] = "netbsd"; 114261d06d6bSBaptiste Daroussin toks[10] = NULL; 114361d06d6bSBaptiste Daroussin 114461d06d6bSBaptiste Daroussin while (*arg) { 114561d06d6bSBaptiste Daroussin o = arg; 114661d06d6bSBaptiste Daroussin switch (getsubopt(&arg, (char * const *)toks, &v)) { 114761d06d6bSBaptiste Daroussin case 0: 114861d06d6bSBaptiste Daroussin curp->wstop = 1; 114961d06d6bSBaptiste Daroussin break; 115061d06d6bSBaptiste Daroussin case 1: 115161d06d6bSBaptiste Daroussin case 2: 11527295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 115361d06d6bSBaptiste Daroussin break; 115461d06d6bSBaptiste Daroussin case 3: 11557295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_STYLE); 115661d06d6bSBaptiste Daroussin break; 115761d06d6bSBaptiste Daroussin case 4: 11587295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_WARNING); 115961d06d6bSBaptiste Daroussin break; 116061d06d6bSBaptiste Daroussin case 5: 11617295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_ERROR); 116261d06d6bSBaptiste Daroussin break; 116361d06d6bSBaptiste Daroussin case 6: 11647295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_UNSUPP); 116561d06d6bSBaptiste Daroussin break; 116661d06d6bSBaptiste Daroussin case 7: 1167*45a5aec3SBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BADARG); 116861d06d6bSBaptiste Daroussin break; 116961d06d6bSBaptiste Daroussin case 8: 11707295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 117161d06d6bSBaptiste Daroussin curp->os_e = MANDOC_OS_OPENBSD; 117261d06d6bSBaptiste Daroussin break; 117361d06d6bSBaptiste Daroussin case 9: 11747295610fSBaptiste Daroussin mandoc_msg_setmin(MANDOCERR_BASE); 117561d06d6bSBaptiste Daroussin curp->os_e = MANDOC_OS_NETBSD; 117661d06d6bSBaptiste Daroussin break; 117761d06d6bSBaptiste Daroussin default: 1178*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-W %s", o); 1179*45a5aec3SBaptiste Daroussin return -1; 1180*45a5aec3SBaptiste Daroussin } 1181*45a5aec3SBaptiste Daroussin } 118261d06d6bSBaptiste Daroussin return 0; 118361d06d6bSBaptiste Daroussin } 118461d06d6bSBaptiste Daroussin 118561d06d6bSBaptiste Daroussin static pid_t 118661d06d6bSBaptiste Daroussin spawn_pager(struct tag_files *tag_files) 118761d06d6bSBaptiste Daroussin { 118861d06d6bSBaptiste Daroussin const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ 118961d06d6bSBaptiste Daroussin #define MAX_PAGER_ARGS 16 119061d06d6bSBaptiste Daroussin char *argv[MAX_PAGER_ARGS]; 119161d06d6bSBaptiste Daroussin const char *pager; 119261d06d6bSBaptiste Daroussin char *cp; 11937295610fSBaptiste Daroussin #if HAVE_LESS_T 119461d06d6bSBaptiste Daroussin size_t cmdlen; 11957295610fSBaptiste Daroussin #endif 11967295610fSBaptiste Daroussin int argc, use_ofn; 119761d06d6bSBaptiste Daroussin pid_t pager_pid; 119861d06d6bSBaptiste Daroussin 119961d06d6bSBaptiste Daroussin pager = getenv("MANPAGER"); 120061d06d6bSBaptiste Daroussin if (pager == NULL || *pager == '\0') 120161d06d6bSBaptiste Daroussin pager = getenv("PAGER"); 120261d06d6bSBaptiste Daroussin if (pager == NULL || *pager == '\0') 120361d06d6bSBaptiste Daroussin pager = "less -s"; 120461d06d6bSBaptiste Daroussin cp = mandoc_strdup(pager); 120561d06d6bSBaptiste Daroussin 120661d06d6bSBaptiste Daroussin /* 120761d06d6bSBaptiste Daroussin * Parse the pager command into words. 120861d06d6bSBaptiste Daroussin * Intentionally do not do anything fancy here. 120961d06d6bSBaptiste Daroussin */ 121061d06d6bSBaptiste Daroussin 121161d06d6bSBaptiste Daroussin argc = 0; 12127295610fSBaptiste Daroussin while (argc + 5 < MAX_PAGER_ARGS) { 121361d06d6bSBaptiste Daroussin argv[argc++] = cp; 121461d06d6bSBaptiste Daroussin cp = strchr(cp, ' '); 121561d06d6bSBaptiste Daroussin if (cp == NULL) 121661d06d6bSBaptiste Daroussin break; 121761d06d6bSBaptiste Daroussin *cp++ = '\0'; 121861d06d6bSBaptiste Daroussin while (*cp == ' ') 121961d06d6bSBaptiste Daroussin cp++; 122061d06d6bSBaptiste Daroussin if (*cp == '\0') 122161d06d6bSBaptiste Daroussin break; 122261d06d6bSBaptiste Daroussin } 122361d06d6bSBaptiste Daroussin 122461d06d6bSBaptiste Daroussin /* For less(1), use the tag file. */ 122561d06d6bSBaptiste Daroussin 12267295610fSBaptiste Daroussin use_ofn = 1; 12277295610fSBaptiste Daroussin #if HAVE_LESS_T 1228*45a5aec3SBaptiste Daroussin if (*tag_files->tfn != '\0' && (cmdlen = strlen(argv[0])) >= 4) { 122961d06d6bSBaptiste Daroussin cp = argv[0] + cmdlen - 4; 123061d06d6bSBaptiste Daroussin if (strcmp(cp, "less") == 0) { 123161d06d6bSBaptiste Daroussin argv[argc++] = mandoc_strdup("-T"); 123261d06d6bSBaptiste Daroussin argv[argc++] = tag_files->tfn; 12337295610fSBaptiste Daroussin if (tag_files->tagname != NULL) { 12347295610fSBaptiste Daroussin argv[argc++] = mandoc_strdup("-t"); 12357295610fSBaptiste Daroussin argv[argc++] = tag_files->tagname; 12367295610fSBaptiste Daroussin use_ofn = 0; 123761d06d6bSBaptiste Daroussin } 123861d06d6bSBaptiste Daroussin } 12397295610fSBaptiste Daroussin } 12407295610fSBaptiste Daroussin #endif 12417295610fSBaptiste Daroussin if (use_ofn) 124261d06d6bSBaptiste Daroussin argv[argc++] = tag_files->ofn; 124361d06d6bSBaptiste Daroussin argv[argc] = NULL; 124461d06d6bSBaptiste Daroussin 124561d06d6bSBaptiste Daroussin switch (pager_pid = fork()) { 124661d06d6bSBaptiste Daroussin case -1: 1247*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_FORK, 0, 0, "%s", strerror(errno)); 1248*45a5aec3SBaptiste Daroussin exit(mandoc_msg_getrc()); 124961d06d6bSBaptiste Daroussin case 0: 125061d06d6bSBaptiste Daroussin break; 125161d06d6bSBaptiste Daroussin default: 125261d06d6bSBaptiste Daroussin (void)setpgid(pager_pid, 0); 125361d06d6bSBaptiste Daroussin (void)tcsetpgrp(tag_files->ofd, pager_pid); 125461d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 1255*45a5aec3SBaptiste Daroussin if (pledge("stdio rpath tmppath tty proc", NULL) == -1) { 1256*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_PLEDGE, 0, 0, 1257*45a5aec3SBaptiste Daroussin "%s", strerror(errno)); 1258*45a5aec3SBaptiste Daroussin exit(mandoc_msg_getrc()); 1259*45a5aec3SBaptiste Daroussin } 126061d06d6bSBaptiste Daroussin #endif 126161d06d6bSBaptiste Daroussin tag_files->pager_pid = pager_pid; 126261d06d6bSBaptiste Daroussin return pager_pid; 126361d06d6bSBaptiste Daroussin } 126461d06d6bSBaptiste Daroussin 126561d06d6bSBaptiste Daroussin /* The child process becomes the pager. */ 126661d06d6bSBaptiste Daroussin 1267*45a5aec3SBaptiste Daroussin if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) { 1268*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); 1269*45a5aec3SBaptiste Daroussin _exit(mandoc_msg_getrc()); 1270*45a5aec3SBaptiste Daroussin } 127161d06d6bSBaptiste Daroussin close(tag_files->ofd); 127261d06d6bSBaptiste Daroussin assert(tag_files->tfd == -1); 127361d06d6bSBaptiste Daroussin 127461d06d6bSBaptiste Daroussin /* Do not start the pager before controlling the terminal. */ 127561d06d6bSBaptiste Daroussin 127661d06d6bSBaptiste Daroussin while (tcgetpgrp(STDOUT_FILENO) != getpid()) 127761d06d6bSBaptiste Daroussin nanosleep(&timeout, NULL); 127861d06d6bSBaptiste Daroussin 127961d06d6bSBaptiste Daroussin execvp(argv[0], argv); 1280*45a5aec3SBaptiste Daroussin mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", argv[0], strerror(errno)); 1281*45a5aec3SBaptiste Daroussin _exit(mandoc_msg_getrc()); 128261d06d6bSBaptiste Daroussin } 1283