1*61d06d6bSBaptiste Daroussin /* $Id: main.c,v 1.306 2018/05/14 14:10:23 schwarze Exp $ */ 2*61d06d6bSBaptiste Daroussin /* 3*61d06d6bSBaptiste Daroussin * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4*61d06d6bSBaptiste Daroussin * Copyright (c) 2010-2012, 2014-2018 Ingo Schwarze <schwarze@openbsd.org> 5*61d06d6bSBaptiste Daroussin * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> 6*61d06d6bSBaptiste Daroussin * 7*61d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any 8*61d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above 9*61d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies. 10*61d06d6bSBaptiste Daroussin * 11*61d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 12*61d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13*61d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 14*61d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15*61d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16*61d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17*61d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18*61d06d6bSBaptiste Daroussin */ 19*61d06d6bSBaptiste Daroussin #include "config.h" 20*61d06d6bSBaptiste Daroussin 21*61d06d6bSBaptiste Daroussin #include <sys/types.h> 22*61d06d6bSBaptiste Daroussin #include <sys/ioctl.h> 23*61d06d6bSBaptiste Daroussin #include <sys/param.h> /* MACHINE */ 24*61d06d6bSBaptiste Daroussin #include <sys/wait.h> 25*61d06d6bSBaptiste Daroussin 26*61d06d6bSBaptiste Daroussin #include <assert.h> 27*61d06d6bSBaptiste Daroussin #include <ctype.h> 28*61d06d6bSBaptiste Daroussin #if HAVE_ERR 29*61d06d6bSBaptiste Daroussin #include <err.h> 30*61d06d6bSBaptiste Daroussin #endif 31*61d06d6bSBaptiste Daroussin #include <errno.h> 32*61d06d6bSBaptiste Daroussin #include <fcntl.h> 33*61d06d6bSBaptiste Daroussin #include <glob.h> 34*61d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT 35*61d06d6bSBaptiste Daroussin #include <sandbox.h> 36*61d06d6bSBaptiste Daroussin #endif 37*61d06d6bSBaptiste Daroussin #include <signal.h> 38*61d06d6bSBaptiste Daroussin #include <stdio.h> 39*61d06d6bSBaptiste Daroussin #include <stdint.h> 40*61d06d6bSBaptiste Daroussin #include <stdlib.h> 41*61d06d6bSBaptiste Daroussin #include <string.h> 42*61d06d6bSBaptiste Daroussin #include <termios.h> 43*61d06d6bSBaptiste Daroussin #include <time.h> 44*61d06d6bSBaptiste Daroussin #include <unistd.h> 45*61d06d6bSBaptiste Daroussin 46*61d06d6bSBaptiste Daroussin #include "mandoc_aux.h" 47*61d06d6bSBaptiste Daroussin #include "mandoc.h" 48*61d06d6bSBaptiste Daroussin #include "mandoc_xr.h" 49*61d06d6bSBaptiste Daroussin #include "roff.h" 50*61d06d6bSBaptiste Daroussin #include "mdoc.h" 51*61d06d6bSBaptiste Daroussin #include "man.h" 52*61d06d6bSBaptiste Daroussin #include "tag.h" 53*61d06d6bSBaptiste Daroussin #include "main.h" 54*61d06d6bSBaptiste Daroussin #include "manconf.h" 55*61d06d6bSBaptiste Daroussin #include "mansearch.h" 56*61d06d6bSBaptiste Daroussin 57*61d06d6bSBaptiste Daroussin enum outmode { 58*61d06d6bSBaptiste Daroussin OUTMODE_DEF = 0, 59*61d06d6bSBaptiste Daroussin OUTMODE_FLN, 60*61d06d6bSBaptiste Daroussin OUTMODE_LST, 61*61d06d6bSBaptiste Daroussin OUTMODE_ALL, 62*61d06d6bSBaptiste Daroussin OUTMODE_ONE 63*61d06d6bSBaptiste Daroussin }; 64*61d06d6bSBaptiste Daroussin 65*61d06d6bSBaptiste Daroussin enum outt { 66*61d06d6bSBaptiste Daroussin OUTT_ASCII = 0, /* -Tascii */ 67*61d06d6bSBaptiste Daroussin OUTT_LOCALE, /* -Tlocale */ 68*61d06d6bSBaptiste Daroussin OUTT_UTF8, /* -Tutf8 */ 69*61d06d6bSBaptiste Daroussin OUTT_TREE, /* -Ttree */ 70*61d06d6bSBaptiste Daroussin OUTT_MAN, /* -Tman */ 71*61d06d6bSBaptiste Daroussin OUTT_HTML, /* -Thtml */ 72*61d06d6bSBaptiste Daroussin OUTT_MARKDOWN, /* -Tmarkdown */ 73*61d06d6bSBaptiste Daroussin OUTT_LINT, /* -Tlint */ 74*61d06d6bSBaptiste Daroussin OUTT_PS, /* -Tps */ 75*61d06d6bSBaptiste Daroussin OUTT_PDF /* -Tpdf */ 76*61d06d6bSBaptiste Daroussin }; 77*61d06d6bSBaptiste Daroussin 78*61d06d6bSBaptiste Daroussin struct curparse { 79*61d06d6bSBaptiste Daroussin struct mparse *mp; 80*61d06d6bSBaptiste Daroussin struct manoutput *outopts; /* output options */ 81*61d06d6bSBaptiste Daroussin void *outdata; /* data for output */ 82*61d06d6bSBaptiste Daroussin char *os_s; /* operating system for display */ 83*61d06d6bSBaptiste Daroussin int wstop; /* stop after a file with a warning */ 84*61d06d6bSBaptiste Daroussin enum mandocerr mmin; /* ignore messages below this */ 85*61d06d6bSBaptiste Daroussin enum mandoc_os os_e; /* check base system conventions */ 86*61d06d6bSBaptiste Daroussin enum outt outtype; /* which output to use */ 87*61d06d6bSBaptiste Daroussin }; 88*61d06d6bSBaptiste Daroussin 89*61d06d6bSBaptiste Daroussin 90*61d06d6bSBaptiste Daroussin int mandocdb(int, char *[]); 91*61d06d6bSBaptiste Daroussin 92*61d06d6bSBaptiste Daroussin static void check_xr(const char *); 93*61d06d6bSBaptiste Daroussin static int fs_lookup(const struct manpaths *, 94*61d06d6bSBaptiste Daroussin size_t ipath, const char *, 95*61d06d6bSBaptiste Daroussin const char *, const char *, 96*61d06d6bSBaptiste Daroussin struct manpage **, size_t *); 97*61d06d6bSBaptiste Daroussin static int fs_search(const struct mansearch *, 98*61d06d6bSBaptiste Daroussin const struct manpaths *, int, char**, 99*61d06d6bSBaptiste Daroussin struct manpage **, size_t *); 100*61d06d6bSBaptiste Daroussin static int koptions(int *, char *); 101*61d06d6bSBaptiste Daroussin static void moptions(int *, char *); 102*61d06d6bSBaptiste Daroussin static void mmsg(enum mandocerr, enum mandoclevel, 103*61d06d6bSBaptiste Daroussin const char *, int, int, const char *); 104*61d06d6bSBaptiste Daroussin static void outdata_alloc(struct curparse *); 105*61d06d6bSBaptiste Daroussin static void parse(struct curparse *, int, const char *); 106*61d06d6bSBaptiste Daroussin static void passthrough(const char *, int, int); 107*61d06d6bSBaptiste Daroussin static pid_t spawn_pager(struct tag_files *); 108*61d06d6bSBaptiste Daroussin static int toptions(struct curparse *, char *); 109*61d06d6bSBaptiste Daroussin static void usage(enum argmode) __attribute__((__noreturn__)); 110*61d06d6bSBaptiste Daroussin static int woptions(struct curparse *, char *); 111*61d06d6bSBaptiste Daroussin 112*61d06d6bSBaptiste Daroussin static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; 113*61d06d6bSBaptiste Daroussin static char help_arg[] = "help"; 114*61d06d6bSBaptiste Daroussin static char *help_argv[] = {help_arg, NULL}; 115*61d06d6bSBaptiste Daroussin static enum mandoclevel rc; 116*61d06d6bSBaptiste Daroussin static FILE *mmsg_stream; 117*61d06d6bSBaptiste Daroussin 118*61d06d6bSBaptiste Daroussin 119*61d06d6bSBaptiste Daroussin int 120*61d06d6bSBaptiste Daroussin main(int argc, char *argv[]) 121*61d06d6bSBaptiste Daroussin { 122*61d06d6bSBaptiste Daroussin struct manconf conf; 123*61d06d6bSBaptiste Daroussin struct mansearch search; 124*61d06d6bSBaptiste Daroussin struct curparse curp; 125*61d06d6bSBaptiste Daroussin struct winsize ws; 126*61d06d6bSBaptiste Daroussin struct tag_files *tag_files; 127*61d06d6bSBaptiste Daroussin struct manpage *res, *resp; 128*61d06d6bSBaptiste Daroussin const char *progname, *sec, *thisarg; 129*61d06d6bSBaptiste Daroussin char *conf_file, *defpaths, *auxpaths; 130*61d06d6bSBaptiste Daroussin char *oarg; 131*61d06d6bSBaptiste Daroussin unsigned char *uc; 132*61d06d6bSBaptiste Daroussin size_t i, sz; 133*61d06d6bSBaptiste Daroussin int prio, best_prio; 134*61d06d6bSBaptiste Daroussin enum outmode outmode; 135*61d06d6bSBaptiste Daroussin int fd, startdir; 136*61d06d6bSBaptiste Daroussin int show_usage; 137*61d06d6bSBaptiste Daroussin int options; 138*61d06d6bSBaptiste Daroussin int use_pager; 139*61d06d6bSBaptiste Daroussin int status, signum; 140*61d06d6bSBaptiste Daroussin int c; 141*61d06d6bSBaptiste Daroussin pid_t pager_pid, tc_pgid, man_pgid, pid; 142*61d06d6bSBaptiste Daroussin 143*61d06d6bSBaptiste Daroussin #if HAVE_PROGNAME 144*61d06d6bSBaptiste Daroussin progname = getprogname(); 145*61d06d6bSBaptiste Daroussin #else 146*61d06d6bSBaptiste Daroussin if (argc < 1) 147*61d06d6bSBaptiste Daroussin progname = mandoc_strdup("mandoc"); 148*61d06d6bSBaptiste Daroussin else if ((progname = strrchr(argv[0], '/')) == NULL) 149*61d06d6bSBaptiste Daroussin progname = argv[0]; 150*61d06d6bSBaptiste Daroussin else 151*61d06d6bSBaptiste Daroussin ++progname; 152*61d06d6bSBaptiste Daroussin setprogname(progname); 153*61d06d6bSBaptiste Daroussin #endif 154*61d06d6bSBaptiste Daroussin 155*61d06d6bSBaptiste Daroussin if (strncmp(progname, "mandocdb", 8) == 0 || 156*61d06d6bSBaptiste Daroussin strcmp(progname, BINM_MAKEWHATIS) == 0) 157*61d06d6bSBaptiste Daroussin return mandocdb(argc, argv); 158*61d06d6bSBaptiste Daroussin 159*61d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 160*61d06d6bSBaptiste Daroussin if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) 161*61d06d6bSBaptiste Daroussin err((int)MANDOCLEVEL_SYSERR, "pledge"); 162*61d06d6bSBaptiste Daroussin #endif 163*61d06d6bSBaptiste Daroussin 164*61d06d6bSBaptiste Daroussin #if HAVE_SANDBOX_INIT 165*61d06d6bSBaptiste Daroussin if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) 166*61d06d6bSBaptiste Daroussin errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); 167*61d06d6bSBaptiste Daroussin #endif 168*61d06d6bSBaptiste Daroussin 169*61d06d6bSBaptiste Daroussin /* Search options. */ 170*61d06d6bSBaptiste Daroussin 171*61d06d6bSBaptiste Daroussin memset(&conf, 0, sizeof(conf)); 172*61d06d6bSBaptiste Daroussin conf_file = defpaths = NULL; 173*61d06d6bSBaptiste Daroussin auxpaths = NULL; 174*61d06d6bSBaptiste Daroussin 175*61d06d6bSBaptiste Daroussin memset(&search, 0, sizeof(struct mansearch)); 176*61d06d6bSBaptiste Daroussin search.outkey = "Nd"; 177*61d06d6bSBaptiste Daroussin oarg = NULL; 178*61d06d6bSBaptiste Daroussin 179*61d06d6bSBaptiste Daroussin if (strcmp(progname, BINM_MAN) == 0) 180*61d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 181*61d06d6bSBaptiste Daroussin else if (strcmp(progname, BINM_APROPOS) == 0) 182*61d06d6bSBaptiste Daroussin search.argmode = ARG_EXPR; 183*61d06d6bSBaptiste Daroussin else if (strcmp(progname, BINM_WHATIS) == 0) 184*61d06d6bSBaptiste Daroussin search.argmode = ARG_WORD; 185*61d06d6bSBaptiste Daroussin else if (strncmp(progname, "help", 4) == 0) 186*61d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 187*61d06d6bSBaptiste Daroussin else 188*61d06d6bSBaptiste Daroussin search.argmode = ARG_FILE; 189*61d06d6bSBaptiste Daroussin 190*61d06d6bSBaptiste Daroussin /* Parser and formatter options. */ 191*61d06d6bSBaptiste Daroussin 192*61d06d6bSBaptiste Daroussin memset(&curp, 0, sizeof(struct curparse)); 193*61d06d6bSBaptiste Daroussin curp.outtype = OUTT_LOCALE; 194*61d06d6bSBaptiste Daroussin curp.mmin = MANDOCERR_MAX; 195*61d06d6bSBaptiste Daroussin curp.outopts = &conf.output; 196*61d06d6bSBaptiste Daroussin options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; 197*61d06d6bSBaptiste Daroussin mmsg_stream = stderr; 198*61d06d6bSBaptiste Daroussin 199*61d06d6bSBaptiste Daroussin use_pager = 1; 200*61d06d6bSBaptiste Daroussin tag_files = NULL; 201*61d06d6bSBaptiste Daroussin show_usage = 0; 202*61d06d6bSBaptiste Daroussin outmode = OUTMODE_DEF; 203*61d06d6bSBaptiste Daroussin 204*61d06d6bSBaptiste Daroussin while ((c = getopt(argc, argv, 205*61d06d6bSBaptiste Daroussin "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) { 206*61d06d6bSBaptiste Daroussin if (c == 'i' && search.argmode == ARG_EXPR) { 207*61d06d6bSBaptiste Daroussin optind--; 208*61d06d6bSBaptiste Daroussin break; 209*61d06d6bSBaptiste Daroussin } 210*61d06d6bSBaptiste Daroussin switch (c) { 211*61d06d6bSBaptiste Daroussin case 'a': 212*61d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 213*61d06d6bSBaptiste Daroussin break; 214*61d06d6bSBaptiste Daroussin case 'C': 215*61d06d6bSBaptiste Daroussin conf_file = optarg; 216*61d06d6bSBaptiste Daroussin break; 217*61d06d6bSBaptiste Daroussin case 'c': 218*61d06d6bSBaptiste Daroussin use_pager = 0; 219*61d06d6bSBaptiste Daroussin break; 220*61d06d6bSBaptiste Daroussin case 'f': 221*61d06d6bSBaptiste Daroussin search.argmode = ARG_WORD; 222*61d06d6bSBaptiste Daroussin break; 223*61d06d6bSBaptiste Daroussin case 'h': 224*61d06d6bSBaptiste Daroussin conf.output.synopsisonly = 1; 225*61d06d6bSBaptiste Daroussin use_pager = 0; 226*61d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 227*61d06d6bSBaptiste Daroussin break; 228*61d06d6bSBaptiste Daroussin case 'I': 229*61d06d6bSBaptiste Daroussin if (strncmp(optarg, "os=", 3)) { 230*61d06d6bSBaptiste Daroussin warnx("-I %s: Bad argument", optarg); 231*61d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_BADARG; 232*61d06d6bSBaptiste Daroussin } 233*61d06d6bSBaptiste Daroussin if (curp.os_s != NULL) { 234*61d06d6bSBaptiste Daroussin warnx("-I %s: Duplicate argument", optarg); 235*61d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_BADARG; 236*61d06d6bSBaptiste Daroussin } 237*61d06d6bSBaptiste Daroussin curp.os_s = mandoc_strdup(optarg + 3); 238*61d06d6bSBaptiste Daroussin break; 239*61d06d6bSBaptiste Daroussin case 'K': 240*61d06d6bSBaptiste Daroussin if ( ! koptions(&options, optarg)) 241*61d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_BADARG; 242*61d06d6bSBaptiste Daroussin break; 243*61d06d6bSBaptiste Daroussin case 'k': 244*61d06d6bSBaptiste Daroussin search.argmode = ARG_EXPR; 245*61d06d6bSBaptiste Daroussin break; 246*61d06d6bSBaptiste Daroussin case 'l': 247*61d06d6bSBaptiste Daroussin search.argmode = ARG_FILE; 248*61d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 249*61d06d6bSBaptiste Daroussin break; 250*61d06d6bSBaptiste Daroussin case 'M': 251*61d06d6bSBaptiste Daroussin defpaths = optarg; 252*61d06d6bSBaptiste Daroussin break; 253*61d06d6bSBaptiste Daroussin case 'm': 254*61d06d6bSBaptiste Daroussin auxpaths = optarg; 255*61d06d6bSBaptiste Daroussin break; 256*61d06d6bSBaptiste Daroussin case 'O': 257*61d06d6bSBaptiste Daroussin oarg = optarg; 258*61d06d6bSBaptiste Daroussin break; 259*61d06d6bSBaptiste Daroussin case 'S': 260*61d06d6bSBaptiste Daroussin search.arch = optarg; 261*61d06d6bSBaptiste Daroussin break; 262*61d06d6bSBaptiste Daroussin case 's': 263*61d06d6bSBaptiste Daroussin search.sec = optarg; 264*61d06d6bSBaptiste Daroussin break; 265*61d06d6bSBaptiste Daroussin case 'T': 266*61d06d6bSBaptiste Daroussin if ( ! toptions(&curp, optarg)) 267*61d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_BADARG; 268*61d06d6bSBaptiste Daroussin break; 269*61d06d6bSBaptiste Daroussin case 'W': 270*61d06d6bSBaptiste Daroussin if ( ! woptions(&curp, optarg)) 271*61d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_BADARG; 272*61d06d6bSBaptiste Daroussin break; 273*61d06d6bSBaptiste Daroussin case 'w': 274*61d06d6bSBaptiste Daroussin outmode = OUTMODE_FLN; 275*61d06d6bSBaptiste Daroussin break; 276*61d06d6bSBaptiste Daroussin default: 277*61d06d6bSBaptiste Daroussin show_usage = 1; 278*61d06d6bSBaptiste Daroussin break; 279*61d06d6bSBaptiste Daroussin } 280*61d06d6bSBaptiste Daroussin } 281*61d06d6bSBaptiste Daroussin 282*61d06d6bSBaptiste Daroussin if (show_usage) 283*61d06d6bSBaptiste Daroussin usage(search.argmode); 284*61d06d6bSBaptiste Daroussin 285*61d06d6bSBaptiste Daroussin /* Postprocess options. */ 286*61d06d6bSBaptiste Daroussin 287*61d06d6bSBaptiste Daroussin if (outmode == OUTMODE_DEF) { 288*61d06d6bSBaptiste Daroussin switch (search.argmode) { 289*61d06d6bSBaptiste Daroussin case ARG_FILE: 290*61d06d6bSBaptiste Daroussin outmode = OUTMODE_ALL; 291*61d06d6bSBaptiste Daroussin use_pager = 0; 292*61d06d6bSBaptiste Daroussin break; 293*61d06d6bSBaptiste Daroussin case ARG_NAME: 294*61d06d6bSBaptiste Daroussin outmode = OUTMODE_ONE; 295*61d06d6bSBaptiste Daroussin break; 296*61d06d6bSBaptiste Daroussin default: 297*61d06d6bSBaptiste Daroussin outmode = OUTMODE_LST; 298*61d06d6bSBaptiste Daroussin break; 299*61d06d6bSBaptiste Daroussin } 300*61d06d6bSBaptiste Daroussin } 301*61d06d6bSBaptiste Daroussin 302*61d06d6bSBaptiste Daroussin if (oarg != NULL) { 303*61d06d6bSBaptiste Daroussin if (outmode == OUTMODE_LST) 304*61d06d6bSBaptiste Daroussin search.outkey = oarg; 305*61d06d6bSBaptiste Daroussin else { 306*61d06d6bSBaptiste Daroussin while (oarg != NULL) { 307*61d06d6bSBaptiste Daroussin thisarg = oarg; 308*61d06d6bSBaptiste Daroussin if (manconf_output(&conf.output, 309*61d06d6bSBaptiste Daroussin strsep(&oarg, ","), 0) == 0) 310*61d06d6bSBaptiste Daroussin continue; 311*61d06d6bSBaptiste Daroussin warnx("-O %s: Bad argument", thisarg); 312*61d06d6bSBaptiste Daroussin return (int)MANDOCLEVEL_BADARG; 313*61d06d6bSBaptiste Daroussin } 314*61d06d6bSBaptiste Daroussin } 315*61d06d6bSBaptiste Daroussin } 316*61d06d6bSBaptiste Daroussin 317*61d06d6bSBaptiste Daroussin if (outmode == OUTMODE_FLN || 318*61d06d6bSBaptiste Daroussin outmode == OUTMODE_LST || 319*61d06d6bSBaptiste Daroussin !isatty(STDOUT_FILENO)) 320*61d06d6bSBaptiste Daroussin use_pager = 0; 321*61d06d6bSBaptiste Daroussin 322*61d06d6bSBaptiste Daroussin if (use_pager && 323*61d06d6bSBaptiste Daroussin (conf.output.width == 0 || conf.output.indent == 0) && 324*61d06d6bSBaptiste Daroussin ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && 325*61d06d6bSBaptiste Daroussin ws.ws_col > 1) { 326*61d06d6bSBaptiste Daroussin if (conf.output.width == 0 && ws.ws_col < 79) 327*61d06d6bSBaptiste Daroussin conf.output.width = ws.ws_col - 1; 328*61d06d6bSBaptiste Daroussin if (conf.output.indent == 0 && ws.ws_col < 66) 329*61d06d6bSBaptiste Daroussin conf.output.indent = 3; 330*61d06d6bSBaptiste Daroussin } 331*61d06d6bSBaptiste Daroussin 332*61d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 333*61d06d6bSBaptiste Daroussin if (!use_pager) 334*61d06d6bSBaptiste Daroussin if (pledge("stdio rpath", NULL) == -1) 335*61d06d6bSBaptiste Daroussin err((int)MANDOCLEVEL_SYSERR, "pledge"); 336*61d06d6bSBaptiste Daroussin #endif 337*61d06d6bSBaptiste Daroussin 338*61d06d6bSBaptiste Daroussin /* Parse arguments. */ 339*61d06d6bSBaptiste Daroussin 340*61d06d6bSBaptiste Daroussin if (argc > 0) { 341*61d06d6bSBaptiste Daroussin argc -= optind; 342*61d06d6bSBaptiste Daroussin argv += optind; 343*61d06d6bSBaptiste Daroussin } 344*61d06d6bSBaptiste Daroussin resp = NULL; 345*61d06d6bSBaptiste Daroussin 346*61d06d6bSBaptiste Daroussin /* 347*61d06d6bSBaptiste Daroussin * Quirks for help(1) 348*61d06d6bSBaptiste Daroussin * and for a man(1) section argument without -s. 349*61d06d6bSBaptiste Daroussin */ 350*61d06d6bSBaptiste Daroussin 351*61d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME) { 352*61d06d6bSBaptiste Daroussin if (*progname == 'h') { 353*61d06d6bSBaptiste Daroussin if (argc == 0) { 354*61d06d6bSBaptiste Daroussin argv = help_argv; 355*61d06d6bSBaptiste Daroussin argc = 1; 356*61d06d6bSBaptiste Daroussin } 357*61d06d6bSBaptiste Daroussin } else if (argc > 1 && 358*61d06d6bSBaptiste Daroussin ((uc = (unsigned char *)argv[0]) != NULL) && 359*61d06d6bSBaptiste Daroussin ((isdigit(uc[0]) && (uc[1] == '\0' || 360*61d06d6bSBaptiste Daroussin (isalpha(uc[1]) && uc[2] == '\0'))) || 361*61d06d6bSBaptiste Daroussin (uc[0] == 'n' && uc[1] == '\0'))) { 362*61d06d6bSBaptiste Daroussin search.sec = (char *)uc; 363*61d06d6bSBaptiste Daroussin argv++; 364*61d06d6bSBaptiste Daroussin argc--; 365*61d06d6bSBaptiste Daroussin } 366*61d06d6bSBaptiste Daroussin if (search.arch == NULL) 367*61d06d6bSBaptiste Daroussin search.arch = getenv("MACHINE"); 368*61d06d6bSBaptiste Daroussin #ifdef MACHINE 369*61d06d6bSBaptiste Daroussin if (search.arch == NULL) 370*61d06d6bSBaptiste Daroussin search.arch = MACHINE; 371*61d06d6bSBaptiste Daroussin #endif 372*61d06d6bSBaptiste Daroussin } 373*61d06d6bSBaptiste Daroussin 374*61d06d6bSBaptiste Daroussin rc = MANDOCLEVEL_OK; 375*61d06d6bSBaptiste Daroussin 376*61d06d6bSBaptiste Daroussin /* man(1), whatis(1), apropos(1) */ 377*61d06d6bSBaptiste Daroussin 378*61d06d6bSBaptiste Daroussin if (search.argmode != ARG_FILE) { 379*61d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME && 380*61d06d6bSBaptiste Daroussin outmode == OUTMODE_ONE) 381*61d06d6bSBaptiste Daroussin search.firstmatch = 1; 382*61d06d6bSBaptiste Daroussin 383*61d06d6bSBaptiste Daroussin /* Access the mandoc database. */ 384*61d06d6bSBaptiste Daroussin 385*61d06d6bSBaptiste Daroussin manconf_parse(&conf, conf_file, defpaths, auxpaths); 386*61d06d6bSBaptiste Daroussin if ( ! mansearch(&search, &conf.manpath, 387*61d06d6bSBaptiste Daroussin argc, argv, &res, &sz)) 388*61d06d6bSBaptiste Daroussin usage(search.argmode); 389*61d06d6bSBaptiste Daroussin 390*61d06d6bSBaptiste Daroussin if (sz == 0 && search.argmode == ARG_NAME) 391*61d06d6bSBaptiste Daroussin fs_search(&search, &conf.manpath, 392*61d06d6bSBaptiste Daroussin argc, argv, &res, &sz); 393*61d06d6bSBaptiste Daroussin 394*61d06d6bSBaptiste Daroussin if (search.argmode == ARG_NAME) { 395*61d06d6bSBaptiste Daroussin for (c = 0; c < argc; c++) { 396*61d06d6bSBaptiste Daroussin if (strchr(argv[c], '/') == NULL) 397*61d06d6bSBaptiste Daroussin continue; 398*61d06d6bSBaptiste Daroussin if (access(argv[c], R_OK) == -1) { 399*61d06d6bSBaptiste Daroussin warn("%s", argv[c]); 400*61d06d6bSBaptiste Daroussin continue; 401*61d06d6bSBaptiste Daroussin } 402*61d06d6bSBaptiste Daroussin res = mandoc_reallocarray(res, 403*61d06d6bSBaptiste Daroussin sz + 1, sizeof(*res)); 404*61d06d6bSBaptiste Daroussin res[sz].file = mandoc_strdup(argv[c]); 405*61d06d6bSBaptiste Daroussin res[sz].names = NULL; 406*61d06d6bSBaptiste Daroussin res[sz].output = NULL; 407*61d06d6bSBaptiste Daroussin res[sz].ipath = SIZE_MAX; 408*61d06d6bSBaptiste Daroussin res[sz].bits = 0; 409*61d06d6bSBaptiste Daroussin res[sz].sec = 10; 410*61d06d6bSBaptiste Daroussin res[sz].form = FORM_SRC; 411*61d06d6bSBaptiste Daroussin sz++; 412*61d06d6bSBaptiste Daroussin } 413*61d06d6bSBaptiste Daroussin } 414*61d06d6bSBaptiste Daroussin 415*61d06d6bSBaptiste Daroussin if (sz == 0) { 416*61d06d6bSBaptiste Daroussin if (search.argmode != ARG_NAME) 417*61d06d6bSBaptiste Daroussin warnx("nothing appropriate"); 418*61d06d6bSBaptiste Daroussin rc = MANDOCLEVEL_BADARG; 419*61d06d6bSBaptiste Daroussin goto out; 420*61d06d6bSBaptiste Daroussin } 421*61d06d6bSBaptiste Daroussin 422*61d06d6bSBaptiste Daroussin /* 423*61d06d6bSBaptiste Daroussin * For standard man(1) and -a output mode, 424*61d06d6bSBaptiste Daroussin * prepare for copying filename pointers 425*61d06d6bSBaptiste Daroussin * into the program parameter array. 426*61d06d6bSBaptiste Daroussin */ 427*61d06d6bSBaptiste Daroussin 428*61d06d6bSBaptiste Daroussin if (outmode == OUTMODE_ONE) { 429*61d06d6bSBaptiste Daroussin argc = 1; 430*61d06d6bSBaptiste Daroussin best_prio = 20; 431*61d06d6bSBaptiste Daroussin } else if (outmode == OUTMODE_ALL) 432*61d06d6bSBaptiste Daroussin argc = (int)sz; 433*61d06d6bSBaptiste Daroussin 434*61d06d6bSBaptiste Daroussin /* Iterate all matching manuals. */ 435*61d06d6bSBaptiste Daroussin 436*61d06d6bSBaptiste Daroussin resp = res; 437*61d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) { 438*61d06d6bSBaptiste Daroussin if (outmode == OUTMODE_FLN) 439*61d06d6bSBaptiste Daroussin puts(res[i].file); 440*61d06d6bSBaptiste Daroussin else if (outmode == OUTMODE_LST) 441*61d06d6bSBaptiste Daroussin printf("%s - %s\n", res[i].names, 442*61d06d6bSBaptiste Daroussin res[i].output == NULL ? "" : 443*61d06d6bSBaptiste Daroussin res[i].output); 444*61d06d6bSBaptiste Daroussin else if (outmode == OUTMODE_ONE) { 445*61d06d6bSBaptiste Daroussin /* Search for the best section. */ 446*61d06d6bSBaptiste Daroussin sec = res[i].file; 447*61d06d6bSBaptiste Daroussin sec += strcspn(sec, "123456789"); 448*61d06d6bSBaptiste Daroussin if (sec[0] == '\0') 449*61d06d6bSBaptiste Daroussin continue; 450*61d06d6bSBaptiste Daroussin prio = sec_prios[sec[0] - '1']; 451*61d06d6bSBaptiste Daroussin if (sec[1] != '/') 452*61d06d6bSBaptiste Daroussin prio += 10; 453*61d06d6bSBaptiste Daroussin if (prio >= best_prio) 454*61d06d6bSBaptiste Daroussin continue; 455*61d06d6bSBaptiste Daroussin best_prio = prio; 456*61d06d6bSBaptiste Daroussin resp = res + i; 457*61d06d6bSBaptiste Daroussin } 458*61d06d6bSBaptiste Daroussin } 459*61d06d6bSBaptiste Daroussin 460*61d06d6bSBaptiste Daroussin /* 461*61d06d6bSBaptiste Daroussin * For man(1), -a and -i output mode, fall through 462*61d06d6bSBaptiste Daroussin * to the main mandoc(1) code iterating files 463*61d06d6bSBaptiste Daroussin * and running the parsers on each of them. 464*61d06d6bSBaptiste Daroussin */ 465*61d06d6bSBaptiste Daroussin 466*61d06d6bSBaptiste Daroussin if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) 467*61d06d6bSBaptiste Daroussin goto out; 468*61d06d6bSBaptiste Daroussin } 469*61d06d6bSBaptiste Daroussin 470*61d06d6bSBaptiste Daroussin /* mandoc(1) */ 471*61d06d6bSBaptiste Daroussin 472*61d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 473*61d06d6bSBaptiste Daroussin if (use_pager) { 474*61d06d6bSBaptiste Daroussin if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) 475*61d06d6bSBaptiste Daroussin err((int)MANDOCLEVEL_SYSERR, "pledge"); 476*61d06d6bSBaptiste Daroussin } else { 477*61d06d6bSBaptiste Daroussin if (pledge("stdio rpath", NULL) == -1) 478*61d06d6bSBaptiste Daroussin err((int)MANDOCLEVEL_SYSERR, "pledge"); 479*61d06d6bSBaptiste Daroussin } 480*61d06d6bSBaptiste Daroussin #endif 481*61d06d6bSBaptiste Daroussin 482*61d06d6bSBaptiste Daroussin if (search.argmode == ARG_FILE) 483*61d06d6bSBaptiste Daroussin moptions(&options, auxpaths); 484*61d06d6bSBaptiste Daroussin 485*61d06d6bSBaptiste Daroussin mchars_alloc(); 486*61d06d6bSBaptiste Daroussin curp.mp = mparse_alloc(options, curp.mmin, mmsg, 487*61d06d6bSBaptiste Daroussin curp.os_e, curp.os_s); 488*61d06d6bSBaptiste Daroussin 489*61d06d6bSBaptiste Daroussin /* 490*61d06d6bSBaptiste Daroussin * Conditionally start up the lookaside buffer before parsing. 491*61d06d6bSBaptiste Daroussin */ 492*61d06d6bSBaptiste Daroussin if (OUTT_MAN == curp.outtype) 493*61d06d6bSBaptiste Daroussin mparse_keep(curp.mp); 494*61d06d6bSBaptiste Daroussin 495*61d06d6bSBaptiste Daroussin if (argc < 1) { 496*61d06d6bSBaptiste Daroussin if (use_pager) 497*61d06d6bSBaptiste Daroussin tag_files = tag_init(); 498*61d06d6bSBaptiste Daroussin parse(&curp, STDIN_FILENO, "<stdin>"); 499*61d06d6bSBaptiste Daroussin } 500*61d06d6bSBaptiste Daroussin 501*61d06d6bSBaptiste Daroussin /* 502*61d06d6bSBaptiste Daroussin * Remember the original working directory, if possible. 503*61d06d6bSBaptiste Daroussin * This will be needed if some names on the command line 504*61d06d6bSBaptiste Daroussin * are page names and some are relative file names. 505*61d06d6bSBaptiste Daroussin * Do not error out if the current directory is not 506*61d06d6bSBaptiste Daroussin * readable: Maybe it won't be needed after all. 507*61d06d6bSBaptiste Daroussin */ 508*61d06d6bSBaptiste Daroussin startdir = open(".", O_RDONLY | O_DIRECTORY); 509*61d06d6bSBaptiste Daroussin 510*61d06d6bSBaptiste Daroussin while (argc > 0) { 511*61d06d6bSBaptiste Daroussin 512*61d06d6bSBaptiste Daroussin /* 513*61d06d6bSBaptiste Daroussin * Changing directories is not needed in ARG_FILE mode. 514*61d06d6bSBaptiste Daroussin * Do it on a best-effort basis. Even in case of 515*61d06d6bSBaptiste Daroussin * failure, some functionality may still work. 516*61d06d6bSBaptiste Daroussin */ 517*61d06d6bSBaptiste Daroussin if (resp != NULL) { 518*61d06d6bSBaptiste Daroussin if (resp->ipath != SIZE_MAX) 519*61d06d6bSBaptiste Daroussin (void)chdir(conf.manpath.paths[resp->ipath]); 520*61d06d6bSBaptiste Daroussin else if (startdir != -1) 521*61d06d6bSBaptiste Daroussin (void)fchdir(startdir); 522*61d06d6bSBaptiste Daroussin } 523*61d06d6bSBaptiste Daroussin 524*61d06d6bSBaptiste Daroussin fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv); 525*61d06d6bSBaptiste Daroussin if (fd != -1) { 526*61d06d6bSBaptiste Daroussin if (use_pager) { 527*61d06d6bSBaptiste Daroussin tag_files = tag_init(); 528*61d06d6bSBaptiste Daroussin use_pager = 0; 529*61d06d6bSBaptiste Daroussin } 530*61d06d6bSBaptiste Daroussin 531*61d06d6bSBaptiste Daroussin if (resp == NULL) 532*61d06d6bSBaptiste Daroussin parse(&curp, fd, *argv); 533*61d06d6bSBaptiste Daroussin else if (resp->form == FORM_SRC) 534*61d06d6bSBaptiste Daroussin parse(&curp, fd, resp->file); 535*61d06d6bSBaptiste Daroussin else 536*61d06d6bSBaptiste Daroussin passthrough(resp->file, fd, 537*61d06d6bSBaptiste Daroussin conf.output.synopsisonly); 538*61d06d6bSBaptiste Daroussin 539*61d06d6bSBaptiste Daroussin if (ferror(stdout)) { 540*61d06d6bSBaptiste Daroussin if (tag_files != NULL) { 541*61d06d6bSBaptiste Daroussin warn("%s", tag_files->ofn); 542*61d06d6bSBaptiste Daroussin tag_unlink(); 543*61d06d6bSBaptiste Daroussin tag_files = NULL; 544*61d06d6bSBaptiste Daroussin } else 545*61d06d6bSBaptiste Daroussin warn("stdout"); 546*61d06d6bSBaptiste Daroussin rc = MANDOCLEVEL_SYSERR; 547*61d06d6bSBaptiste Daroussin break; 548*61d06d6bSBaptiste Daroussin } 549*61d06d6bSBaptiste Daroussin 550*61d06d6bSBaptiste Daroussin if (argc > 1 && curp.outtype <= OUTT_UTF8) { 551*61d06d6bSBaptiste Daroussin if (curp.outdata == NULL) 552*61d06d6bSBaptiste Daroussin outdata_alloc(&curp); 553*61d06d6bSBaptiste Daroussin terminal_sepline(curp.outdata); 554*61d06d6bSBaptiste Daroussin } 555*61d06d6bSBaptiste Daroussin } else if (rc < MANDOCLEVEL_ERROR) 556*61d06d6bSBaptiste Daroussin rc = MANDOCLEVEL_ERROR; 557*61d06d6bSBaptiste Daroussin 558*61d06d6bSBaptiste Daroussin if (MANDOCLEVEL_OK != rc && curp.wstop) 559*61d06d6bSBaptiste Daroussin break; 560*61d06d6bSBaptiste Daroussin 561*61d06d6bSBaptiste Daroussin if (resp != NULL) 562*61d06d6bSBaptiste Daroussin resp++; 563*61d06d6bSBaptiste Daroussin else 564*61d06d6bSBaptiste Daroussin argv++; 565*61d06d6bSBaptiste Daroussin if (--argc) 566*61d06d6bSBaptiste Daroussin mparse_reset(curp.mp); 567*61d06d6bSBaptiste Daroussin } 568*61d06d6bSBaptiste Daroussin if (startdir != -1) { 569*61d06d6bSBaptiste Daroussin (void)fchdir(startdir); 570*61d06d6bSBaptiste Daroussin close(startdir); 571*61d06d6bSBaptiste Daroussin } 572*61d06d6bSBaptiste Daroussin 573*61d06d6bSBaptiste Daroussin if (curp.outdata != NULL) { 574*61d06d6bSBaptiste Daroussin switch (curp.outtype) { 575*61d06d6bSBaptiste Daroussin case OUTT_HTML: 576*61d06d6bSBaptiste Daroussin html_free(curp.outdata); 577*61d06d6bSBaptiste Daroussin break; 578*61d06d6bSBaptiste Daroussin case OUTT_UTF8: 579*61d06d6bSBaptiste Daroussin case OUTT_LOCALE: 580*61d06d6bSBaptiste Daroussin case OUTT_ASCII: 581*61d06d6bSBaptiste Daroussin ascii_free(curp.outdata); 582*61d06d6bSBaptiste Daroussin break; 583*61d06d6bSBaptiste Daroussin case OUTT_PDF: 584*61d06d6bSBaptiste Daroussin case OUTT_PS: 585*61d06d6bSBaptiste Daroussin pspdf_free(curp.outdata); 586*61d06d6bSBaptiste Daroussin break; 587*61d06d6bSBaptiste Daroussin default: 588*61d06d6bSBaptiste Daroussin break; 589*61d06d6bSBaptiste Daroussin } 590*61d06d6bSBaptiste Daroussin } 591*61d06d6bSBaptiste Daroussin mandoc_xr_free(); 592*61d06d6bSBaptiste Daroussin mparse_free(curp.mp); 593*61d06d6bSBaptiste Daroussin mchars_free(); 594*61d06d6bSBaptiste Daroussin 595*61d06d6bSBaptiste Daroussin out: 596*61d06d6bSBaptiste Daroussin if (search.argmode != ARG_FILE) { 597*61d06d6bSBaptiste Daroussin manconf_free(&conf); 598*61d06d6bSBaptiste Daroussin mansearch_free(res, sz); 599*61d06d6bSBaptiste Daroussin } 600*61d06d6bSBaptiste Daroussin 601*61d06d6bSBaptiste Daroussin free(curp.os_s); 602*61d06d6bSBaptiste Daroussin 603*61d06d6bSBaptiste Daroussin /* 604*61d06d6bSBaptiste Daroussin * When using a pager, finish writing both temporary files, 605*61d06d6bSBaptiste Daroussin * fork it, wait for the user to close it, and clean up. 606*61d06d6bSBaptiste Daroussin */ 607*61d06d6bSBaptiste Daroussin 608*61d06d6bSBaptiste Daroussin if (tag_files != NULL) { 609*61d06d6bSBaptiste Daroussin fclose(stdout); 610*61d06d6bSBaptiste Daroussin tag_write(); 611*61d06d6bSBaptiste Daroussin man_pgid = getpgid(0); 612*61d06d6bSBaptiste Daroussin tag_files->tcpgid = man_pgid == getpid() ? 613*61d06d6bSBaptiste Daroussin getpgid(getppid()) : man_pgid; 614*61d06d6bSBaptiste Daroussin pager_pid = 0; 615*61d06d6bSBaptiste Daroussin signum = SIGSTOP; 616*61d06d6bSBaptiste Daroussin for (;;) { 617*61d06d6bSBaptiste Daroussin 618*61d06d6bSBaptiste Daroussin /* Stop here until moved to the foreground. */ 619*61d06d6bSBaptiste Daroussin 620*61d06d6bSBaptiste Daroussin tc_pgid = tcgetpgrp(tag_files->ofd); 621*61d06d6bSBaptiste Daroussin if (tc_pgid != man_pgid) { 622*61d06d6bSBaptiste Daroussin if (tc_pgid == pager_pid) { 623*61d06d6bSBaptiste Daroussin (void)tcsetpgrp(tag_files->ofd, 624*61d06d6bSBaptiste Daroussin man_pgid); 625*61d06d6bSBaptiste Daroussin if (signum == SIGTTIN) 626*61d06d6bSBaptiste Daroussin continue; 627*61d06d6bSBaptiste Daroussin } else 628*61d06d6bSBaptiste Daroussin tag_files->tcpgid = tc_pgid; 629*61d06d6bSBaptiste Daroussin kill(0, signum); 630*61d06d6bSBaptiste Daroussin continue; 631*61d06d6bSBaptiste Daroussin } 632*61d06d6bSBaptiste Daroussin 633*61d06d6bSBaptiste Daroussin /* Once in the foreground, activate the pager. */ 634*61d06d6bSBaptiste Daroussin 635*61d06d6bSBaptiste Daroussin if (pager_pid) { 636*61d06d6bSBaptiste Daroussin (void)tcsetpgrp(tag_files->ofd, pager_pid); 637*61d06d6bSBaptiste Daroussin kill(pager_pid, SIGCONT); 638*61d06d6bSBaptiste Daroussin } else 639*61d06d6bSBaptiste Daroussin pager_pid = spawn_pager(tag_files); 640*61d06d6bSBaptiste Daroussin 641*61d06d6bSBaptiste Daroussin /* Wait for the pager to stop or exit. */ 642*61d06d6bSBaptiste Daroussin 643*61d06d6bSBaptiste Daroussin while ((pid = waitpid(pager_pid, &status, 644*61d06d6bSBaptiste Daroussin WUNTRACED)) == -1 && errno == EINTR) 645*61d06d6bSBaptiste Daroussin continue; 646*61d06d6bSBaptiste Daroussin 647*61d06d6bSBaptiste Daroussin if (pid == -1) { 648*61d06d6bSBaptiste Daroussin warn("wait"); 649*61d06d6bSBaptiste Daroussin rc = MANDOCLEVEL_SYSERR; 650*61d06d6bSBaptiste Daroussin break; 651*61d06d6bSBaptiste Daroussin } 652*61d06d6bSBaptiste Daroussin if (!WIFSTOPPED(status)) 653*61d06d6bSBaptiste Daroussin break; 654*61d06d6bSBaptiste Daroussin 655*61d06d6bSBaptiste Daroussin signum = WSTOPSIG(status); 656*61d06d6bSBaptiste Daroussin } 657*61d06d6bSBaptiste Daroussin tag_unlink(); 658*61d06d6bSBaptiste Daroussin } 659*61d06d6bSBaptiste Daroussin 660*61d06d6bSBaptiste Daroussin return (int)rc; 661*61d06d6bSBaptiste Daroussin } 662*61d06d6bSBaptiste Daroussin 663*61d06d6bSBaptiste Daroussin static void 664*61d06d6bSBaptiste Daroussin usage(enum argmode argmode) 665*61d06d6bSBaptiste Daroussin { 666*61d06d6bSBaptiste Daroussin 667*61d06d6bSBaptiste Daroussin switch (argmode) { 668*61d06d6bSBaptiste Daroussin case ARG_FILE: 669*61d06d6bSBaptiste Daroussin fputs("usage: mandoc [-ac] [-I os=name] " 670*61d06d6bSBaptiste Daroussin "[-K encoding] [-mdoc | -man] [-O options]\n" 671*61d06d6bSBaptiste Daroussin "\t [-T output] [-W level] [file ...]\n", stderr); 672*61d06d6bSBaptiste Daroussin break; 673*61d06d6bSBaptiste Daroussin case ARG_NAME: 674*61d06d6bSBaptiste Daroussin fputs("usage: man [-acfhklw] [-C file] [-M path] " 675*61d06d6bSBaptiste Daroussin "[-m path] [-S subsection]\n" 676*61d06d6bSBaptiste Daroussin "\t [[-s] section] name ...\n", stderr); 677*61d06d6bSBaptiste Daroussin break; 678*61d06d6bSBaptiste Daroussin case ARG_WORD: 679*61d06d6bSBaptiste Daroussin fputs("usage: whatis [-afk] [-C file] " 680*61d06d6bSBaptiste Daroussin "[-M path] [-m path] [-O outkey] [-S arch]\n" 681*61d06d6bSBaptiste Daroussin "\t [-s section] name ...\n", stderr); 682*61d06d6bSBaptiste Daroussin break; 683*61d06d6bSBaptiste Daroussin case ARG_EXPR: 684*61d06d6bSBaptiste Daroussin fputs("usage: apropos [-afk] [-C file] " 685*61d06d6bSBaptiste Daroussin "[-M path] [-m path] [-O outkey] [-S arch]\n" 686*61d06d6bSBaptiste Daroussin "\t [-s section] expression ...\n", stderr); 687*61d06d6bSBaptiste Daroussin break; 688*61d06d6bSBaptiste Daroussin } 689*61d06d6bSBaptiste Daroussin exit((int)MANDOCLEVEL_BADARG); 690*61d06d6bSBaptiste Daroussin } 691*61d06d6bSBaptiste Daroussin 692*61d06d6bSBaptiste Daroussin static int 693*61d06d6bSBaptiste Daroussin fs_lookup(const struct manpaths *paths, size_t ipath, 694*61d06d6bSBaptiste Daroussin const char *sec, const char *arch, const char *name, 695*61d06d6bSBaptiste Daroussin struct manpage **res, size_t *ressz) 696*61d06d6bSBaptiste Daroussin { 697*61d06d6bSBaptiste Daroussin glob_t globinfo; 698*61d06d6bSBaptiste Daroussin struct manpage *page; 699*61d06d6bSBaptiste Daroussin char *file; 700*61d06d6bSBaptiste Daroussin int globres; 701*61d06d6bSBaptiste Daroussin enum form form; 702*61d06d6bSBaptiste Daroussin 703*61d06d6bSBaptiste Daroussin form = FORM_SRC; 704*61d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s.%s", 705*61d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name, sec); 706*61d06d6bSBaptiste Daroussin if (access(file, R_OK) != -1) 707*61d06d6bSBaptiste Daroussin goto found; 708*61d06d6bSBaptiste Daroussin free(file); 709*61d06d6bSBaptiste Daroussin 710*61d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/cat%s/%s.0", 711*61d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name); 712*61d06d6bSBaptiste Daroussin if (access(file, R_OK) != -1) { 713*61d06d6bSBaptiste Daroussin form = FORM_CAT; 714*61d06d6bSBaptiste Daroussin goto found; 715*61d06d6bSBaptiste Daroussin } 716*61d06d6bSBaptiste Daroussin free(file); 717*61d06d6bSBaptiste Daroussin 718*61d06d6bSBaptiste Daroussin if (arch != NULL) { 719*61d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", 720*61d06d6bSBaptiste Daroussin paths->paths[ipath], sec, arch, name, sec); 721*61d06d6bSBaptiste Daroussin if (access(file, R_OK) != -1) 722*61d06d6bSBaptiste Daroussin goto found; 723*61d06d6bSBaptiste Daroussin free(file); 724*61d06d6bSBaptiste Daroussin } 725*61d06d6bSBaptiste Daroussin 726*61d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", 727*61d06d6bSBaptiste Daroussin paths->paths[ipath], sec, name); 728*61d06d6bSBaptiste Daroussin globres = glob(file, 0, NULL, &globinfo); 729*61d06d6bSBaptiste Daroussin if (globres != 0 && globres != GLOB_NOMATCH) 730*61d06d6bSBaptiste Daroussin warn("%s: glob", file); 731*61d06d6bSBaptiste Daroussin free(file); 732*61d06d6bSBaptiste Daroussin if (globres == 0) 733*61d06d6bSBaptiste Daroussin file = mandoc_strdup(*globinfo.gl_pathv); 734*61d06d6bSBaptiste Daroussin globfree(&globinfo); 735*61d06d6bSBaptiste Daroussin if (globres == 0) 736*61d06d6bSBaptiste Daroussin goto found; 737*61d06d6bSBaptiste Daroussin if (res != NULL || ipath + 1 != paths->sz) 738*61d06d6bSBaptiste Daroussin return 0; 739*61d06d6bSBaptiste Daroussin 740*61d06d6bSBaptiste Daroussin mandoc_asprintf(&file, "%s.%s", name, sec); 741*61d06d6bSBaptiste Daroussin globres = access(file, R_OK); 742*61d06d6bSBaptiste Daroussin free(file); 743*61d06d6bSBaptiste Daroussin return globres != -1; 744*61d06d6bSBaptiste Daroussin 745*61d06d6bSBaptiste Daroussin found: 746*61d06d6bSBaptiste Daroussin warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", 747*61d06d6bSBaptiste Daroussin name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); 748*61d06d6bSBaptiste Daroussin if (res == NULL) { 749*61d06d6bSBaptiste Daroussin free(file); 750*61d06d6bSBaptiste Daroussin return 1; 751*61d06d6bSBaptiste Daroussin } 752*61d06d6bSBaptiste Daroussin *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); 753*61d06d6bSBaptiste Daroussin page = *res + (*ressz - 1); 754*61d06d6bSBaptiste Daroussin page->file = file; 755*61d06d6bSBaptiste Daroussin page->names = NULL; 756*61d06d6bSBaptiste Daroussin page->output = NULL; 757*61d06d6bSBaptiste Daroussin page->ipath = ipath; 758*61d06d6bSBaptiste Daroussin page->bits = NAME_FILE & NAME_MASK; 759*61d06d6bSBaptiste Daroussin page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; 760*61d06d6bSBaptiste Daroussin page->form = form; 761*61d06d6bSBaptiste Daroussin return 1; 762*61d06d6bSBaptiste Daroussin } 763*61d06d6bSBaptiste Daroussin 764*61d06d6bSBaptiste Daroussin static int 765*61d06d6bSBaptiste Daroussin fs_search(const struct mansearch *cfg, const struct manpaths *paths, 766*61d06d6bSBaptiste Daroussin int argc, char **argv, struct manpage **res, size_t *ressz) 767*61d06d6bSBaptiste Daroussin { 768*61d06d6bSBaptiste Daroussin const char *const sections[] = 769*61d06d6bSBaptiste Daroussin {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; 770*61d06d6bSBaptiste Daroussin const size_t nsec = sizeof(sections)/sizeof(sections[0]); 771*61d06d6bSBaptiste Daroussin 772*61d06d6bSBaptiste Daroussin size_t ipath, isec, lastsz; 773*61d06d6bSBaptiste Daroussin 774*61d06d6bSBaptiste Daroussin assert(cfg->argmode == ARG_NAME); 775*61d06d6bSBaptiste Daroussin 776*61d06d6bSBaptiste Daroussin if (res != NULL) 777*61d06d6bSBaptiste Daroussin *res = NULL; 778*61d06d6bSBaptiste Daroussin *ressz = lastsz = 0; 779*61d06d6bSBaptiste Daroussin while (argc) { 780*61d06d6bSBaptiste Daroussin for (ipath = 0; ipath < paths->sz; ipath++) { 781*61d06d6bSBaptiste Daroussin if (cfg->sec != NULL) { 782*61d06d6bSBaptiste Daroussin if (fs_lookup(paths, ipath, cfg->sec, 783*61d06d6bSBaptiste Daroussin cfg->arch, *argv, res, ressz) && 784*61d06d6bSBaptiste Daroussin cfg->firstmatch) 785*61d06d6bSBaptiste Daroussin return 1; 786*61d06d6bSBaptiste Daroussin } else for (isec = 0; isec < nsec; isec++) 787*61d06d6bSBaptiste Daroussin if (fs_lookup(paths, ipath, sections[isec], 788*61d06d6bSBaptiste Daroussin cfg->arch, *argv, res, ressz) && 789*61d06d6bSBaptiste Daroussin cfg->firstmatch) 790*61d06d6bSBaptiste Daroussin return 1; 791*61d06d6bSBaptiste Daroussin } 792*61d06d6bSBaptiste Daroussin if (res != NULL && *ressz == lastsz && 793*61d06d6bSBaptiste Daroussin strchr(*argv, '/') == NULL) 794*61d06d6bSBaptiste Daroussin warnx("No entry for %s in the manual.", *argv); 795*61d06d6bSBaptiste Daroussin lastsz = *ressz; 796*61d06d6bSBaptiste Daroussin argv++; 797*61d06d6bSBaptiste Daroussin argc--; 798*61d06d6bSBaptiste Daroussin } 799*61d06d6bSBaptiste Daroussin return 0; 800*61d06d6bSBaptiste Daroussin } 801*61d06d6bSBaptiste Daroussin 802*61d06d6bSBaptiste Daroussin static void 803*61d06d6bSBaptiste Daroussin parse(struct curparse *curp, int fd, const char *file) 804*61d06d6bSBaptiste Daroussin { 805*61d06d6bSBaptiste Daroussin enum mandoclevel rctmp; 806*61d06d6bSBaptiste Daroussin struct roff_man *man; 807*61d06d6bSBaptiste Daroussin 808*61d06d6bSBaptiste Daroussin /* Begin by parsing the file itself. */ 809*61d06d6bSBaptiste Daroussin 810*61d06d6bSBaptiste Daroussin assert(file); 811*61d06d6bSBaptiste Daroussin assert(fd >= 0); 812*61d06d6bSBaptiste Daroussin 813*61d06d6bSBaptiste Daroussin rctmp = mparse_readfd(curp->mp, fd, file); 814*61d06d6bSBaptiste Daroussin if (fd != STDIN_FILENO) 815*61d06d6bSBaptiste Daroussin close(fd); 816*61d06d6bSBaptiste Daroussin if (rc < rctmp) 817*61d06d6bSBaptiste Daroussin rc = rctmp; 818*61d06d6bSBaptiste Daroussin 819*61d06d6bSBaptiste Daroussin /* 820*61d06d6bSBaptiste Daroussin * With -Wstop and warnings or errors of at least the requested 821*61d06d6bSBaptiste Daroussin * level, do not produce output. 822*61d06d6bSBaptiste Daroussin */ 823*61d06d6bSBaptiste Daroussin 824*61d06d6bSBaptiste Daroussin if (rctmp != MANDOCLEVEL_OK && curp->wstop) 825*61d06d6bSBaptiste Daroussin return; 826*61d06d6bSBaptiste Daroussin 827*61d06d6bSBaptiste Daroussin if (curp->outdata == NULL) 828*61d06d6bSBaptiste Daroussin outdata_alloc(curp); 829*61d06d6bSBaptiste Daroussin 830*61d06d6bSBaptiste Daroussin mparse_result(curp->mp, &man, NULL); 831*61d06d6bSBaptiste Daroussin 832*61d06d6bSBaptiste Daroussin /* Execute the out device, if it exists. */ 833*61d06d6bSBaptiste Daroussin 834*61d06d6bSBaptiste Daroussin if (man == NULL) 835*61d06d6bSBaptiste Daroussin return; 836*61d06d6bSBaptiste Daroussin mandoc_xr_reset(); 837*61d06d6bSBaptiste Daroussin if (man->macroset == MACROSET_MDOC) { 838*61d06d6bSBaptiste Daroussin if (curp->outtype != OUTT_TREE || !curp->outopts->noval) 839*61d06d6bSBaptiste Daroussin mdoc_validate(man); 840*61d06d6bSBaptiste Daroussin switch (curp->outtype) { 841*61d06d6bSBaptiste Daroussin case OUTT_HTML: 842*61d06d6bSBaptiste Daroussin html_mdoc(curp->outdata, man); 843*61d06d6bSBaptiste Daroussin break; 844*61d06d6bSBaptiste Daroussin case OUTT_TREE: 845*61d06d6bSBaptiste Daroussin tree_mdoc(curp->outdata, man); 846*61d06d6bSBaptiste Daroussin break; 847*61d06d6bSBaptiste Daroussin case OUTT_MAN: 848*61d06d6bSBaptiste Daroussin man_mdoc(curp->outdata, man); 849*61d06d6bSBaptiste Daroussin break; 850*61d06d6bSBaptiste Daroussin case OUTT_PDF: 851*61d06d6bSBaptiste Daroussin case OUTT_ASCII: 852*61d06d6bSBaptiste Daroussin case OUTT_UTF8: 853*61d06d6bSBaptiste Daroussin case OUTT_LOCALE: 854*61d06d6bSBaptiste Daroussin case OUTT_PS: 855*61d06d6bSBaptiste Daroussin terminal_mdoc(curp->outdata, man); 856*61d06d6bSBaptiste Daroussin break; 857*61d06d6bSBaptiste Daroussin case OUTT_MARKDOWN: 858*61d06d6bSBaptiste Daroussin markdown_mdoc(curp->outdata, man); 859*61d06d6bSBaptiste Daroussin break; 860*61d06d6bSBaptiste Daroussin default: 861*61d06d6bSBaptiste Daroussin break; 862*61d06d6bSBaptiste Daroussin } 863*61d06d6bSBaptiste Daroussin } 864*61d06d6bSBaptiste Daroussin if (man->macroset == MACROSET_MAN) { 865*61d06d6bSBaptiste Daroussin if (curp->outtype != OUTT_TREE || !curp->outopts->noval) 866*61d06d6bSBaptiste Daroussin man_validate(man); 867*61d06d6bSBaptiste Daroussin switch (curp->outtype) { 868*61d06d6bSBaptiste Daroussin case OUTT_HTML: 869*61d06d6bSBaptiste Daroussin html_man(curp->outdata, man); 870*61d06d6bSBaptiste Daroussin break; 871*61d06d6bSBaptiste Daroussin case OUTT_TREE: 872*61d06d6bSBaptiste Daroussin tree_man(curp->outdata, man); 873*61d06d6bSBaptiste Daroussin break; 874*61d06d6bSBaptiste Daroussin case OUTT_MAN: 875*61d06d6bSBaptiste Daroussin man_man(curp->outdata, man); 876*61d06d6bSBaptiste Daroussin break; 877*61d06d6bSBaptiste Daroussin case OUTT_PDF: 878*61d06d6bSBaptiste Daroussin case OUTT_ASCII: 879*61d06d6bSBaptiste Daroussin case OUTT_UTF8: 880*61d06d6bSBaptiste Daroussin case OUTT_LOCALE: 881*61d06d6bSBaptiste Daroussin case OUTT_PS: 882*61d06d6bSBaptiste Daroussin terminal_man(curp->outdata, man); 883*61d06d6bSBaptiste Daroussin break; 884*61d06d6bSBaptiste Daroussin default: 885*61d06d6bSBaptiste Daroussin break; 886*61d06d6bSBaptiste Daroussin } 887*61d06d6bSBaptiste Daroussin } 888*61d06d6bSBaptiste Daroussin if (curp->mmin < MANDOCERR_STYLE) 889*61d06d6bSBaptiste Daroussin check_xr(file); 890*61d06d6bSBaptiste Daroussin mparse_updaterc(curp->mp, &rc); 891*61d06d6bSBaptiste Daroussin } 892*61d06d6bSBaptiste Daroussin 893*61d06d6bSBaptiste Daroussin static void 894*61d06d6bSBaptiste Daroussin check_xr(const char *file) 895*61d06d6bSBaptiste Daroussin { 896*61d06d6bSBaptiste Daroussin static struct manpaths paths; 897*61d06d6bSBaptiste Daroussin struct mansearch search; 898*61d06d6bSBaptiste Daroussin struct mandoc_xr *xr; 899*61d06d6bSBaptiste Daroussin char *cp; 900*61d06d6bSBaptiste Daroussin size_t sz; 901*61d06d6bSBaptiste Daroussin 902*61d06d6bSBaptiste Daroussin if (paths.sz == 0) 903*61d06d6bSBaptiste Daroussin manpath_base(&paths); 904*61d06d6bSBaptiste Daroussin 905*61d06d6bSBaptiste Daroussin for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { 906*61d06d6bSBaptiste Daroussin if (xr->line == -1) 907*61d06d6bSBaptiste Daroussin continue; 908*61d06d6bSBaptiste Daroussin search.arch = NULL; 909*61d06d6bSBaptiste Daroussin search.sec = xr->sec; 910*61d06d6bSBaptiste Daroussin search.outkey = NULL; 911*61d06d6bSBaptiste Daroussin search.argmode = ARG_NAME; 912*61d06d6bSBaptiste Daroussin search.firstmatch = 1; 913*61d06d6bSBaptiste Daroussin if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz)) 914*61d06d6bSBaptiste Daroussin continue; 915*61d06d6bSBaptiste Daroussin if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz)) 916*61d06d6bSBaptiste Daroussin continue; 917*61d06d6bSBaptiste Daroussin if (xr->count == 1) 918*61d06d6bSBaptiste Daroussin mandoc_asprintf(&cp, "Xr %s %s", xr->name, xr->sec); 919*61d06d6bSBaptiste Daroussin else 920*61d06d6bSBaptiste Daroussin mandoc_asprintf(&cp, "Xr %s %s (%d times)", 921*61d06d6bSBaptiste Daroussin xr->name, xr->sec, xr->count); 922*61d06d6bSBaptiste Daroussin mmsg(MANDOCERR_XR_BAD, MANDOCLEVEL_STYLE, 923*61d06d6bSBaptiste Daroussin file, xr->line, xr->pos + 1, cp); 924*61d06d6bSBaptiste Daroussin free(cp); 925*61d06d6bSBaptiste Daroussin } 926*61d06d6bSBaptiste Daroussin } 927*61d06d6bSBaptiste Daroussin 928*61d06d6bSBaptiste Daroussin static void 929*61d06d6bSBaptiste Daroussin outdata_alloc(struct curparse *curp) 930*61d06d6bSBaptiste Daroussin { 931*61d06d6bSBaptiste Daroussin switch (curp->outtype) { 932*61d06d6bSBaptiste Daroussin case OUTT_HTML: 933*61d06d6bSBaptiste Daroussin curp->outdata = html_alloc(curp->outopts); 934*61d06d6bSBaptiste Daroussin break; 935*61d06d6bSBaptiste Daroussin case OUTT_UTF8: 936*61d06d6bSBaptiste Daroussin curp->outdata = utf8_alloc(curp->outopts); 937*61d06d6bSBaptiste Daroussin break; 938*61d06d6bSBaptiste Daroussin case OUTT_LOCALE: 939*61d06d6bSBaptiste Daroussin curp->outdata = locale_alloc(curp->outopts); 940*61d06d6bSBaptiste Daroussin break; 941*61d06d6bSBaptiste Daroussin case OUTT_ASCII: 942*61d06d6bSBaptiste Daroussin curp->outdata = ascii_alloc(curp->outopts); 943*61d06d6bSBaptiste Daroussin break; 944*61d06d6bSBaptiste Daroussin case OUTT_PDF: 945*61d06d6bSBaptiste Daroussin curp->outdata = pdf_alloc(curp->outopts); 946*61d06d6bSBaptiste Daroussin break; 947*61d06d6bSBaptiste Daroussin case OUTT_PS: 948*61d06d6bSBaptiste Daroussin curp->outdata = ps_alloc(curp->outopts); 949*61d06d6bSBaptiste Daroussin break; 950*61d06d6bSBaptiste Daroussin default: 951*61d06d6bSBaptiste Daroussin break; 952*61d06d6bSBaptiste Daroussin } 953*61d06d6bSBaptiste Daroussin } 954*61d06d6bSBaptiste Daroussin 955*61d06d6bSBaptiste Daroussin static void 956*61d06d6bSBaptiste Daroussin passthrough(const char *file, int fd, int synopsis_only) 957*61d06d6bSBaptiste Daroussin { 958*61d06d6bSBaptiste Daroussin const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; 959*61d06d6bSBaptiste Daroussin const char synr[] = "SYNOPSIS"; 960*61d06d6bSBaptiste Daroussin 961*61d06d6bSBaptiste Daroussin FILE *stream; 962*61d06d6bSBaptiste Daroussin const char *syscall; 963*61d06d6bSBaptiste Daroussin char *line, *cp; 964*61d06d6bSBaptiste Daroussin size_t linesz; 965*61d06d6bSBaptiste Daroussin ssize_t len, written; 966*61d06d6bSBaptiste Daroussin int print; 967*61d06d6bSBaptiste Daroussin 968*61d06d6bSBaptiste Daroussin line = NULL; 969*61d06d6bSBaptiste Daroussin linesz = 0; 970*61d06d6bSBaptiste Daroussin 971*61d06d6bSBaptiste Daroussin if (fflush(stdout) == EOF) { 972*61d06d6bSBaptiste Daroussin syscall = "fflush"; 973*61d06d6bSBaptiste Daroussin goto fail; 974*61d06d6bSBaptiste Daroussin } 975*61d06d6bSBaptiste Daroussin 976*61d06d6bSBaptiste Daroussin if ((stream = fdopen(fd, "r")) == NULL) { 977*61d06d6bSBaptiste Daroussin close(fd); 978*61d06d6bSBaptiste Daroussin syscall = "fdopen"; 979*61d06d6bSBaptiste Daroussin goto fail; 980*61d06d6bSBaptiste Daroussin } 981*61d06d6bSBaptiste Daroussin 982*61d06d6bSBaptiste Daroussin print = 0; 983*61d06d6bSBaptiste Daroussin while ((len = getline(&line, &linesz, stream)) != -1) { 984*61d06d6bSBaptiste Daroussin cp = line; 985*61d06d6bSBaptiste Daroussin if (synopsis_only) { 986*61d06d6bSBaptiste Daroussin if (print) { 987*61d06d6bSBaptiste Daroussin if ( ! isspace((unsigned char)*cp)) 988*61d06d6bSBaptiste Daroussin goto done; 989*61d06d6bSBaptiste Daroussin while (isspace((unsigned char)*cp)) { 990*61d06d6bSBaptiste Daroussin cp++; 991*61d06d6bSBaptiste Daroussin len--; 992*61d06d6bSBaptiste Daroussin } 993*61d06d6bSBaptiste Daroussin } else { 994*61d06d6bSBaptiste Daroussin if (strcmp(cp, synb) == 0 || 995*61d06d6bSBaptiste Daroussin strcmp(cp, synr) == 0) 996*61d06d6bSBaptiste Daroussin print = 1; 997*61d06d6bSBaptiste Daroussin continue; 998*61d06d6bSBaptiste Daroussin } 999*61d06d6bSBaptiste Daroussin } 1000*61d06d6bSBaptiste Daroussin for (; len > 0; len -= written) { 1001*61d06d6bSBaptiste Daroussin if ((written = write(STDOUT_FILENO, cp, len)) != -1) 1002*61d06d6bSBaptiste Daroussin continue; 1003*61d06d6bSBaptiste Daroussin fclose(stream); 1004*61d06d6bSBaptiste Daroussin syscall = "write"; 1005*61d06d6bSBaptiste Daroussin goto fail; 1006*61d06d6bSBaptiste Daroussin } 1007*61d06d6bSBaptiste Daroussin } 1008*61d06d6bSBaptiste Daroussin 1009*61d06d6bSBaptiste Daroussin if (ferror(stream)) { 1010*61d06d6bSBaptiste Daroussin fclose(stream); 1011*61d06d6bSBaptiste Daroussin syscall = "getline"; 1012*61d06d6bSBaptiste Daroussin goto fail; 1013*61d06d6bSBaptiste Daroussin } 1014*61d06d6bSBaptiste Daroussin 1015*61d06d6bSBaptiste Daroussin done: 1016*61d06d6bSBaptiste Daroussin free(line); 1017*61d06d6bSBaptiste Daroussin fclose(stream); 1018*61d06d6bSBaptiste Daroussin return; 1019*61d06d6bSBaptiste Daroussin 1020*61d06d6bSBaptiste Daroussin fail: 1021*61d06d6bSBaptiste Daroussin free(line); 1022*61d06d6bSBaptiste Daroussin warn("%s: SYSERR: %s", file, syscall); 1023*61d06d6bSBaptiste Daroussin if (rc < MANDOCLEVEL_SYSERR) 1024*61d06d6bSBaptiste Daroussin rc = MANDOCLEVEL_SYSERR; 1025*61d06d6bSBaptiste Daroussin } 1026*61d06d6bSBaptiste Daroussin 1027*61d06d6bSBaptiste Daroussin static int 1028*61d06d6bSBaptiste Daroussin koptions(int *options, char *arg) 1029*61d06d6bSBaptiste Daroussin { 1030*61d06d6bSBaptiste Daroussin 1031*61d06d6bSBaptiste Daroussin if ( ! strcmp(arg, "utf-8")) { 1032*61d06d6bSBaptiste Daroussin *options |= MPARSE_UTF8; 1033*61d06d6bSBaptiste Daroussin *options &= ~MPARSE_LATIN1; 1034*61d06d6bSBaptiste Daroussin } else if ( ! strcmp(arg, "iso-8859-1")) { 1035*61d06d6bSBaptiste Daroussin *options |= MPARSE_LATIN1; 1036*61d06d6bSBaptiste Daroussin *options &= ~MPARSE_UTF8; 1037*61d06d6bSBaptiste Daroussin } else if ( ! strcmp(arg, "us-ascii")) { 1038*61d06d6bSBaptiste Daroussin *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); 1039*61d06d6bSBaptiste Daroussin } else { 1040*61d06d6bSBaptiste Daroussin warnx("-K %s: Bad argument", arg); 1041*61d06d6bSBaptiste Daroussin return 0; 1042*61d06d6bSBaptiste Daroussin } 1043*61d06d6bSBaptiste Daroussin return 1; 1044*61d06d6bSBaptiste Daroussin } 1045*61d06d6bSBaptiste Daroussin 1046*61d06d6bSBaptiste Daroussin static void 1047*61d06d6bSBaptiste Daroussin moptions(int *options, char *arg) 1048*61d06d6bSBaptiste Daroussin { 1049*61d06d6bSBaptiste Daroussin 1050*61d06d6bSBaptiste Daroussin if (arg == NULL) 1051*61d06d6bSBaptiste Daroussin return; 1052*61d06d6bSBaptiste Daroussin if (strcmp(arg, "doc") == 0) 1053*61d06d6bSBaptiste Daroussin *options |= MPARSE_MDOC; 1054*61d06d6bSBaptiste Daroussin else if (strcmp(arg, "an") == 0) 1055*61d06d6bSBaptiste Daroussin *options |= MPARSE_MAN; 1056*61d06d6bSBaptiste Daroussin } 1057*61d06d6bSBaptiste Daroussin 1058*61d06d6bSBaptiste Daroussin static int 1059*61d06d6bSBaptiste Daroussin toptions(struct curparse *curp, char *arg) 1060*61d06d6bSBaptiste Daroussin { 1061*61d06d6bSBaptiste Daroussin 1062*61d06d6bSBaptiste Daroussin if (0 == strcmp(arg, "ascii")) 1063*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_ASCII; 1064*61d06d6bSBaptiste Daroussin else if (0 == strcmp(arg, "lint")) { 1065*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_LINT; 1066*61d06d6bSBaptiste Daroussin curp->mmin = MANDOCERR_BASE; 1067*61d06d6bSBaptiste Daroussin mmsg_stream = stdout; 1068*61d06d6bSBaptiste Daroussin } else if (0 == strcmp(arg, "tree")) 1069*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_TREE; 1070*61d06d6bSBaptiste Daroussin else if (0 == strcmp(arg, "man")) 1071*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_MAN; 1072*61d06d6bSBaptiste Daroussin else if (0 == strcmp(arg, "html")) 1073*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_HTML; 1074*61d06d6bSBaptiste Daroussin else if (0 == strcmp(arg, "markdown")) 1075*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_MARKDOWN; 1076*61d06d6bSBaptiste Daroussin else if (0 == strcmp(arg, "utf8")) 1077*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_UTF8; 1078*61d06d6bSBaptiste Daroussin else if (0 == strcmp(arg, "locale")) 1079*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_LOCALE; 1080*61d06d6bSBaptiste Daroussin else if (0 == strcmp(arg, "ps")) 1081*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_PS; 1082*61d06d6bSBaptiste Daroussin else if (0 == strcmp(arg, "pdf")) 1083*61d06d6bSBaptiste Daroussin curp->outtype = OUTT_PDF; 1084*61d06d6bSBaptiste Daroussin else { 1085*61d06d6bSBaptiste Daroussin warnx("-T %s: Bad argument", arg); 1086*61d06d6bSBaptiste Daroussin return 0; 1087*61d06d6bSBaptiste Daroussin } 1088*61d06d6bSBaptiste Daroussin 1089*61d06d6bSBaptiste Daroussin return 1; 1090*61d06d6bSBaptiste Daroussin } 1091*61d06d6bSBaptiste Daroussin 1092*61d06d6bSBaptiste Daroussin static int 1093*61d06d6bSBaptiste Daroussin woptions(struct curparse *curp, char *arg) 1094*61d06d6bSBaptiste Daroussin { 1095*61d06d6bSBaptiste Daroussin char *v, *o; 1096*61d06d6bSBaptiste Daroussin const char *toks[11]; 1097*61d06d6bSBaptiste Daroussin 1098*61d06d6bSBaptiste Daroussin toks[0] = "stop"; 1099*61d06d6bSBaptiste Daroussin toks[1] = "all"; 1100*61d06d6bSBaptiste Daroussin toks[2] = "base"; 1101*61d06d6bSBaptiste Daroussin toks[3] = "style"; 1102*61d06d6bSBaptiste Daroussin toks[4] = "warning"; 1103*61d06d6bSBaptiste Daroussin toks[5] = "error"; 1104*61d06d6bSBaptiste Daroussin toks[6] = "unsupp"; 1105*61d06d6bSBaptiste Daroussin toks[7] = "fatal"; 1106*61d06d6bSBaptiste Daroussin toks[8] = "openbsd"; 1107*61d06d6bSBaptiste Daroussin toks[9] = "netbsd"; 1108*61d06d6bSBaptiste Daroussin toks[10] = NULL; 1109*61d06d6bSBaptiste Daroussin 1110*61d06d6bSBaptiste Daroussin while (*arg) { 1111*61d06d6bSBaptiste Daroussin o = arg; 1112*61d06d6bSBaptiste Daroussin switch (getsubopt(&arg, (char * const *)toks, &v)) { 1113*61d06d6bSBaptiste Daroussin case 0: 1114*61d06d6bSBaptiste Daroussin curp->wstop = 1; 1115*61d06d6bSBaptiste Daroussin break; 1116*61d06d6bSBaptiste Daroussin case 1: 1117*61d06d6bSBaptiste Daroussin case 2: 1118*61d06d6bSBaptiste Daroussin curp->mmin = MANDOCERR_BASE; 1119*61d06d6bSBaptiste Daroussin break; 1120*61d06d6bSBaptiste Daroussin case 3: 1121*61d06d6bSBaptiste Daroussin curp->mmin = MANDOCERR_STYLE; 1122*61d06d6bSBaptiste Daroussin break; 1123*61d06d6bSBaptiste Daroussin case 4: 1124*61d06d6bSBaptiste Daroussin curp->mmin = MANDOCERR_WARNING; 1125*61d06d6bSBaptiste Daroussin break; 1126*61d06d6bSBaptiste Daroussin case 5: 1127*61d06d6bSBaptiste Daroussin curp->mmin = MANDOCERR_ERROR; 1128*61d06d6bSBaptiste Daroussin break; 1129*61d06d6bSBaptiste Daroussin case 6: 1130*61d06d6bSBaptiste Daroussin curp->mmin = MANDOCERR_UNSUPP; 1131*61d06d6bSBaptiste Daroussin break; 1132*61d06d6bSBaptiste Daroussin case 7: 1133*61d06d6bSBaptiste Daroussin curp->mmin = MANDOCERR_MAX; 1134*61d06d6bSBaptiste Daroussin break; 1135*61d06d6bSBaptiste Daroussin case 8: 1136*61d06d6bSBaptiste Daroussin curp->mmin = MANDOCERR_BASE; 1137*61d06d6bSBaptiste Daroussin curp->os_e = MANDOC_OS_OPENBSD; 1138*61d06d6bSBaptiste Daroussin break; 1139*61d06d6bSBaptiste Daroussin case 9: 1140*61d06d6bSBaptiste Daroussin curp->mmin = MANDOCERR_BASE; 1141*61d06d6bSBaptiste Daroussin curp->os_e = MANDOC_OS_NETBSD; 1142*61d06d6bSBaptiste Daroussin break; 1143*61d06d6bSBaptiste Daroussin default: 1144*61d06d6bSBaptiste Daroussin warnx("-W %s: Bad argument", o); 1145*61d06d6bSBaptiste Daroussin return 0; 1146*61d06d6bSBaptiste Daroussin } 1147*61d06d6bSBaptiste Daroussin } 1148*61d06d6bSBaptiste Daroussin return 1; 1149*61d06d6bSBaptiste Daroussin } 1150*61d06d6bSBaptiste Daroussin 1151*61d06d6bSBaptiste Daroussin static void 1152*61d06d6bSBaptiste Daroussin mmsg(enum mandocerr t, enum mandoclevel lvl, 1153*61d06d6bSBaptiste Daroussin const char *file, int line, int col, const char *msg) 1154*61d06d6bSBaptiste Daroussin { 1155*61d06d6bSBaptiste Daroussin const char *mparse_msg; 1156*61d06d6bSBaptiste Daroussin 1157*61d06d6bSBaptiste Daroussin fprintf(mmsg_stream, "%s: %s:", getprogname(), 1158*61d06d6bSBaptiste Daroussin file == NULL ? "<stdin>" : file); 1159*61d06d6bSBaptiste Daroussin 1160*61d06d6bSBaptiste Daroussin if (line) 1161*61d06d6bSBaptiste Daroussin fprintf(mmsg_stream, "%d:%d:", line, col + 1); 1162*61d06d6bSBaptiste Daroussin 1163*61d06d6bSBaptiste Daroussin fprintf(mmsg_stream, " %s", mparse_strlevel(lvl)); 1164*61d06d6bSBaptiste Daroussin 1165*61d06d6bSBaptiste Daroussin if ((mparse_msg = mparse_strerror(t)) != NULL) 1166*61d06d6bSBaptiste Daroussin fprintf(mmsg_stream, ": %s", mparse_msg); 1167*61d06d6bSBaptiste Daroussin 1168*61d06d6bSBaptiste Daroussin if (msg) 1169*61d06d6bSBaptiste Daroussin fprintf(mmsg_stream, ": %s", msg); 1170*61d06d6bSBaptiste Daroussin 1171*61d06d6bSBaptiste Daroussin fputc('\n', mmsg_stream); 1172*61d06d6bSBaptiste Daroussin } 1173*61d06d6bSBaptiste Daroussin 1174*61d06d6bSBaptiste Daroussin static pid_t 1175*61d06d6bSBaptiste Daroussin spawn_pager(struct tag_files *tag_files) 1176*61d06d6bSBaptiste Daroussin { 1177*61d06d6bSBaptiste Daroussin const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ 1178*61d06d6bSBaptiste Daroussin #define MAX_PAGER_ARGS 16 1179*61d06d6bSBaptiste Daroussin char *argv[MAX_PAGER_ARGS]; 1180*61d06d6bSBaptiste Daroussin const char *pager; 1181*61d06d6bSBaptiste Daroussin char *cp; 1182*61d06d6bSBaptiste Daroussin size_t cmdlen; 1183*61d06d6bSBaptiste Daroussin int argc; 1184*61d06d6bSBaptiste Daroussin pid_t pager_pid; 1185*61d06d6bSBaptiste Daroussin 1186*61d06d6bSBaptiste Daroussin pager = getenv("MANPAGER"); 1187*61d06d6bSBaptiste Daroussin if (pager == NULL || *pager == '\0') 1188*61d06d6bSBaptiste Daroussin pager = getenv("PAGER"); 1189*61d06d6bSBaptiste Daroussin if (pager == NULL || *pager == '\0') 1190*61d06d6bSBaptiste Daroussin pager = "less -s"; 1191*61d06d6bSBaptiste Daroussin cp = mandoc_strdup(pager); 1192*61d06d6bSBaptiste Daroussin 1193*61d06d6bSBaptiste Daroussin /* 1194*61d06d6bSBaptiste Daroussin * Parse the pager command into words. 1195*61d06d6bSBaptiste Daroussin * Intentionally do not do anything fancy here. 1196*61d06d6bSBaptiste Daroussin */ 1197*61d06d6bSBaptiste Daroussin 1198*61d06d6bSBaptiste Daroussin argc = 0; 1199*61d06d6bSBaptiste Daroussin while (argc + 4 < MAX_PAGER_ARGS) { 1200*61d06d6bSBaptiste Daroussin argv[argc++] = cp; 1201*61d06d6bSBaptiste Daroussin cp = strchr(cp, ' '); 1202*61d06d6bSBaptiste Daroussin if (cp == NULL) 1203*61d06d6bSBaptiste Daroussin break; 1204*61d06d6bSBaptiste Daroussin *cp++ = '\0'; 1205*61d06d6bSBaptiste Daroussin while (*cp == ' ') 1206*61d06d6bSBaptiste Daroussin cp++; 1207*61d06d6bSBaptiste Daroussin if (*cp == '\0') 1208*61d06d6bSBaptiste Daroussin break; 1209*61d06d6bSBaptiste Daroussin } 1210*61d06d6bSBaptiste Daroussin 1211*61d06d6bSBaptiste Daroussin /* For less(1), use the tag file. */ 1212*61d06d6bSBaptiste Daroussin 1213*61d06d6bSBaptiste Daroussin if ((cmdlen = strlen(argv[0])) >= 4) { 1214*61d06d6bSBaptiste Daroussin cp = argv[0] + cmdlen - 4; 1215*61d06d6bSBaptiste Daroussin if (strcmp(cp, "less") == 0) { 1216*61d06d6bSBaptiste Daroussin argv[argc++] = mandoc_strdup("-T"); 1217*61d06d6bSBaptiste Daroussin argv[argc++] = tag_files->tfn; 1218*61d06d6bSBaptiste Daroussin } 1219*61d06d6bSBaptiste Daroussin } 1220*61d06d6bSBaptiste Daroussin argv[argc++] = tag_files->ofn; 1221*61d06d6bSBaptiste Daroussin argv[argc] = NULL; 1222*61d06d6bSBaptiste Daroussin 1223*61d06d6bSBaptiste Daroussin switch (pager_pid = fork()) { 1224*61d06d6bSBaptiste Daroussin case -1: 1225*61d06d6bSBaptiste Daroussin err((int)MANDOCLEVEL_SYSERR, "fork"); 1226*61d06d6bSBaptiste Daroussin case 0: 1227*61d06d6bSBaptiste Daroussin break; 1228*61d06d6bSBaptiste Daroussin default: 1229*61d06d6bSBaptiste Daroussin (void)setpgid(pager_pid, 0); 1230*61d06d6bSBaptiste Daroussin (void)tcsetpgrp(tag_files->ofd, pager_pid); 1231*61d06d6bSBaptiste Daroussin #if HAVE_PLEDGE 1232*61d06d6bSBaptiste Daroussin if (pledge("stdio rpath tmppath tty proc", NULL) == -1) 1233*61d06d6bSBaptiste Daroussin err((int)MANDOCLEVEL_SYSERR, "pledge"); 1234*61d06d6bSBaptiste Daroussin #endif 1235*61d06d6bSBaptiste Daroussin tag_files->pager_pid = pager_pid; 1236*61d06d6bSBaptiste Daroussin return pager_pid; 1237*61d06d6bSBaptiste Daroussin } 1238*61d06d6bSBaptiste Daroussin 1239*61d06d6bSBaptiste Daroussin /* The child process becomes the pager. */ 1240*61d06d6bSBaptiste Daroussin 1241*61d06d6bSBaptiste Daroussin if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) 1242*61d06d6bSBaptiste Daroussin err((int)MANDOCLEVEL_SYSERR, "pager stdout"); 1243*61d06d6bSBaptiste Daroussin close(tag_files->ofd); 1244*61d06d6bSBaptiste Daroussin assert(tag_files->tfd == -1); 1245*61d06d6bSBaptiste Daroussin 1246*61d06d6bSBaptiste Daroussin /* Do not start the pager before controlling the terminal. */ 1247*61d06d6bSBaptiste Daroussin 1248*61d06d6bSBaptiste Daroussin while (tcgetpgrp(STDOUT_FILENO) != getpid()) 1249*61d06d6bSBaptiste Daroussin nanosleep(&timeout, NULL); 1250*61d06d6bSBaptiste Daroussin 1251*61d06d6bSBaptiste Daroussin execvp(argv[0], argv); 1252*61d06d6bSBaptiste Daroussin err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]); 1253*61d06d6bSBaptiste Daroussin } 1254