1cec8643Michal Nowak/* $Id: main.c,v 1.322 2019/03/06 10:18:58 schwarze Exp $ */ 295c635eGarrett D'Amore/* 3260e9a8Yuri Pankov * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> 4cec8643Michal Nowak * Copyright (c) 2010-2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org> 5260e9a8Yuri Pankov * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> 695c635eGarrett D'Amore * 795c635eGarrett D'Amore * Permission to use, copy, modify, and distribute this software for any 895c635eGarrett D'Amore * purpose with or without fee is hereby granted, provided that the above 995c635eGarrett D'Amore * copyright notice and this permission notice appear in all copies. 1095c635eGarrett D'Amore * 11371584cYuri Pankov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES 1295c635eGarrett D'Amore * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13371584cYuri Pankov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR 1495c635eGarrett D'Amore * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1595c635eGarrett D'Amore * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1695c635eGarrett D'Amore * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1795c635eGarrett D'Amore * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1895c635eGarrett D'Amore */ 1995c635eGarrett D'Amore#include "config.h" 20260e9a8Yuri Pankov 21260e9a8Yuri Pankov#include <sys/types.h> 226640c13Yuri Pankov#include <sys/ioctl.h> 23260e9a8Yuri Pankov#include <sys/param.h> /* MACHINE */ 24260e9a8Yuri Pankov#include <sys/wait.h> 2595c635eGarrett D'Amore 2695c635eGarrett D'Amore#include <assert.h> 27260e9a8Yuri Pankov#include <ctype.h> 28371584cYuri Pankov#if HAVE_ERR 29371584cYuri Pankov#include <err.h> 30371584cYuri Pankov#endif 31260e9a8Yuri Pankov#include <errno.h> 32260e9a8Yuri Pankov#include <fcntl.h> 33260e9a8Yuri Pankov#include <glob.h> 34371584cYuri Pankov#if HAVE_SANDBOX_INIT 35371584cYuri Pankov#include <sandbox.h> 36371584cYuri Pankov#endif 37371584cYuri Pankov#include <signal.h> 3895c635eGarrett D'Amore#include <stdio.h> 3995c635eGarrett D'Amore#include <stdint.h> 4095c635eGarrett D'Amore#include <stdlib.h> 4195c635eGarrett D'Amore#include <string.h> 42cec8643Michal Nowak#include <termios.h> 43371584cYuri Pankov#include <time.h> 4495c635eGarrett D'Amore#include <unistd.h> 4595c635eGarrett D'Amore 46260e9a8Yuri Pankov#include "mandoc_aux.h" 47371584cYuri Pankov#include "mandoc.h" 48c66b804Yuri Pankov#include "mandoc_xr.h" 49371584cYuri Pankov#include "roff.h" 5095c635eGarrett D'Amore#include "mdoc.h" 5195c635eGarrett D'Amore#include "man.h" 52cec8643Michal Nowak#include "mandoc_parse.h" 53371584cYuri Pankov#include "tag.h" 54371584cYuri Pankov#include "main.h" 55371584cYuri Pankov#include "manconf.h" 56260e9a8Yuri Pankov#include "mansearch.h" 5795c635eGarrett D'Amore 58260e9a8Yuri Pankovenum outmode { 59260e9a8Yuri Pankov OUTMODE_DEF = 0, 60260e9a8Yuri Pankov OUTMODE_FLN, 61260e9a8Yuri Pankov OUTMODE_LST, 62260e9a8Yuri Pankov OUTMODE_ALL, 63260e9a8Yuri Pankov OUTMODE_ONE 64260e9a8Yuri Pankov}; 65260e9a8Yuri Pankov 6695c635eGarrett D'Amoreenum outt { 6795c635eGarrett D'Amore OUTT_ASCII = 0, /* -Tascii */ 6895c635eGarrett D'Amore OUTT_LOCALE, /* -Tlocale */ 6995c635eGarrett D'Amore OUTT_UTF8, /* -Tutf8 */ 7095c635eGarrett D'Amore OUTT_TREE, /* -Ttree */ 7195c635eGarrett D'Amore OUTT_MAN, /* -Tman */ 7295c635eGarrett D'Amore OUTT_HTML, /* -Thtml */ 73c66b804Yuri Pankov OUTT_MARKDOWN, /* -Tmarkdown */ 7495c635eGarrett D'Amore OUTT_LINT, /* -Tlint */ 7595c635eGarrett D'Amore OUTT_PS, /* -Tps */ 7695c635eGarrett D'Amore OUTT_PDF /* -Tpdf */ 7795c635eGarrett D'Amore}; 7895c635eGarrett D'Amore 7995c635eGarrett D'Amorestruct curparse { 8095c635eGarrett D'Amore struct mparse *mp; 81c66b804Yuri Pankov struct manoutput *outopts; /* output options */ 82c66b804Yuri Pankov void *outdata; /* data for output */ 83c66b804Yuri Pankov char *os_s; /* operating system for display */ 8495c635eGarrett D'Amore int wstop; /* stop after a file with a warning */ 85c66b804Yuri Pankov enum mandoc_os os_e; /* check base system conventions */ 86260e9a8Yuri Pankov enum outt outtype; /* which output to use */ 8795c635eGarrett D'Amore}; 8895c635eGarrett D'Amore 89a593473Yuri Pankov 90a593473Yuri Pankovint mandocdb(int, char *[]); 91a593473Yuri Pankov 92cec8643Michal Nowakstatic void check_xr(void); 93260e9a8Yuri Pankovstatic int fs_lookup(const struct manpaths *, 94260e9a8Yuri Pankov size_t ipath, const char *, 95260e9a8Yuri Pankov const char *, const char *, 96260e9a8Yuri Pankov struct manpage **, size_t *); 97c66b804Yuri Pankovstatic int fs_search(const struct mansearch *, 98260e9a8Yuri Pankov const struct manpaths *, int, char**, 99260e9a8Yuri Pankov struct manpage **, size_t *); 100260e9a8Yuri Pankovstatic int koptions(int *, char *); 101c66b804Yuri Pankovstatic void moptions(int *, char *); 102a593473Yuri Pankovstatic void outdata_alloc(struct curparse *); 103371584cYuri Pankovstatic void parse(struct curparse *, int, const char *); 104371584cYuri Pankovstatic void passthrough(const char *, int, int); 105371584cYuri Pankovstatic pid_t spawn_pager(struct tag_files *); 10695c635eGarrett D'Amorestatic int toptions(struct curparse *, char *); 107a40ea1aYuri Pankovstatic void usage(enum argmode) __attribute__((__noreturn__)); 10895c635eGarrett D'Amorestatic int woptions(struct curparse *, char *); 10995c635eGarrett D'Amore 110260e9a8Yuri Pankovstatic const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; 111260e9a8Yuri Pankovstatic char help_arg[] = "help"; 112260e9a8Yuri Pankovstatic char *help_argv[] = {help_arg, NULL}; 11395c635eGarrett D'Amore 114260e9a8Yuri Pankov 11595c635eGarrett D'Amoreint 11695c635eGarrett D'Amoremain(int argc, char *argv[]) 11795c635eGarrett D'Amore{ 118371584cYuri Pankov struct manconf conf; 119260e9a8Yuri Pankov struct mansearch search; 120a40ea1aYuri Pankov struct curparse curp; 1216640c13Yuri Pankov struct winsize ws; 122371584cYuri Pankov struct tag_files *tag_files; 123260e9a8Yuri Pankov struct manpage *res, *resp; 124a40ea1aYuri Pankov const char *progname, *sec, *thisarg; 125a40ea1aYuri Pankov char *conf_file, *defpaths, *auxpaths; 126cec8643Michal Nowak char *oarg, *tagarg; 127a40ea1aYuri Pankov unsigned char *uc; 128371584cYuri Pankov size_t i, sz; 129371584cYuri Pankov int prio, best_prio; 130260e9a8Yuri Pankov enum outmode outmode; 1316640c13Yuri Pankov int fd, startdir; 132260e9a8Yuri Pankov int show_usage; 133260e9a8Yuri Pankov int options; 134371584cYuri Pankov int use_pager; 135371584cYuri Pankov int status, signum; 136260e9a8Yuri Pankov int c; 137371584cYuri Pankov pid_t pager_pid, tc_pgid, man_pgid, pid; 13895c635eGarrett D'Amore 139371584cYuri Pankov#if HAVE_PROGNAME 140371584cYuri Pankov progname = getprogname(); 141371584cYuri Pankov#else 142260e9a8Yuri Pankov if (argc < 1) 143371584cYuri Pankov progname = mandoc_strdup("mandoc"); 144260e9a8Yuri Pankov else if ((progname = strrchr(argv[0], '/')) == NULL) 14595c635eGarrett D'Amore progname = argv[0]; 14695c635eGarrett D'Amore else 14795c635eGarrett D'Amore ++progname; 148371584cYuri Pankov setprogname(progname); 149371584cYuri Pankov#endif 15095c635eGarrett D'Amore 151cec8643Michal Nowak mandoc_msg_setoutfile(stderr); 152371584cYuri Pankov if (strncmp(progname, "mandocdb", 8) == 0 || 153371584cYuri Pankov strcmp(progname, BINM_MAKEWHATIS) == 0) 154371584cYuri Pankov return mandocdb(argc, argv); 155371584cYuri Pankov 156371584cYuri Pankov#if HAVE_PLEDGE 157c66b804Yuri Pankov if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) 158371584cYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 159371584cYuri Pankov#endif 160371584cYuri Pankov 161371584cYuri Pankov#if HAVE_SANDBOX_INIT 162371584cYuri Pankov if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) 163371584cYuri Pankov errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); 164260e9a8Yuri Pankov#endif 165260e9a8Yuri Pankov 166260e9a8Yuri Pankov /* Search options. */ 16795c635eGarrett D'Amore 168371584cYuri Pankov memset(&conf, 0, sizeof(conf)); 169260e9a8Yuri Pankov conf_file = defpaths = NULL; 170260e9a8Yuri Pankov auxpaths = NULL; 171260e9a8Yuri Pankov 172260e9a8Yuri Pankov memset(&search, 0, sizeof(struct mansearch)); 173260e9a8Yuri Pankov search.outkey = "Nd"; 174a40ea1aYuri Pankov oarg = NULL; 175260e9a8Yuri Pankov 176260e9a8Yuri Pankov if (strcmp(progname, BINM_MAN) == 0) 177260e9a8Yuri Pankov search.argmode = ARG_NAME; 178260e9a8Yuri Pankov else if (strcmp(progname, BINM_APROPOS) == 0) 179260e9a8Yuri Pankov search.argmode = ARG_EXPR; 180260e9a8Yuri Pankov else if (strcmp(progname, BINM_WHATIS) == 0) 181260e9a8Yuri Pankov search.argmode = ARG_WORD; 182260e9a8Yuri Pankov else if (strncmp(progname, "help", 4) == 0) 183260e9a8Yuri Pankov search.argmode = ARG_NAME; 184260e9a8Yuri Pankov else 185260e9a8Yuri Pankov search.argmode = ARG_FILE; 186260e9a8Yuri Pankov 187260e9a8Yuri Pankov /* Parser and formatter options. */ 188260e9a8Yuri Pankov 189260e9a8Yuri Pankov memset(&curp, 0, sizeof(struct curparse)); 190260e9a8Yuri Pankov curp.outtype = OUTT_LOCALE; 191371584cYuri Pankov curp.outopts = &conf.output; 192260e9a8Yuri Pankov options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; 19395c635eGarrett D'Amore 194371584cYuri Pankov use_pager = 1; 195371584cYuri Pankov tag_files = NULL; 196260e9a8Yuri Pankov show_usage = 0; 197260e9a8Yuri Pankov outmode = OUTMODE_DEF; 198260e9a8Yuri Pankov 199c66b804Yuri Pankov while ((c = getopt(argc, argv, 200c66b804Yuri Pankov "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) { 201c66b804Yuri Pankov if (c == 'i' && search.argmode == ARG_EXPR) { 202c66b804Yuri Pankov optind--; 203c66b804Yuri Pankov break; 204c66b804Yuri Pankov } 20595c635eGarrett D'Amore switch (c) { 206260e9a8Yuri Pankov case 'a': 207260e9a8Yuri Pankov outmode = OUTMODE_ALL; 208260e9a8Yuri Pankov break; 209260e9a8Yuri Pankov case 'C': 210260e9a8Yuri Pankov conf_file = optarg; 211260e9a8Yuri Pankov break; 212260e9a8Yuri Pankov case 'c': 213371584cYuri Pankov use_pager = 0; 214260e9a8Yuri Pankov break; 215260e9a8Yuri Pankov case 'f': 216260e9a8Yuri Pankov search.argmode = ARG_WORD; 217260e9a8Yuri Pankov break; 218260e9a8Yuri Pankov case 'h': 219371584cYuri Pankov conf.output.synopsisonly = 1; 220371584cYuri Pankov use_pager = 0; 221260e9a8Yuri Pankov outmode = OUTMODE_ALL; 222260e9a8Yuri Pankov break; 223260e9a8Yuri Pankov case 'I': 224698f87aGarrett D'Amore if (strncmp(optarg, "os=", 3)) { 225371584cYuri Pankov warnx("-I %s: Bad argument", optarg); 226371584cYuri Pankov return (int)MANDOCLEVEL_BADARG; 227698f87aGarrett D'Amore } 228c66b804Yuri Pankov if (curp.os_s != NULL) { 229371584cYuri Pankov warnx("-I %s: Duplicate argument", optarg); 230371584cYuri Pankov return (int)MANDOCLEVEL_BADARG; 231698f87aGarrett D'Amore } 232c66b804Yuri Pankov curp.os_s = mandoc_strdup(optarg + 3); 233260e9a8Yuri Pankov break; 234260e9a8Yuri Pankov case 'K': 235260e9a8Yuri Pankov if ( ! koptions(&options, optarg)) 236371584cYuri Pankov return (int)MANDOCLEVEL_BADARG; 23795c635eGarrett D'Amore break; 238260e9a8Yuri Pankov case 'k': 239260e9a8Yuri Pankov search.argmode = ARG_EXPR; 240260e9a8Yuri Pankov break; 241260e9a8Yuri Pankov case 'l': 242260e9a8Yuri Pankov search.argmode = ARG_FILE; 243260e9a8Yuri Pankov outmode = OUTMODE_ALL; 244260e9a8Yuri Pankov break; 245260e9a8Yuri Pankov case 'M': 246260e9a8Yuri Pankov defpaths = optarg; 247260e9a8Yuri Pankov break; 248260e9a8Yuri Pankov case 'm': 249260e9a8Yuri Pankov auxpaths = optarg; 250260e9a8Yuri Pankov break; 251260e9a8Yuri Pankov case 'O': 252a40ea1aYuri Pankov oarg = optarg; 25395c635eGarrett D'Amore break; 254260e9a8Yuri Pankov case 'S': 255260e9a8Yuri Pankov search.arch = optarg; 256260e9a8Yuri Pankov break; 257260e9a8Yuri Pankov case 's': 258260e9a8Yuri Pankov search.sec = optarg; 259260e9a8Yuri Pankov break; 260260e9a8Yuri Pankov case 'T': 26195c635eGarrett D'Amore if ( ! toptions(&curp, optarg)) 262371584cYuri Pankov return (int)MANDOCLEVEL_BADARG; 26395c635eGarrett D'Amore break; 264260e9a8Yuri Pankov case 'W': 26595c635eGarrett D'Amore if ( ! woptions(&curp, optarg)) 266371584cYuri Pankov return (int)MANDOCLEVEL_BADARG; 26795c635eGarrett D'Amore break; 268260e9a8Yuri Pankov case 'w': 269260e9a8Yuri Pankov outmode = OUTMODE_FLN; 270260e9a8Yuri Pankov break; 27195c635eGarrett D'Amore default: 272260e9a8Yuri Pankov show_usage = 1; 273260e9a8Yuri Pankov break; 27495c635eGarrett D'Amore } 275260e9a8Yuri Pankov } 276260e9a8Yuri Pankov 277260e9a8Yuri Pankov if (show_usage) 278260e9a8Yuri Pankov usage(search.argmode); 27995c635eGarrett D'Amore 280260e9a8Yuri Pankov /* Postprocess options. */ 281260e9a8Yuri Pankov 282260e9a8Yuri Pankov if (outmode == OUTMODE_DEF) { 283260e9a8Yuri Pankov switch (search.argmode) { 284260e9a8Yuri Pankov case ARG_FILE: 285260e9a8Yuri Pankov outmode = OUTMODE_ALL; 286371584cYuri Pankov use_pager = 0; 287260e9a8Yuri Pankov break; 288260e9a8Yuri Pankov case ARG_NAME: 289260e9a8Yuri Pankov outmode = OUTMODE_ONE; 290260e9a8Yuri Pankov break; 291260e9a8Yuri Pankov default: 292260e9a8Yuri Pankov outmode = OUTMODE_LST; 293260e9a8Yuri Pankov break; 294260e9a8Yuri Pankov } 295260e9a8Yuri Pankov } 296260e9a8Yuri Pankov 297a40ea1aYuri Pankov if (oarg != NULL) { 298a40ea1aYuri Pankov if (outmode == OUTMODE_LST) 299a40ea1aYuri Pankov search.outkey = oarg; 300a40ea1aYuri Pankov else { 301a40ea1aYuri Pankov while (oarg != NULL) { 302a40ea1aYuri Pankov thisarg = oarg; 303a40ea1aYuri Pankov if (manconf_output(&conf.output, 304a40ea1aYuri Pankov strsep(&oarg, ","), 0) == 0) 305a40ea1aYuri Pankov continue; 306a40ea1aYuri Pankov warnx("-O %s: Bad argument", thisarg); 307a40ea1aYuri Pankov return (int)MANDOCLEVEL_BADARG; 308a40ea1aYuri Pankov } 309a40ea1aYuri Pankov } 310a40ea1aYuri Pankov } 311a40ea1aYuri Pankov 312cec8643Michal Nowak if (curp.outtype != OUTT_TREE || !curp.outopts->noval) 313cec8643Michal Nowak options |= MPARSE_VALIDATE; 314cec8643Michal Nowak 315371584cYuri Pankov if (outmode == OUTMODE_FLN || 316371584cYuri Pankov outmode == OUTMODE_LST || 317371584cYuri Pankov !isatty(STDOUT_FILENO)) 318371584cYuri Pankov use_pager = 0; 319371584cYuri Pankov 3206640c13Yuri Pankov if (use_pager && 3216640c13Yuri Pankov (conf.output.width == 0 || conf.output.indent == 0) && 3226640c13Yuri Pankov ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && 3236640c13Yuri Pankov ws.ws_col > 1) { 3246640c13Yuri Pankov if (conf.output.width == 0 && ws.ws_col < 79) 3256640c13Yuri Pankov conf.output.width = ws.ws_col - 1; 3266640c13Yuri Pankov if (conf.output.indent == 0 && ws.ws_col < 66) 3276640c13Yuri Pankov conf.output.indent = 3; 3286640c13Yuri Pankov } 3296640c13Yuri Pankov 330371584cYuri Pankov#if HAVE_PLEDGE 331371584cYuri Pankov if (!use_pager) 332c66b804Yuri Pankov if (pledge("stdio rpath", NULL) == -1) 333371584cYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 334371584cYuri Pankov#endif 335371584cYuri Pankov 336260e9a8Yuri Pankov /* Parse arguments. */ 337260e9a8Yuri Pankov 338260e9a8Yuri Pankov if (argc > 0) { 339260e9a8Yuri Pankov argc -= optind; 340260e9a8Yuri Pankov argv += optind; 341260e9a8Yuri Pankov } 342260e9a8Yuri Pankov resp = NULL; 343260e9a8Yuri Pankov 344260e9a8Yuri Pankov /* 345260e9a8Yuri Pankov * Quirks for help(1) 346260e9a8Yuri Pankov * and for a man(1) section argument without -s. 347260e9a8Yuri Pankov */ 348260e9a8Yuri Pankov 349260e9a8Yuri Pankov if (search.argmode == ARG_NAME) { 350260e9a8Yuri Pankov if (*progname == 'h') { 351260e9a8Yuri Pankov if (argc == 0) { 352260e9a8Yuri Pankov argv = help_argv; 353260e9a8Yuri Pankov argc = 1; 354260e9a8Yuri Pankov } 355260e9a8Yuri Pankov } else if (argc > 1 && 356260e9a8Yuri Pankov ((uc = (unsigned char *)argv[0]) != NULL) && 357260e9a8Yuri Pankov ((isdigit(uc[0]) && (uc[1] == '\0' || 358260e9a8Yuri Pankov (isalpha(uc[1]) && uc[2] == '\0'))) || 359260e9a8Yuri Pankov (uc[0] == 'n' && uc[1] == '\0'))) { 360260e9a8Yuri Pankov search.sec = (char *)uc; 361260e9a8Yuri Pankov argv++; 362260e9a8Yuri Pankov argc--; 363260e9a8Yuri Pankov } 364260e9a8Yuri Pankov if (search.arch == NULL) 365260e9a8Yuri Pankov search.arch = getenv("MACHINE"); 366260e9a8Yuri Pankov#ifdef MACHINE 367260e9a8Yuri Pankov if (search.arch == NULL) 368260e9a8Yuri Pankov search.arch = MACHINE; 369260e9a8Yuri Pankov#endif 370260e9a8Yuri Pankov } 371260e9a8Yuri Pankov 372cec8643Michal Nowak /* 373cec8643Michal Nowak * Use the first argument for -O tag in addition to 374cec8643Michal Nowak * using it as a search term for man(1) or apropos(1). 375cec8643Michal Nowak */ 376cec8643Michal Nowak 377cec8643Michal Nowak if (conf.output.tag != NULL && *conf.output.tag == '\0') { 378cec8643Michal Nowak tagarg = argc > 0 && search.argmode == ARG_EXPR ? 379cec8643Michal Nowak strchr(*argv, '=') : NULL; 380cec8643Michal Nowak conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; 381cec8643Michal Nowak } 382260e9a8Yuri Pankov 383260e9a8Yuri Pankov /* man(1), whatis(1), apropos(1) */ 384260e9a8Yuri Pankov 385260e9a8Yuri Pankov if (search.argmode != ARG_FILE) { 386260e9a8Yuri Pankov if (search.argmode == ARG_NAME && 387260e9a8Yuri Pankov outmode == OUTMODE_ONE) 388260e9a8Yuri Pankov search.firstmatch = 1; 389260e9a8Yuri Pankov 390260e9a8Yuri Pankov /* Access the mandoc database. */ 391260e9a8Yuri Pankov 392371584cYuri Pankov manconf_parse(&conf, conf_file, defpaths, auxpaths); 393371584cYuri Pankov if ( ! mansearch(&search, &conf.manpath, 394371584cYuri Pankov argc, argv, &res, &sz)) 395260e9a8Yuri Pankov usage(search.argmode); 396260e9a8Yuri Pankov 3976640c13Yuri Pankov if (sz == 0 && search.argmode == ARG_NAME) 3986640c13Yuri Pankov fs_search(&search, &conf.manpath, 3996640c13Yuri Pankov argc, argv, &res, &sz); 4006640c13Yuri Pankov 4016640c13Yuri Pankov if (search.argmode == ARG_NAME) { 4026640c13Yuri Pankov for (c = 0; c < argc; c++) { 4036640c13Yuri Pankov if (strchr(argv[c], '/') == NULL) 4046640c13Yuri Pankov continue; 4056640c13Yuri Pankov if (access(argv[c], R_OK) == -1) { 4066640c13Yuri Pankov warn("%s", argv[c]); 4076640c13Yuri Pankov continue; 4086640c13Yuri Pankov } 4096640c13Yuri Pankov res = mandoc_reallocarray(res, 4106640c13Yuri Pankov sz + 1, sizeof(*res)); 4116640c13Yuri Pankov res[sz].file = mandoc_strdup(argv[c]); 4126640c13Yuri Pankov res[sz].names = NULL; 4136640c13Yuri Pankov res[sz].output = NULL; 4146640c13Yuri Pankov res[sz].ipath = SIZE_MAX; 4156640c13Yuri Pankov res[sz].sec = 10; 4166640c13Yuri Pankov res[sz].form = FORM_SRC; 4176640c13Yuri Pankov sz++; 4186640c13Yuri Pankov } 419371584cYuri Pankov } 420260e9a8Yuri Pankov 421260e9a8Yuri Pankov if (sz == 0) { 4226640c13Yuri Pankov if (search.argmode != ARG_NAME) 4236640c13Yuri Pankov warnx("nothing appropriate"); 424cec8643Michal Nowak mandoc_msg_setrc(MANDOCLEVEL_BADARG); 425260e9a8Yuri Pankov goto out; 426260e9a8Yuri Pankov } 427260e9a8Yuri Pankov 428260e9a8Yuri Pankov /* 429260e9a8Yuri Pankov * For standard man(1) and -a output mode, 430260e9a8Yuri Pankov * prepare for copying filename pointers 431260e9a8Yuri Pankov * into the program parameter array. 432260e9a8Yuri Pankov */ 433260e9a8Yuri Pankov 434260e9a8Yuri Pankov if (outmode == OUTMODE_ONE) { 435260e9a8Yuri Pankov argc = 1; 436371584cYuri Pankov best_prio = 20; 437260e9a8Yuri Pankov } else if (outmode == OUTMODE_ALL) 438260e9a8Yuri Pankov argc = (int)sz; 439260e9a8Yuri Pankov 440260e9a8Yuri Pankov /* Iterate all matching manuals. */ 441260e9a8Yuri Pankov 442260e9a8Yuri Pankov resp = res; 443260e9a8Yuri Pankov for (i = 0; i < sz; i++) { 444260e9a8Yuri Pankov if (outmode == OUTMODE_FLN) 445260e9a8Yuri Pankov puts(res[i].file); 446260e9a8Yuri Pankov else if (outmode == OUTMODE_LST) 447260e9a8Yuri Pankov printf("%s - %s\n", res[i].names, 448260e9a8Yuri Pankov res[i].output == NULL ? "" : 449260e9a8Yuri Pankov res[i].output); 450260e9a8Yuri Pankov else if (outmode == OUTMODE_ONE) { 451260e9a8Yuri Pankov /* Search for the best section. */ 452371584cYuri Pankov sec = res[i].file; 453371584cYuri Pankov sec += strcspn(sec, "123456789"); 454371584cYuri Pankov if (sec[0] == '\0') 455260e9a8Yuri Pankov continue; 456371584cYuri Pankov prio = sec_prios[sec[0] - '1']; 457371584cYuri Pankov if (sec[1] != '/') 458371584cYuri Pankov prio += 10; 459260e9a8Yuri Pankov if (prio >= best_prio) 460260e9a8Yuri Pankov continue; 461260e9a8Yuri Pankov best_prio = prio; 462260e9a8Yuri Pankov resp = res + i; 463260e9a8Yuri Pankov } 464260e9a8Yuri Pankov } 465260e9a8Yuri Pankov 466260e9a8Yuri Pankov /* 467260e9a8Yuri Pankov * For man(1), -a and -i output mode, fall through 468260e9a8Yuri Pankov * to the main mandoc(1) code iterating files 469260e9a8Yuri Pankov * and running the parsers on each of them. 470260e9a8Yuri Pankov */ 471260e9a8Yuri Pankov 472260e9a8Yuri Pankov if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) 473260e9a8Yuri Pankov goto out; 474260e9a8Yuri Pankov } 475260e9a8Yuri Pankov 476260e9a8Yuri Pankov /* mandoc(1) */ 477260e9a8Yuri Pankov 478371584cYuri Pankov#if HAVE_PLEDGE 479371584cYuri Pankov if (use_pager) { 480371584cYuri Pankov if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) 481371584cYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 482371584cYuri Pankov } else { 483371584cYuri Pankov if (pledge("stdio rpath", NULL) == -1) 484371584cYuri Pankov err((int)MANDOCLEVEL_SYSERR, "pledge"); 485371584cYuri Pankov } 486371584cYuri Pankov#endif 487371584cYuri Pankov 488c66b804Yuri Pankov if (search.argmode == ARG_FILE) 489c66b804Yuri Pankov moptions(&options, auxpaths); 490260e9a8Yuri Pankov 491371584cYuri Pankov mchars_alloc(); 492cec8643Michal Nowak curp.mp = mparse_alloc(options, curp.os_e, curp.os_s); 49395c635eGarrett D'Amore 494260e9a8Yuri Pankov if (argc < 1) { 495cec8643Michal Nowak if (use_pager) { 496371584cYuri Pankov tag_files = tag_init(); 497cec8643Michal Nowak tag_files->tagname = conf.output.tag; 498cec8643Michal Nowak } 499cec8643Michal Nowak thisarg = "<stdin>"; 500cec8643Michal Nowak mandoc_msg_setinfilename(thisarg); 501cec8643Michal Nowak parse(&curp, STDIN_FILENO, thisarg); 502cec8643Michal Nowak mandoc_msg_setinfilename(NULL); 503260e9a8Yuri Pankov } 50495c635eGarrett D'Amore 5056640c13Yuri Pankov /* 5066640c13Yuri Pankov * Remember the original working directory, if possible. 5076640c13Yuri Pankov * This will be needed if some names on the command line 5086640c13Yuri Pankov * are page names and some are relative file names. 5096640c13Yuri Pankov * Do not error out if the current directory is not 5106640c13Yuri Pankov * readable: Maybe it won't be needed after all. 5116640c13Yuri Pankov */ 5126640c13Yuri Pankov startdir = open(".", O_RDONLY | O_DIRECTORY); 5136640c13Yuri Pankov 514260e9a8Yuri Pankov while (argc > 0) { 5156640c13Yuri Pankov 5166640c13Yuri Pankov /* 5176640c13Yuri Pankov * Changing directories is not needed in ARG_FILE mode. 5186640c13Yuri Pankov * Do it on a best-effort basis. Even in case of 5196640c13Yuri Pankov * failure, some functionality may still work. 5206640c13Yuri Pankov */ 5216640c13Yuri Pankov if (resp != NULL) { 5226640c13Yuri Pankov if (resp->ipath != SIZE_MAX) 5236640c13Yuri Pankov (void)chdir(conf.manpath.paths[resp->ipath]); 5246640c13Yuri Pankov else if (startdir != -1) 5256640c13Yuri Pankov (void)fchdir(startdir); 526cec8643Michal Nowak thisarg = resp->file; 527cec8643Michal Nowak } else 528cec8643Michal Nowak thisarg = *argv; 5296640c13Yuri Pankov 530cec8643Michal Nowak fd = mparse_open(curp.mp, thisarg); 531260e9a8Yuri Pankov if (fd != -1) { 532371584cYuri Pankov if (use_pager) { 533371584cYuri Pankov use_pager = 0; 534cec8643Michal Nowak tag_files = tag_init(); 535cec8643Michal Nowak tag_files->tagname = conf.output.tag; 536371584cYuri Pankov } 537260e9a8Yuri Pankov 538cec8643Michal Nowak mandoc_msg_setinfilename(thisarg); 539cec8643Michal Nowak if (resp == NULL || resp->form == FORM_SRC) 540cec8643Michal Nowak parse(&curp, fd, thisarg); 5416640c13Yuri Pankov else 542371584cYuri Pankov passthrough(resp->file, fd, 543371584cYuri Pankov conf.output.synopsisonly); 544cec8643Michal Nowak mandoc_msg_setinfilename(NULL); 545260e9a8Yuri Pankov 5466640c13Yuri Pankov if (ferror(stdout)) { 5476640c13Yuri Pankov if (tag_files != NULL) { 5486640c13Yuri Pankov warn("%s", tag_files->ofn); 5496640c13Yuri Pankov tag_unlink(); 5506640c13Yuri Pankov tag_files = NULL; 5516640c13Yuri Pankov } else 5526640c13Yuri Pankov warn("stdout"); 553cec8643Michal Nowak mandoc_msg_setrc(MANDOCLEVEL_SYSERR); 5546640c13Yuri Pankov break; 5556640c13Yuri Pankov } 5566640c13Yuri Pankov 557a593473Yuri Pankov if (argc > 1 && curp.outtype <= OUTT_UTF8) { 558a593473Yuri Pankov if (curp.outdata == NULL) 559a593473Yuri Pankov outdata_alloc(&curp); 560371584cYuri Pankov terminal_sepline(curp.outdata); 561a593473Yuri Pankov } 562cec8643Michal Nowak } else 563cec8643Michal Nowak mandoc_msg(MANDOCERR_FILE, 0, 0, 564cec8643Michal Nowak "%s: %s", thisarg, strerror(errno)); 56595c635eGarrett D'Amore 566cec8643Michal Nowak if (curp.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) 56795c635eGarrett D'Amore break; 568260e9a8Yuri Pankov 569260e9a8Yuri Pankov if (resp != NULL) 570260e9a8Yuri Pankov resp++; 571260e9a8Yuri Pankov else 572260e9a8Yuri Pankov argv++; 573260e9a8Yuri Pankov if (--argc) 574260e9a8Yuri Pankov mparse_reset(curp.mp); 57595c635eGarrett D'Amore } 5766640c13Yuri Pankov if (startdir != -1) { 5776640c13Yuri Pankov (void)fchdir(startdir); 5786640c13Yuri Pankov close(startdir); 5796640c13Yuri Pankov } 58095c635eGarrett D'Amore 581371584cYuri Pankov if (curp.outdata != NULL) { 582371584cYuri Pankov switch (curp.outtype) { 583371584cYuri Pankov case OUTT_HTML: 584371584cYuri Pankov html_free(curp.outdata); 585371584cYuri Pankov break; 586371584cYuri Pankov case OUTT_UTF8: 587371584cYuri Pankov case OUTT_LOCALE: 588371584cYuri Pankov case OUTT_ASCII: 589371584cYuri Pankov ascii_free(curp.outdata); 590371584cYuri Pankov break; 591371584cYuri Pankov case OUTT_PDF: 592371584cYuri Pankov case OUTT_PS: 593371584cYuri Pankov pspdf_free(curp.outdata); 594371584cYuri Pankov break; 595371584cYuri Pankov default: 596371584cYuri Pankov break; 597371584cYuri Pankov } 598371584cYuri Pankov } 599c66b804Yuri Pankov mandoc_xr_free(); 600260e9a8Yuri Pankov mparse_free(curp.mp); 601371584cYuri Pankov mchars_free(); 602260e9a8Yuri Pankov 603260e9a8Yuri Pankovout: 604260e9a8Yuri Pankov if (search.argmode != ARG_FILE) { 605371584cYuri Pankov manconf_free(&conf); 606260e9a8Yuri Pankov mansearch_free(res, sz); 607260e9a8Yuri Pankov } 608260e9a8Yuri Pankov 609c66b804Yuri Pankov free(curp.os_s); 61095c635eGarrett D'Amore 611260e9a8Yuri Pankov /* 612371584cYuri Pankov * When using a pager, finish writing both temporary files, 613371584cYuri Pankov * fork it, wait for the user to close it, and clean up. 614260e9a8Yuri Pankov */ 615260e9a8Yuri Pankov 616371584cYuri Pankov if (tag_files != NULL) { 617260e9a8Yuri Pankov fclose(stdout); 618371584cYuri Pankov tag_write(); 619371584cYuri Pankov man_pgid = getpgid(0); 620371584cYuri Pankov tag_files->tcpgid = man_pgid == getpid() ? 621371584cYuri Pankov getpgid(getppid()) : man_pgid; 622371584cYuri Pankov pager_pid = 0; 623371584cYuri Pankov signum = SIGSTOP; 624371584cYuri Pankov for (;;) { 625371584cYuri Pankov 626371584cYuri Pankov /* Stop here until moved to the foreground. */ 627371584cYuri Pankov 628a40ea1aYuri Pankov tc_pgid = tcgetpgrp(tag_files->ofd); 629371584cYuri Pankov if (tc_pgid != man_pgid) { 630371584cYuri Pankov if (tc_pgid == pager_pid) { 631a40ea1aYuri Pankov (void)tcsetpgrp(tag_files->ofd, 632371584cYuri Pankov man_pgid); 633371584cYuri Pankov if (signum == SIGTTIN) 634371584cYuri Pankov continue; 635371584cYuri Pankov } else 636371584cYuri Pankov tag_files->tcpgid = tc_pgid; 637371584cYuri Pankov kill(0, signum); 638371584cYuri Pankov continue; 639371584cYuri Pankov } 640371584cYuri Pankov 641371584cYuri Pankov /* Once in the foreground, activate the pager. */ 642371584cYuri Pankov 643371584cYuri Pankov if (pager_pid) { 644a40ea1aYuri Pankov (void)tcsetpgrp(tag_files->ofd, pager_pid); 645371584cYuri Pankov kill(pager_pid, SIGCONT); 646371584cYuri Pankov } else 647371584cYuri Pankov pager_pid = spawn_pager(tag_files); 648371584cYuri Pankov 649371584cYuri Pankov /* Wait for the pager to stop or exit. */ 650371584cYuri Pankov 651371584cYuri Pankov while ((pid = waitpid(pager_pid, &status, 652371584cYuri Pankov WUNTRACED)) == -1 && errno == EINTR) 653371584cYuri Pankov continue; 654371584cYuri Pankov 655371584cYuri Pankov if (pid == -1) { 656371584cYuri Pankov warn("wait"); 657cec8643Michal Nowak mandoc_msg_setrc(MANDOCLEVEL_SYSERR); 658371584cYuri Pankov break; 659371584cYuri Pankov } 660371584cYuri Pankov if (!WIFSTOPPED(status)) 661371584cYuri Pankov break; 662371584cYuri Pankov 663371584cYuri Pankov signum = WSTOPSIG(status); 664371584cYuri Pankov } 665371584cYuri Pankov tag_unlink(); 666260e9a8Yuri Pankov } 667cec8643Michal Nowak return (int)mandoc_msg_getrc(); 66895c635eGarrett D'Amore} 66995c635eGarrett D'Amore 67095c635eGarrett D'Amorestatic void 671260e9a8Yuri Pankovusage(enum argmode argmode) 67295c635eGarrett D'Amore{ 67395c635eGarrett D'Amore 674260e9a8Yuri Pankov switch (argmode) { 675260e9a8Yuri Pankov case ARG_FILE: 676c66b804Yuri Pankov fputs("usage: mandoc [-ac] [-I os=name] " 677c66b804Yuri Pankov "[-K encoding] [-mdoc | -man] [-O options]\n" 678371584cYuri Pankov "\t [-T output] [-W level] [file ...]\n", stderr); 679260e9a8Yuri Pankov break; 680260e9a8Yuri Pankov case ARG_NAME: 681c66b804Yuri Pankov fputs("usage: man [-acfhklw] [-C file] [-M path] " 682c66b804Yuri Pankov "[-m path] [-S subsection]\n" 683c66b804Yuri Pankov "\t [[-s] section] name ...\n", stderr); 684260e9a8Yuri Pankov break; 685260e9a8Yuri Pankov case ARG_WORD: 686c66b804Yuri Pankov fputs("usage: whatis [-afk] [-C file] " 687260e9a8Yuri Pankov "[-M path] [-m path] [-O outkey] [-S arch]\n" 688260e9a8Yuri Pankov "\t [-s section] name ...\n", stderr); 689260e9a8Yuri Pankov break; 690260e9a8Yuri Pankov case ARG_EXPR: 691c66b804Yuri Pankov fputs("usage: apropos [-afk] [-C file] " 692260e9a8Yuri Pankov "[-M path] [-m path] [-O outkey] [-S arch]\n" 693260e9a8Yuri Pankov "\t [-s section] expression ...\n", stderr); 694260e9a8Yuri Pankov break; 695260e9a8Yuri Pankov } 696260e9a8Yuri Pankov exit((int)MANDOCLEVEL_BADARG); 69795c635eGarrett D'Amore} 69895c635eGarrett D'Amore 699260e9a8Yuri Pankovstatic int 700260e9a8Yuri Pankovfs_lookup(const struct manpaths *paths, size_t ipath, 701260e9a8Yuri Pankov const char *sec, const char *arch, const char *name, 702260e9a8Yuri Pankov struct manpage **res, size_t *ressz) 70395c635eGarrett D'Amore{ 704260e9a8Yuri Pankov glob_t globinfo; 705260e9a8Yuri Pankov struct manpage *page; 706260e9a8Yuri Pankov char *file; 707a40ea1aYuri Pankov int globres; 708a40ea1aYuri Pankov enum form form; 709260e9a8Yuri Pankov 710260e9a8Yuri Pankov form = FORM_SRC; 711260e9a8Yuri Pankov mandoc_asprintf(&file, "%s/man%s/%s.%s", 712260e9a8Yuri Pankov paths->paths[ipath], sec, name, sec); 713260e9a8Yuri Pankov if (access(file, R_OK) != -1) 714260e9a8Yuri Pankov goto found; 715260e9a8Yuri Pankov free(file); 716260e9a8Yuri Pankov 717260e9a8Yuri Pankov mandoc_asprintf(&file, "%s/cat%s/%s.0", 718260e9a8Yuri Pankov paths->paths[ipath], sec, name); 719260e9a8Yuri Pankov if (access(file, R_OK) != -1) { 720260e9a8Yuri Pankov form = FORM_CAT; 721260e9a8Yuri Pankov goto found; 722260e9a8Yuri Pankov } 723260e9a8Yuri Pankov free(file); 724260e9a8Yuri Pankov 725260e9a8Yuri Pankov if (arch != NULL) { 726260e9a8Yuri Pankov mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", 727260e9a8Yuri Pankov paths->paths[ipath], sec, arch, name, sec); 728260e9a8Yuri Pankov if (access(file, R_OK) != -1) 729260e9a8Yuri Pankov goto found; 730260e9a8Yuri Pankov free(file); 731260e9a8Yuri Pankov } 732260e9a8Yuri Pankov 733371584cYuri Pankov mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", 734260e9a8Yuri Pankov paths->paths[ipath], sec, name); 735260e9a8Yuri Pankov globres = glob(file, 0, NULL, &globinfo); 736260e9a8Yuri Pankov if (globres != 0 && globres != GLOB_NOMATCH) 737371584cYuri Pankov warn("%s: glob", file); 738260e9a8Yuri Pankov free(file); 739260e9a8Yuri Pankov if (globres == 0) 740260e9a8Yuri Pankov file = mandoc_strdup(*globinfo.gl_pathv); 741260e9a8Yuri Pankov globfree(&globinfo); 742c66b804Yuri Pankov if (globres == 0) 743c66b804Yuri Pankov goto found; 744c66b804Yuri Pankov if (res != NULL || ipath + 1 != paths->sz) 745371584cYuri Pankov return 0; 74695c635eGarrett D'Amore 747c66b804Yuri Pankov mandoc_asprintf(&file, "%s.%s", name, sec); 748c66b804Yuri Pankov globres = access(file, R_OK); 749c66b804Yuri Pankov free(file); 750c66b804Yuri Pankov return globres != -1; 751c66b804Yuri Pankov 752260e9a8Yuri Pankovfound: 753371584cYuri Pankov warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", 754371584cYuri Pankov name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); 755c66b804Yuri Pankov if (res == NULL) { 756c66b804Yuri Pankov free(file); 757c66b804Yuri Pankov return 1; 758c66b804Yuri Pankov } 759260e9a8Yuri Pankov *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); 760260e9a8Yuri Pankov page = *res + (*ressz - 1); 761260e9a8Yuri Pankov page->file = file; 762260e9a8Yuri Pankov page->names = NULL; 763260e9a8Yuri Pankov page->output = NULL; 764260e9a8Yuri Pankov page->ipath = ipath; 765260e9a8Yuri Pankov page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; 766260e9a8Yuri Pankov page->form = form; 767371584cYuri Pankov return 1; 768260e9a8Yuri Pankov} 769260e9a8Yuri Pankov 770c66b804Yuri Pankovstatic int 771260e9a8Yuri Pankovfs_search(const struct mansearch *cfg, const struct manpaths *paths, 772260e9a8Yuri Pankov int argc, char **argv, struct manpage **res, size_t *ressz) 773260e9a8Yuri Pankov{ 774260e9a8Yuri Pankov const char *const sections[] = 775371584cYuri Pankov {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; 776260e9a8Yuri Pankov const size_t nsec = sizeof(sections)/sizeof(sections[0]); 777260e9a8Yuri Pankov 778260e9a8Yuri Pankov size_t ipath, isec, lastsz; 779260e9a8Yuri Pankov 780260e9a8Yuri Pankov assert(cfg->argmode == ARG_NAME); 781260e9a8Yuri Pankov 782c66b804Yuri Pankov if (res != NULL) 783c66b804Yuri Pankov *res = NULL; 784260e9a8Yuri Pankov *ressz = lastsz = 0; 785260e9a8Yuri Pankov while (argc) { 786260e9a8Yuri Pankov for (ipath = 0; ipath < paths->sz; ipath++) { 787260e9a8Yuri Pankov if (cfg->sec != NULL) { 788260e9a8Yuri Pankov if (fs_lookup(paths, ipath, cfg->sec, 789260e9a8Yuri Pankov cfg->arch, *argv, res, ressz) && 790260e9a8Yuri Pankov cfg->firstmatch) 791c66b804Yuri Pankov return 1; 792260e9a8Yuri Pankov } else for (isec = 0; isec < nsec; isec++) 793260e9a8Yuri Pankov if (fs_lookup(paths, ipath, sections[isec], 794260e9a8Yuri Pankov cfg->arch, *argv, res, ressz) && 795260e9a8Yuri Pankov cfg->firstmatch) 796c66b804Yuri Pankov return 1; 797260e9a8Yuri Pankov } 7986640c13Yuri Pankov if (res != NULL && *ressz == lastsz && 799cec8643Michal Nowak strchr(*argv, '/') == NULL) { 800cec8643Michal Nowak if (cfg->arch != NULL && 801cec8643Michal Nowak arch_valid(cfg->arch, OSENUM) == 0) 802cec8643Michal Nowak warnx("Unknown architecture \"%s\".", 803cec8643Michal Nowak cfg->arch); 804cec8643Michal Nowak else if (cfg->sec == NULL) 805cec8643Michal Nowak warnx("No entry for %s in the manual.", 806cec8643Michal Nowak *argv); 807cec8643Michal Nowak else 808cec8643Michal Nowak warnx("No entry for %s in section %s " 809cec8643Michal Nowak "of the manual.", *argv, cfg->sec); 810cec8643Michal Nowak } 811260e9a8Yuri Pankov lastsz = *ressz; 812260e9a8Yuri Pankov argv++; 813260e9a8Yuri Pankov argc--; 814260e9a8Yuri Pankov } 815c66b804Yuri Pankov return 0; 81695c635eGarrett D'Amore} 81795c635eGarrett D'Amore 81895c635eGarrett D'Amorestatic void 819371584cYuri Pankovparse(struct curparse *curp, int fd, const char *file) 82095c635eGarrett D'Amore{ 821cec8643Michal Nowak struct roff_meta *meta; 82295c635eGarrett D'Amore 82395c635eGarrett D'Amore /* Begin by parsing the file itself. */ 82495c635eGarrett D'Amore 82595c635eGarrett D'Amore assert(file); 826371584cYuri Pankov assert(fd >= 0); 82795c635eGarrett D'Amore 828cec8643Michal Nowak mparse_readfd(curp->mp, fd, file); 829371584cYuri Pankov if (fd != STDIN_FILENO) 830371584cYuri Pankov close(fd); 83195c635eGarrett D'Amore 83295c635eGarrett D'Amore /* 83395c635eGarrett D'Amore * With -Wstop and warnings or errors of at least the requested 83495c635eGarrett D'Amore * level, do not produce output. 83595c635eGarrett D'Amore */ 83695c635eGarrett D'Amore 837cec8643Michal Nowak if (curp->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) 838371584cYuri Pankov return; 83995c635eGarrett D'Amore 840a593473Yuri Pankov if (curp->outdata == NULL) 841a593473Yuri Pankov outdata_alloc(curp); 842cec8643Michal Nowak else if (curp->outtype == OUTT_HTML) 843cec8643Michal Nowak html_reset(curp); 844371584cYuri Pankov 845cec8643Michal Nowak mandoc_xr_reset(); 846cec8643Michal Nowak meta = mparse_result(curp->mp); 84795c635eGarrett D'Amore 848371584cYuri Pankov /* Execute the out device, if it exists. */ 849371584cYuri Pankov 850cec8643Michal Nowak if (meta->macroset == MACROSET_MDOC) { 85195c635eGarrett D'Amore switch (curp->outtype) { 852260e9a8Yuri Pankov case OUTT_HTML: 853cec8643Michal Nowak html_mdoc(curp->outdata, meta); 85495c635eGarrett D'Amore break; 855260e9a8Yuri Pankov case OUTT_TREE: 856cec8643Michal Nowak tree_mdoc(curp->outdata, meta); 85795c635eGarrett D'Amore break; 858260e9a8Yuri Pankov case OUTT_MAN: 859cec8643Michal Nowak man_mdoc(curp->outdata, meta); 86095c635eGarrett D'Amore break; 861260e9a8Yuri Pankov case OUTT_PDF: 862260e9a8Yuri Pankov case OUTT_ASCII: 863260e9a8Yuri Pankov case OUTT_UTF8: 864260e9a8Yuri Pankov case OUTT_LOCALE: 865260e9a8Yuri Pankov case OUTT_PS: 866cec8643Michal Nowak terminal_mdoc(curp->outdata, meta); 867371584cYuri Pankov break; 868c66b804Yuri Pankov case OUTT_MARKDOWN: 869cec8643Michal Nowak markdown_mdoc(curp->outdata, meta); 870c66b804Yuri Pankov break; 871371584cYuri Pankov default: 872371584cYuri Pankov break; 873371584cYuri Pankov } 874371584cYuri Pankov } 875cec8643Michal Nowak if (meta->macroset == MACROSET_MAN) { 876371584cYuri Pankov switch (curp->outtype) { 877371584cYuri Pankov case OUTT_HTML: 878cec8643Michal Nowak html_man(curp->outdata, meta); 879371584cYuri Pankov break; 880371584cYuri Pankov case OUTT_TREE: 881cec8643Michal Nowak tree_man(curp->outdata, meta); 882371584cYuri Pankov break; 883371584cYuri Pankov case OUTT_MAN: 884cec8643Michal Nowak mparse_copy(curp->mp); 885371584cYuri Pankov break; 886371584cYuri Pankov case OUTT_PDF: 887371584cYuri Pankov case OUTT_ASCII: 888371584cYuri Pankov case OUTT_UTF8: 889371584cYuri Pankov case OUTT_LOCALE: 890371584cYuri Pankov case OUTT_PS: 891cec8643Michal Nowak terminal_man(curp->outdata, meta); 89295c635eGarrett D'Amore break; 89395c635eGarrett D'Amore default: 89495c635eGarrett D'Amore break; 89595c635eGarrett D'Amore } 89695c635eGarrett D'Amore } 897cec8643Michal Nowak if (mandoc_msg_getmin() < MANDOCERR_STYLE) 898cec8643Michal Nowak check_xr(); 899a593473Yuri Pankov} 900a593473Yuri Pankov 901a593473Yuri Pankovstatic void 902cec8643Michal Nowakcheck_xr(void) 903c66b804Yuri Pankov{ 904c66b804Yuri Pankov static struct manpaths paths; 905c66b804Yuri Pankov struct mansearch search; 906c66b804Yuri Pankov struct mandoc_xr *xr; 907c66b804Yuri Pankov size_t sz; 908c66b804Yuri Pankov 909c66b804Yuri Pankov if (paths.sz == 0) 910c66b804Yuri Pankov manpath_base(&paths); 911c66b804Yuri Pankov 912c66b804Yuri Pankov for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { 913c66b804Yuri Pankov if (xr->line == -1) 914c66b804Yuri Pankov continue; 915c66b804Yuri Pankov search.arch = NULL; 916c66b804Yuri Pankov search.sec = xr->sec; 917c66b804Yuri Pankov search.outkey = NULL; 918c66b804Yuri Pankov search.argmode = ARG_NAME; 919c66b804Yuri Pankov search.firstmatch = 1; 920c66b804Yuri Pankov if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz)) 921c66b804Yuri Pankov continue; 922c66b804Yuri Pankov if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz)) 923c66b804Yuri Pankov continue; 924c66b804Yuri Pankov if (xr->count == 1) 925cec8643Michal Nowak mandoc_msg(MANDOCERR_XR_BAD, xr->line, 926cec8643Michal Nowak xr->pos + 1, "Xr %s %s", xr->name, xr->sec); 927c66b804Yuri Pankov else 928cec8643Michal Nowak mandoc_msg(MANDOCERR_XR_BAD, xr->line, 929cec8643Michal Nowak xr->pos + 1, "Xr %s %s (%d times)", 930c66b804Yuri Pankov xr->name, xr->sec, xr->count); 931c66b804Yuri Pankov } 932c66b804Yuri Pankov} 933c66b804Yuri Pankov 934c66b804Yuri Pankovstatic void 935a593473Yuri Pankovoutdata_alloc(struct curparse *curp) 936a593473Yuri Pankov{ 937a593473Yuri Pankov switch (curp->outtype) { 938a593473Yuri Pankov case OUTT_HTML: 939a593473Yuri Pankov curp->outdata = html_alloc(curp->outopts); 940a593473Yuri Pankov break; 941a593473Yuri Pankov case OUTT_UTF8: 942a593473Yuri Pankov curp->outdata = utf8_alloc(curp->outopts); 943a593473Yuri Pankov break; 944a593473Yuri Pankov case OUTT_LOCALE: 945a593473Yuri Pankov curp->outdata = locale_alloc(curp->outopts); 946a593473Yuri Pankov break; 947a593473Yuri Pankov case OUTT_ASCII: 948a593473Yuri Pankov curp->outdata = ascii_alloc(curp->outopts); 949a593473Yuri Pankov break; 950a593473Yuri Pankov case OUTT_PDF: 951a593473Yuri Pankov curp->outdata = pdf_alloc(curp->outopts); 952a593473Yuri Pankov break; 953a593473Yuri Pankov case OUTT_PS: 954a593473Yuri Pankov curp->outdata = ps_alloc(curp->outopts); 955a593473Yuri Pankov break; 956a593473Yuri Pankov default: 957a593473Yuri Pankov break; 958a593473Yuri Pankov } 95995c635eGarrett D'Amore} 96095c635eGarrett D'Amore 961371584cYuri Pankovstatic void 962260e9a8Yuri Pankovpassthrough(const char *file, int fd, int synopsis_only) 963260e9a8Yuri Pankov{ 964260e9a8Yuri Pankov const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; 965260e9a8Yuri Pankov const char synr[] = "SYNOPSIS"; 966260e9a8Yuri Pankov 967260e9a8Yuri Pankov FILE *stream; 968260e9a8Yuri Pankov const char *syscall; 969371584cYuri Pankov char *line, *cp; 970371584cYuri Pankov size_t linesz; 971a593473Yuri Pankov ssize_t len, written; 972260e9a8Yuri Pankov int print; 973260e9a8Yuri Pankov 974371584cYuri Pankov line = NULL; 975371584cYuri Pankov linesz = 0; 976260e9a8Yuri Pankov 977a593473Yuri Pankov if (fflush(stdout) == EOF) { 978a593473Yuri Pankov syscall = "fflush"; 979a593473Yuri Pankov goto fail; 980a593473Yuri Pankov } 981a593473Yuri Pankov 982260e9a8Yuri Pankov if ((stream = fdopen(fd, "r")) == NULL) { 983260e9a8Yuri Pankov close(fd); 984260e9a8Yuri Pankov syscall = "fdopen"; 985260e9a8Yuri Pankov goto fail; 986260e9a8Yuri Pankov } 987260e9a8Yuri Pankov 988260e9a8Yuri Pankov print = 0; 989a593473Yuri Pankov while ((len = getline(&line, &linesz, stream)) != -1) { 990371584cYuri Pankov cp = line; 991260e9a8Yuri Pankov if (synopsis_only) { 992260e9a8Yuri Pankov if (print) { 993371584cYuri Pankov if ( ! isspace((unsigned char)*cp)) 994260e9a8Yuri Pankov goto done; 995a593473Yuri Pankov while (isspace((unsigned char)*cp)) { 996371584cYuri Pankov cp++; 997a593473Yuri Pankov len--; 998a593473Yuri Pankov } 999260e9a8Yuri Pankov } else { 1000371584cYuri Pankov if (strcmp(cp, synb) == 0 || 1001371584cYuri Pankov strcmp(cp, synr) == 0) 1002260e9a8Yuri Pankov print = 1; 1003260e9a8Yuri Pankov continue; 1004260e9a8Yuri Pankov } 1005260e9a8Yuri Pankov } 1006a593473Yuri Pankov for (; len > 0; len -= written) { 1007a593473Yuri Pankov if ((written = write(STDOUT_FILENO, cp, len)) != -1) 1008a593473Yuri Pankov continue; 1009371584cYuri Pankov fclose(stream); 1010a593473Yuri Pankov syscall = "write"; 1011371584cYuri Pankov goto fail; 1012371584cYuri Pankov } 1013260e9a8Yuri Pankov } 1014260e9a8Yuri Pankov 1015260e9a8Yuri Pankov if (ferror(stream)) { 1016260e9a8Yuri Pankov fclose(stream); 1017371584cYuri Pankov syscall = "getline"; 1018260e9a8Yuri Pankov goto fail; 1019260e9a8Yuri Pankov } 1020260e9a8Yuri Pankov 1021260e9a8Yuri Pankovdone: 1022371584cYuri Pankov free(line); 1023260e9a8Yuri Pankov fclose(stream); 1024371584cYuri Pankov return; 1025260e9a8Yuri Pankov 1026260e9a8Yuri Pankovfail: 1027371584cYuri Pankov free(line); 1028371584cYuri Pankov warn("%s: SYSERR: %s", file, syscall); 1029cec8643Michal Nowak mandoc_msg_setrc(MANDOCLEVEL_SYSERR); 1030260e9a8Yuri Pankov} 1031260e9a8Yuri Pankov 1032260e9a8Yuri Pankovstatic int 1033260e9a8Yuri Pankovkoptions(int *options, char *arg) 1034260e9a8Yuri Pankov{ 1035260e9a8Yuri Pankov 1036260e9a8Yuri Pankov if ( ! strcmp(arg, "utf-8")) { 1037260e9a8Yuri Pankov *options |= MPARSE_UTF8; 1038260e9a8Yuri Pankov *options &= ~MPARSE_LATIN1; 1039260e9a8Yuri Pankov } else if ( ! strcmp(arg, "iso-8859-1")) { 1040260e9a8Yuri Pankov *options |= MPARSE_LATIN1; 1041260e9a8Yuri Pankov *options &= ~MPARSE_UTF8; 1042260e9a8Yuri Pankov } else if ( ! strcmp(arg, "us-ascii")) { 1043260e9a8Yuri Pankov *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); 1044260e9a8Yuri Pankov } else { 1045371584cYuri Pankov warnx("-K %s: Bad argument", arg); 1046371584cYuri Pankov return 0; 1047260e9a8Yuri Pankov } 1048371584cYuri Pankov return 1; 1049260e9a8Yuri Pankov} 1050260e9a8Yuri Pankov 1051c66b804Yuri Pankovstatic void 1052260e9a8Yuri Pankovmoptions(int *options, char *arg) 105395c635eGarrett D'Amore{ 105495c635eGarrett D'Amore 1055260e9a8Yuri Pankov if (arg == NULL) 1056c66b804Yuri Pankov return; 1057c66b804Yuri Pankov if (strcmp(arg, "doc") == 0) 1058260e9a8Yuri Pankov *options |= MPARSE_MDOC; 1059c66b804Yuri Pankov else if (strcmp(arg, "an") == 0) 1060260e9a8Yuri Pankov *options |= MPARSE_MAN; 106195c635eGarrett D'Amore} 106295c635eGarrett D'Amore 106395c635eGarrett D'Amorestatic int 106495c635eGarrett D'Amoretoptions(struct curparse *curp, char *arg) 106595c635eGarrett D'Amore{ 106695c635eGarrett D'Amore 106795c635eGarrett D'Amore if (0 == strcmp(arg, "ascii")) 106895c635eGarrett D'Amore curp->outtype = OUTT_ASCII; 106995c635eGarrett D'Amore else if (0 == strcmp(arg, "lint")) { 107095c635eGarrett D'Amore curp->outtype = OUTT_LINT; 1071cec8643Michal Nowak mandoc_msg_setoutfile(stdout); 1072