[BACK]Return to main.c CVS log [TXT][DIR] Up to [cvsweb.bsd.lv] / mandoc

Diff for /mandoc/main.c between version 1.96 and 1.329

version 1.96, 2010/07/02 12:54:33 version 1.329, 2019/07/10 19:39:01
Line 1 
Line 1 
 /*      $Id$ */  /*      $Id$ */
 /*  /*
  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>   * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
    * Copyright (c) 2010-2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org>
    * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.   * copyright notice and this permission notice appear in all copies.
  *   *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF   * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR   * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES   * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */   */
 #ifdef HAVE_CONFIG_H  
 #include "config.h"  #include "config.h"
 #endif  
   
 #include <sys/mman.h>  #include <sys/types.h>
   #include <sys/ioctl.h>
   #include <sys/param.h>  /* MACHINE */
 #include <sys/stat.h>  #include <sys/stat.h>
   #include <sys/wait.h>
   
 #include <assert.h>  #include <assert.h>
   #include <ctype.h>
   #if HAVE_ERR
   #include <err.h>
   #endif
   #include <errno.h>
 #include <fcntl.h>  #include <fcntl.h>
   #include <glob.h>
   #if HAVE_SANDBOX_INIT
   #include <sandbox.h>
   #endif
   #include <signal.h>
 #include <stdio.h>  #include <stdio.h>
 #include <stdint.h>  #include <stdint.h>
 #include <stdlib.h>  #include <stdlib.h>
 #include <string.h>  #include <string.h>
   #include <termios.h>
   #include <time.h>
 #include <unistd.h>  #include <unistd.h>
   
   #include "mandoc_aux.h"
 #include "mandoc.h"  #include "mandoc.h"
 #include "regs.h"  #include "mandoc_xr.h"
 #include "main.h"  #include "roff.h"
 #include "mdoc.h"  #include "mdoc.h"
 #include "man.h"  #include "man.h"
 #include "roff.h"  #include "mandoc_parse.h"
   #include "tag.h"
   #include "main.h"
   #include "manconf.h"
   #include "mansearch.h"
   
 #define UNCONST(a)      ((void *)(uintptr_t)(const void *)(a))  enum    outmode {
           OUTMODE_DEF = 0,
 /* FIXME: Intel's compiler?  LLVM?  pcc?  */          OUTMODE_FLN,
           OUTMODE_LST,
 #if !defined(__GNUC__) || (__GNUC__ < 2)          OUTMODE_ALL,
 # if !defined(lint)          OUTMODE_ONE
 #  define __attribute__(x)  
 # endif  
 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */  
   
 typedef void            (*out_mdoc)(void *, const struct mdoc *);  
 typedef void            (*out_man)(void *, const struct man *);  
 typedef void            (*out_free)(void *);  
   
 struct  buf {  
         char             *buf;  
         size_t            sz;  
 };  };
   
 enum    intt {  
         INTT_AUTO,  
         INTT_MDOC,  
         INTT_MAN  
 };  
   
 enum    outt {  enum    outt {
         OUTT_ASCII = 0,          OUTT_ASCII = 0, /* -Tascii */
         OUTT_TREE,          OUTT_LOCALE,    /* -Tlocale */
         OUTT_HTML,          OUTT_UTF8,      /* -Tutf8 */
         OUTT_XHTML,          OUTT_TREE,      /* -Ttree */
         OUTT_LINT,          OUTT_MAN,       /* -Tman */
         OUTT_PS          OUTT_HTML,      /* -Thtml */
           OUTT_MARKDOWN,  /* -Tmarkdown */
           OUTT_LINT,      /* -Tlint */
           OUTT_PS,        /* -Tps */
           OUTT_PDF        /* -Tpdf */
 };  };
   
 struct  curparse {  struct  curparse {
         const char       *file;         /* Current parse. */          struct mparse    *mp;
         int               fd;           /* Current parse. */          struct manoutput *outopts;      /* output options */
         int               wflags;  
         /* FIXME: set by max error */  
 #define WARN_WALL        (1 << 0)       /* All-warnings mask. */  
 #define WARN_WERR        (1 << 2)       /* Warnings->errors. */  
         int               fflags;  
 #define FL_IGN_SCOPE     (1 << 0)       /* Ignore scope errors. */  
 #define FL_NIGN_ESCAPE   (1 << 1)       /* Don't ignore bad escapes. */  
 #define FL_NIGN_MACRO    (1 << 2)       /* Don't ignore bad macros. */  
 #define FL_IGN_ERRORS    (1 << 4)       /* Ignore failed parse. */  
 #define FL_STRICT         FL_NIGN_ESCAPE | \  
                           FL_NIGN_MACRO /* ignore nothing */  
         enum intt         inttype;      /* which parser to use */  
         struct man       *man;          /* man parser */  
         struct mdoc      *mdoc;         /* mdoc parser */  
         struct roff      *roff;         /* roff parser (!NULL) */  
         struct regset     regs;         /* roff registers */  
         enum outt         outtype;      /* which output to use */  
         out_mdoc          outmdoc;      /* mdoc output ptr */  
         out_man           outman;       /* man output ptr */  
         out_free          outfree;      /* free output ptr */  
         void             *outdata;      /* data for output */          void             *outdata;      /* data for output */
         char              outopts[BUFSIZ]; /* buf of output opts */          char             *os_s;         /* operating system for display */
           int               wstop;        /* stop after a file with a warning */
           enum mandoc_os    os_e;         /* check base system conventions */
           enum outt         outtype;      /* which output to use */
 };  };
   
 static  const char * const      mandocerrs[MANDOCERR_MAX] = {  
         "ok",  
   
         "generic warning",  int                       mandocdb(int, char *[]);
   
         "text should be uppercase",  static  void              check_xr(void);
         "sections out of conventional order",  static  int               fs_lookup(const struct manpaths *,
         "section name repeats",                                  size_t ipath, const char *,
         "out of order prologue",                                  const char *, const char *,
         "repeated prologue entry",                                  struct manpage **, size_t *);
         "list type must come first",  static  int               fs_search(const struct mansearch *,
         "bad standard",                                  const struct manpaths *, int, char**,
         "bad library",                                  struct manpage **, size_t *);
         "bad escape sequence",  static  void              outdata_alloc(struct curparse *);
         "unterminated quoted string",  static  void              parse(struct curparse *, int, const char *);
         "argument requires the width argument",  static  void              passthrough(int, int);
         "superfluous width argument",  static  pid_t             spawn_pager(struct tag_files *);
         "ignoring argument",  static  void              usage(enum argmode) __attribute__((__noreturn__));
         "bad date argument",  static  int               woptions(struct curparse *, char *);
         "bad width argument",  
         "unknown manual section",  
         "section not in conventional manual section",  
         "end of line whitespace",  
         "blocks badly nested",  
         "scope open on exit",  
   
         "generic error",  static  const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
   static  char              help_arg[] = "help";
   static  char             *help_argv[] = {help_arg, NULL};
   
         "NAME section must come first",  
         "bad Boolean value",  
         "child violates parent syntax",  
         "bad AT&T symbol",  
         "list type repeated",  
         "display type repeated",  
         "argument repeated",  
         "manual name not yet set",  
         "obsolete macro ignored",  
         "empty macro ignored",  
         "macro not allowed in body",  
         "macro not allowed in prologue",  
         "bad character",  
         "bad NAME section contents",  
         "no blank lines",  
         "no text in this context",  
         "bad comment style",  
         "unknown macro will be lost",  
         "line scope broken",  
         "argument count wrong",  
         "request scope close w/none open",  
         "scope already open",  
         "macro requires line argument(s)",  
         "macro requires body argument(s)",  
         "macro requires argument(s)",  
         "no title in document",  
         "missing list type",  
         "missing display type",  
         "missing font type",  
         "line argument(s) will be lost",  
         "body argument(s) will be lost",  
   
         "generic fatal error",  
   
         "column syntax is inconsistent",  
         "displays may not be nested",  
         "unsupported display type",  
         "blocks badly nested",  
         "no such block is open",  
         "scope broken, syntax violated",  
         "line scope broken, syntax violated",  
         "argument count wrong, violates syntax",  
         "child violates parent syntax",  
         "argument count wrong, violates syntax",  
         "no document body",  
         "no document prologue",  
         "utsname system call failed",  
         "memory exhausted",  
 };  
   
 static  void              fdesc(struct curparse *);  
 static  void              ffile(const char *, struct curparse *);  
 static  int               foptions(int *, char *);  
 static  struct man       *man_init(struct curparse *);  
 static  struct mdoc      *mdoc_init(struct curparse *);  
 static  struct roff      *roff_init(struct curparse *);  
 static  int               moptions(enum intt *, char *);  
 static  int               mmsg(enum mandocerr, void *,  
                                 int, int, const char *);  
 static  int               pset(const char *, int, struct curparse *,  
                                 struct man **, struct mdoc **);  
 static  int               toptions(struct curparse *, char *);  
 static  void              usage(void) __attribute__((noreturn));  
 static  void              version(void) __attribute__((noreturn));  
 static  int               woptions(int *, char *);  
   
 static  const char       *progname;  
 static  int               with_fatal;  
 static  int               with_error;  
   
 int  int
 main(int argc, char *argv[])  main(int argc, char *argv[])
 {  {
         int              c;          struct manconf   conf;
           struct mansearch search;
         struct curparse  curp;          struct curparse  curp;
           struct winsize   ws;
           struct tag_files *tag_files;
           struct manpage  *res, *resp;
           const char      *progname, *sec, *thisarg;
           char            *conf_file, *defpaths, *auxpaths;
           char            *oarg, *tagarg;
           unsigned char   *uc;
           size_t           i, sz, ssz;
           int              prio, best_prio;
           enum outmode     outmode;
           int              fd, startdir;
           int              show_usage;
           int              options;
           int              use_pager;
           int              status, signum;
           int              c;
           pid_t            pager_pid, tc_pgid, man_pgid, pid;
   
         progname = strrchr(argv[0], '/');  #if HAVE_PROGNAME
         if (progname == NULL)          progname = getprogname();
   #else
           if (argc < 1)
                   progname = mandoc_strdup("mandoc");
           else if ((progname = strrchr(argv[0], '/')) == NULL)
                 progname = argv[0];                  progname = argv[0];
         else          else
                 ++progname;                  ++progname;
           setprogname(progname);
   #endif
   
           mandoc_msg_setoutfile(stderr);
           if (strncmp(progname, "mandocdb", 8) == 0 ||
               strcmp(progname, BINM_MAKEWHATIS) == 0)
                   return mandocdb(argc, argv);
   
   #if HAVE_PLEDGE
           if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) {
                   mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno));
                   return mandoc_msg_getrc();
           }
   #endif
   #if HAVE_SANDBOX_INIT
           if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1)
                   errx((int)MANDOCLEVEL_SYSERR, "sandbox_init");
   #endif
   
           /* Search options. */
   
           memset(&conf, 0, sizeof(conf));
           conf_file = defpaths = NULL;
           auxpaths = NULL;
   
           memset(&search, 0, sizeof(struct mansearch));
           search.outkey = "Nd";
           oarg = NULL;
   
           if (strcmp(progname, BINM_MAN) == 0)
                   search.argmode = ARG_NAME;
           else if (strcmp(progname, BINM_APROPOS) == 0)
                   search.argmode = ARG_EXPR;
           else if (strcmp(progname, BINM_WHATIS) == 0)
                   search.argmode = ARG_WORD;
           else if (strncmp(progname, "help", 4) == 0)
                   search.argmode = ARG_NAME;
           else
                   search.argmode = ARG_FILE;
   
           /* Parser and formatter options. */
   
         memset(&curp, 0, sizeof(struct curparse));          memset(&curp, 0, sizeof(struct curparse));
           curp.outtype = OUTT_LOCALE;
           curp.outopts = &conf.output;
           options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1;
   
         curp.inttype = INTT_AUTO;          use_pager = 1;
         curp.outtype = OUTT_ASCII;          tag_files = NULL;
           show_usage = 0;
           outmode = OUTMODE_DEF;
   
         /* LINTED */          while ((c = getopt(argc, argv,
         while (-1 != (c = getopt(argc, argv, "f:m:O:T:VW:")))              "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) {
                   if (c == 'i' && search.argmode == ARG_EXPR) {
                           optind--;
                           break;
                   }
                 switch (c) {                  switch (c) {
                 case ('f'):                  case 'a':
                         if ( ! foptions(&curp.fflags, optarg))                          outmode = OUTMODE_ALL;
                                 return(EXIT_FAILURE);  
                         break;                          break;
                 case ('m'):                  case 'C':
                         if ( ! moptions(&curp.inttype, optarg))                          conf_file = optarg;
                                 return(EXIT_FAILURE);  
                         break;                          break;
                 case ('O'):                  case 'c':
                         (void)strlcat(curp.outopts, optarg, BUFSIZ);                          use_pager = 0;
                         (void)strlcat(curp.outopts, ",", BUFSIZ);  
                         break;                          break;
                 case ('T'):                  case 'f':
                         if ( ! toptions(&curp, optarg))                          search.argmode = ARG_WORD;
                                 return(EXIT_FAILURE);  
                         break;                          break;
                 case ('W'):                  case 'h':
                         if ( ! woptions(&curp.wflags, optarg))                          conf.output.synopsisonly = 1;
                                 return(EXIT_FAILURE);                          use_pager = 0;
                           outmode = OUTMODE_ALL;
                         break;                          break;
                 case ('V'):                  case 'I':
                         version();                          if (strncmp(optarg, "os=", 3) != 0) {
                         /* NOTREACHED */                                  mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0,
                                       "-I %s", optarg);
                                   return mandoc_msg_getrc();
                           }
                           if (curp.os_s != NULL) {
                                   mandoc_msg(MANDOCERR_BADARG_DUPE, 0, 0,
                                       "-I %s", optarg);
                                   return mandoc_msg_getrc();
                           }
                           curp.os_s = mandoc_strdup(optarg + 3);
                           break;
                   case 'K':
                           options &= ~(MPARSE_UTF8 | MPARSE_LATIN1);
                           if (strcmp(optarg, "utf-8") == 0)
                                   options |=  MPARSE_UTF8;
                           else if (strcmp(optarg, "iso-8859-1") == 0)
                                   options |=  MPARSE_LATIN1;
                           else if (strcmp(optarg, "us-ascii") != 0) {
                                   mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0,
                                       "-K %s", optarg);
                                   return mandoc_msg_getrc();
                           }
                           break;
                   case 'k':
                           search.argmode = ARG_EXPR;
                           break;
                   case 'l':
                           search.argmode = ARG_FILE;
                           outmode = OUTMODE_ALL;
                           break;
                   case 'M':
                           defpaths = optarg;
                           break;
                   case 'm':
                           auxpaths = optarg;
                           break;
                   case 'O':
                           oarg = optarg;
                           break;
                   case 'S':
                           search.arch = optarg;
                           break;
                   case 's':
                           search.sec = optarg;
                           break;
                   case 'T':
                           if (strcmp(optarg, "ascii") == 0)
                                   curp.outtype = OUTT_ASCII;
                           else if (strcmp(optarg, "lint") == 0) {
                                   curp.outtype = OUTT_LINT;
                                   mandoc_msg_setoutfile(stdout);
                                   mandoc_msg_setmin(MANDOCERR_BASE);
                           } else if (strcmp(optarg, "tree") == 0)
                                   curp.outtype = OUTT_TREE;
                           else if (strcmp(optarg, "man") == 0)
                                   curp.outtype = OUTT_MAN;
                           else if (strcmp(optarg, "html") == 0)
                                   curp.outtype = OUTT_HTML;
                           else if (strcmp(optarg, "markdown") == 0)
                                   curp.outtype = OUTT_MARKDOWN;
                           else if (strcmp(optarg, "utf8") == 0)
                                   curp.outtype = OUTT_UTF8;
                           else if (strcmp(optarg, "locale") == 0)
                                   curp.outtype = OUTT_LOCALE;
                           else if (strcmp(optarg, "ps") == 0)
                                   curp.outtype = OUTT_PS;
                           else if (strcmp(optarg, "pdf") == 0)
                                   curp.outtype = OUTT_PDF;
                           else {
                                   mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0,
                                       "-T %s", optarg);
                                   return mandoc_msg_getrc();
                           }
                           break;
                   case 'W':
                           if (woptions(&curp, optarg) == -1)
                                   return mandoc_msg_getrc();
                           break;
                   case 'w':
                           outmode = OUTMODE_FLN;
                           break;
                 default:                  default:
                         usage();                          show_usage = 1;
                         /* NOTREACHED */                          break;
                 }                  }
           }
   
         argc -= optind;          if (show_usage)
         argv += optind;                  usage(search.argmode);
   
         if (NULL == *argv) {          /* Postprocess options. */
                 curp.file = "<stdin>";  
                 curp.fd = STDIN_FILENO;  
   
                 fdesc(&curp);          if (outmode == OUTMODE_DEF) {
                   switch (search.argmode) {
                   case ARG_FILE:
                           outmode = OUTMODE_ALL;
                           use_pager = 0;
                           break;
                   case ARG_NAME:
                           outmode = OUTMODE_ONE;
                           break;
                   default:
                           outmode = OUTMODE_LST;
                           break;
                   }
         }          }
   
         while (*argv) {          if (oarg != NULL) {
                 ffile(*argv, &curp);                  if (outmode == OUTMODE_LST)
                           search.outkey = oarg;
                 if (with_fatal && !(curp.fflags & FL_IGN_ERRORS))                  else {
                         break;                          while (oarg != NULL) {
                 ++argv;                                  if (manconf_output(&conf.output,
                                       strsep(&oarg, ","), 0) == -1)
                                           return mandoc_msg_getrc();
                           }
                   }
         }          }
   
         if (curp.outfree)          if (curp.outtype != OUTT_TREE || !curp.outopts->noval)
                 (*curp.outfree)(curp.outdata);                  options |= MPARSE_VALIDATE;
         if (curp.mdoc)  
                 mdoc_free(curp.mdoc);  
         if (curp.man)  
                 man_free(curp.man);  
         if (curp.roff)  
                 roff_free(curp.roff);  
   
         return((with_fatal || with_error) ?          if (outmode == OUTMODE_FLN ||
                         EXIT_FAILURE :  EXIT_SUCCESS);              outmode == OUTMODE_LST ||
 }              !isatty(STDOUT_FILENO))
                   use_pager = 0;
   
           if (use_pager &&
               (conf.output.width == 0 || conf.output.indent == 0) &&
               ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 &&
               ws.ws_col > 1) {
                   if (conf.output.width == 0 && ws.ws_col < 79)
                           conf.output.width = ws.ws_col - 1;
                   if (conf.output.indent == 0 && ws.ws_col < 66)
                           conf.output.indent = 3;
           }
   
 static void  #if HAVE_PLEDGE
 version(void)          if (use_pager == 0) {
 {                  if (pledge("stdio rpath", NULL) == -1) {
                           mandoc_msg(MANDOCERR_PLEDGE, 0, 0,
                               "%s", strerror(errno));
                           return mandoc_msg_getrc();
                   }
           }
   #endif
   
         (void)printf("%s %s\n", progname, VERSION);          /* Parse arguments. */
         exit(EXIT_SUCCESS);  
 }  
   
           if (argc > 0) {
                   argc -= optind;
                   argv += optind;
           }
           resp = NULL;
   
 static void          /*
 usage(void)           * Quirks for help(1)
 {           * and for a man(1) section argument without -s.
            */
   
         (void)fprintf(stderr, "usage: %s [-V] [-foption] "          if (search.argmode == ARG_NAME) {
                         "[-mformat] [-Ooption] [-Toutput] "                  if (*progname == 'h') {
                         "[-Werr] [file...]\n", progname);                          if (argc == 0) {
         exit(EXIT_FAILURE);                                  argv = help_argv;
 }                                  argc = 1;
                           }
                   } else if (argc > 1 &&
                       ((uc = (unsigned char *)argv[0]) != NULL) &&
                       ((isdigit(uc[0]) && (uc[1] == '\0' ||
                         isalpha(uc[1]))) ||
                        (uc[0] == 'n' && uc[1] == '\0'))) {
                           search.sec = (char *)uc;
                           argv++;
                           argc--;
                   }
                   if (search.arch == NULL)
                           search.arch = getenv("MACHINE");
   #ifdef MACHINE
                   if (search.arch == NULL)
                           search.arch = MACHINE;
   #endif
           }
   
           /*
            * Use the first argument for -O tag in addition to
            * using it as a search term for man(1) or apropos(1).
            */
   
 static struct man *          if (conf.output.tag != NULL && *conf.output.tag == '\0') {
 man_init(struct curparse *curp)                  tagarg = argc > 0 && search.argmode == ARG_EXPR ?
 {                      strchr(*argv, '=') : NULL;
         int              pflags;                  conf.output.tag = tagarg == NULL ? *argv : tagarg + 1;
           }
   
         /* Defaults from mandoc.1. */          /* man(1), whatis(1), apropos(1) */
   
         pflags = MAN_IGN_MACRO | MAN_IGN_ESCAPE;          if (search.argmode != ARG_FILE) {
                   if (search.argmode == ARG_NAME &&
                       outmode == OUTMODE_ONE)
                           search.firstmatch = 1;
   
         if (curp->fflags & FL_NIGN_MACRO)                  /* Access the mandoc database. */
                 pflags &= ~MAN_IGN_MACRO;  
         if (curp->fflags & FL_NIGN_ESCAPE)  
                 pflags &= ~MAN_IGN_ESCAPE;  
   
         return(man_alloc(&curp->regs, curp, pflags, mmsg));                  manconf_parse(&conf, conf_file, defpaths, auxpaths);
 }                  if ( ! mansearch(&search, &conf.manpath,
                       argc, argv, &res, &sz))
                           usage(search.argmode);
   
                   if (sz == 0 && search.argmode == ARG_NAME)
                           (void)fs_search(&search, &conf.manpath,
                               argc, argv, &res, &sz);
   
 static struct roff *                  if (search.argmode == ARG_NAME) {
 roff_init(struct curparse *curp)                          for (c = 0; c < argc; c++) {
 {                                  if (strchr(argv[c], '/') == NULL)
                                           continue;
                                   if (access(argv[c], R_OK) == -1) {
                                           mandoc_msg_setinfilename(argv[c]);
                                           mandoc_msg(MANDOCERR_BADARG_BAD,
                                               0, 0, "%s", strerror(errno));
                                           mandoc_msg_setinfilename(NULL);
                                           continue;
                                   }
                                   res = mandoc_reallocarray(res,
                                       sz + 1, sizeof(*res));
                                   res[sz].file = mandoc_strdup(argv[c]);
                                   res[sz].names = NULL;
                                   res[sz].output = NULL;
                                   res[sz].bits = 0;
                                   res[sz].ipath = SIZE_MAX;
                                   res[sz].sec = 10;
                                   res[sz].form = FORM_SRC;
                                   sz++;
                           }
                   }
   
         return(roff_alloc(&curp->regs, mmsg, curp));                  if (sz == 0) {
 }                          if (search.argmode != ARG_NAME)
                                   warnx("nothing appropriate");
                           mandoc_msg_setrc(MANDOCLEVEL_BADARG);
                           goto out;
                   }
   
                   /*
                    * For standard man(1) and -a output mode,
                    * prepare for copying filename pointers
                    * into the program parameter array.
                    */
   
 static struct mdoc *                  if (outmode == OUTMODE_ONE) {
 mdoc_init(struct curparse *curp)                          argc = 1;
 {                          best_prio = 40;
         int              pflags;                  } else if (outmode == OUTMODE_ALL)
                           argc = (int)sz;
   
         /* Defaults from mandoc.1. */                  /* Iterate all matching manuals. */
   
         pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE;                  resp = res;
                   for (i = 0; i < sz; i++) {
                           if (outmode == OUTMODE_FLN)
                                   puts(res[i].file);
                           else if (outmode == OUTMODE_LST)
                                   printf("%s - %s\n", res[i].names,
                                       res[i].output == NULL ? "" :
                                       res[i].output);
                           else if (outmode == OUTMODE_ONE) {
                                   /* Search for the best section. */
                                   sec = res[i].file;
                                   sec += strcspn(sec, "123456789");
                                   if (sec[0] == '\0')
                                           continue; /* No section at all. */
                                   prio = sec_prios[sec[0] - '1'];
                                   if (search.sec != NULL) {
                                           ssz = strlen(search.sec);
                                           if (strncmp(sec, search.sec, ssz) == 0)
                                                   sec += ssz;
                                   } else
                                           sec++; /* Prefer without suffix. */
                                   if (*sec != '/')
                                           prio += 10; /* Wrong dir name. */
                                   if (search.sec != NULL &&
                                       (strlen(sec) <= ssz  + 3 ||
                                        strcmp(sec + strlen(sec) - ssz,
                                         search.sec) != 0))
                                           prio += 20; /* Wrong file ext. */
                                   if (prio >= best_prio)
                                           continue;
                                   best_prio = prio;
                                   resp = res + i;
                           }
                   }
   
         if (curp->fflags & FL_IGN_SCOPE)                  /*
                 pflags |= MDOC_IGN_SCOPE;                   * For man(1), -a and -i output mode, fall through
         if (curp->fflags & FL_NIGN_ESCAPE)                   * to the main mandoc(1) code iterating files
                 pflags &= ~MDOC_IGN_ESCAPE;                   * and running the parsers on each of them.
         if (curp->fflags & FL_NIGN_MACRO)                   */
                 pflags &= ~MDOC_IGN_MACRO;  
   
         return(mdoc_alloc(&curp->regs, curp, pflags, mmsg));                  if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST)
 }                          goto out;
   
   
 static void  
 ffile(const char *file, struct curparse *curp)  
 {  
   
         curp->file = file;  
         if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {  
                 perror(curp->file);  
                 with_fatal = 1;  
                 return;  
         }          }
   
         fdesc(curp);          /* mandoc(1) */
   
         if (-1 == close(curp->fd))  #if HAVE_PLEDGE
                 perror(curp->file);          if (use_pager) {
 }                  if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) {
                           mandoc_msg(MANDOCERR_PLEDGE, 0, 0,
                               "%s", strerror(errno));
 static int                          return mandoc_msg_getrc();
 resize_buf(struct buf *buf, size_t initial)                  }
 {          } else {
         void *tmp;                  if (pledge("stdio rpath", NULL) == -1) {
         size_t sz;                          mandoc_msg(MANDOCERR_PLEDGE, 0, 0,
                               "%s", strerror(errno));
         if (buf->sz == 0)                          return mandoc_msg_getrc();
                 sz = initial;                  }
         else  
                 sz = 2 * buf->sz;  
         tmp = realloc(buf->buf, sz);  
         if (NULL == tmp) {  
                 perror(NULL);  
                 return(0);  
         }          }
         buf->buf = tmp;  #endif
         buf->sz = sz;  
         return(1);  
 }  
   
           if (search.argmode == ARG_FILE && auxpaths != NULL) {
 static int                  if (strcmp(auxpaths, "doc") == 0)
 read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)                          options |= MPARSE_MDOC;
 {                  else if (strcmp(auxpaths, "an") == 0)
         struct stat      st;                          options |= MPARSE_MAN;
         size_t           off;  
         ssize_t          ssz;  
   
         if (-1 == fstat(curp->fd, &st)) {  
                 perror(curp->file);  
                 with_fatal = 1;  
                 return(0);  
         }          }
   
         /*          mchars_alloc();
          * If we're a regular file, try just reading in the whole entry          curp.mp = mparse_alloc(options, curp.os_e, curp.os_s);
          * via mmap().  This is faster than reading it into blocks, and  
          * since each file is only a few bytes to begin with, I'm not  
          * concerned that this is going to tank any machines.  
          */  
   
         if (S_ISREG(st.st_mode)) {          if (argc < 1) {
                 if (st.st_size >= (1U << 31)) {                  if (use_pager) {
                         fprintf(stderr, "%s: input too large\n",                          tag_files = tag_init();
                                         curp->file);                          if (tag_files != NULL)
                         with_fatal = 1;                                  tag_files->tagname = conf.output.tag;
                         return(0);  
                 }                  }
                 *with_mmap = 1;                  thisarg = "<stdin>";
                 fb->sz = (size_t)st.st_size;                  mandoc_msg_setinfilename(thisarg);
                 fb->buf = mmap(NULL, fb->sz, PROT_READ,                  parse(&curp, STDIN_FILENO, thisarg);
                                 MAP_FILE|MAP_SHARED, curp->fd, 0);                  mandoc_msg_setinfilename(NULL);
                 if (fb->buf != MAP_FAILED)  
                         return(1);  
         }          }
   
         /*          /*
          * If this isn't a regular file (like, say, stdin), then we must           * Remember the original working directory, if possible.
          * go the old way and just read things in bit by bit.           * This will be needed if some names on the command line
            * are page names and some are relative file names.
            * Do not error out if the current directory is not
            * readable: Maybe it won't be needed after all.
          */           */
           startdir = open(".", O_RDONLY | O_DIRECTORY);
   
         *with_mmap = 0;          while (argc > 0) {
         off = 0;  
         fb->sz = 0;                  /*
         fb->buf = NULL;                   * Changing directories is not needed in ARG_FILE mode.
         for (;;) {                   * Do it on a best-effort basis.  Even in case of
                 if (off == fb->sz) {                   * failure, some functionality may still work.
                         if (fb->sz == (1U << 31)) {                   */
                                 fprintf(stderr, "%s: input too large\n",                  if (resp != NULL) {
                                                 curp->file);                          if (resp->ipath != SIZE_MAX)
                                 break;                                  (void)chdir(conf.manpath.paths[resp->ipath]);
                           else if (startdir != -1)
                                   (void)fchdir(startdir);
                           thisarg = resp->file;
                   } else
                           thisarg = *argv;
   
                   mandoc_msg_setinfilename(thisarg);
                   fd = mparse_open(curp.mp, thisarg);
                   if (fd != -1) {
                           if (use_pager) {
                                   use_pager = 0;
                                   tag_files = tag_init();
                                   if (tag_files != NULL)
                                           tag_files->tagname = conf.output.tag;
                         }                          }
                         if (! resize_buf(fb, 65536))  
                           if (resp == NULL || resp->form == FORM_SRC)
                                   parse(&curp, fd, thisarg);
                           else
                                   passthrough(fd, conf.output.synopsisonly);
   
                           if (ferror(stdout)) {
                                   if (tag_files != NULL) {
                                           mandoc_msg(MANDOCERR_WRITE, 0, 0,
                                               "%s: %s", tag_files->ofn,
                                               strerror(errno));
                                           tag_unlink();
                                           tag_files = NULL;
                                   } else
                                           mandoc_msg(MANDOCERR_WRITE, 0, 0,
                                               "%s", strerror(errno));
                                 break;                                  break;
                 }                          }
                 ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);  
                 if (ssz == 0) {  
                         fb->sz = off;  
                         return(1);  
                 }  
                 if (ssz == -1) {  
                         perror(curp->file);  
                         break;  
                 }  
                 off += (size_t)ssz;  
         }  
   
         free(fb->buf);                          if (argc > 1 && curp.outtype <= OUTT_UTF8) {
         fb->buf = NULL;                                  if (curp.outdata == NULL)
         with_fatal = 1;                                          outdata_alloc(&curp);
         return(0);                                  terminal_sepline(curp.outdata);
 }                          }
                   } else
                           mandoc_msg(resp == NULL ? MANDOCERR_BADARG_BAD :
                               MANDOCERR_OPEN, 0, 0, "%s", strerror(errno));
   
                   mandoc_msg_setinfilename(NULL);
   
 static void                  if (curp.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK)
 fdesc(struct curparse *curp)                          break;
 {  
         struct buf       ln, blk;  
         int              i, pos, lnn, lnn_start, with_mmap, of;  
         enum rofferr     re;  
         struct man      *man;  
         struct mdoc     *mdoc;  
         struct roff     *roff;  
   
         man = NULL;                  if (resp != NULL)
         mdoc = NULL;                          resp++;
         roff = NULL;                  else
                           argv++;
                   if (--argc)
                           mparse_reset(curp.mp);
           }
           if (startdir != -1) {
                   (void)fchdir(startdir);
                   close(startdir);
           }
   
         memset(&ln, 0, sizeof(struct buf));          if (curp.outdata != NULL) {
                   switch (curp.outtype) {
                   case OUTT_HTML:
                           html_free(curp.outdata);
                           break;
                   case OUTT_UTF8:
                   case OUTT_LOCALE:
                   case OUTT_ASCII:
                           ascii_free(curp.outdata);
                           break;
                   case OUTT_PDF:
                   case OUTT_PS:
                           pspdf_free(curp.outdata);
                           break;
                   default:
                           break;
                   }
           }
           mandoc_xr_free();
           mparse_free(curp.mp);
           mchars_free();
   
   out:
           if (search.argmode != ARG_FILE) {
                   manconf_free(&conf);
                   mansearch_free(res, sz);
           }
   
           free(curp.os_s);
   
         /*          /*
          * Two buffers: ln and buf.  buf is the input file and may be           * When using a pager, finish writing both temporary files,
          * memory mapped.  ln is a line buffer and grows on-demand.           * fork it, wait for the user to close it, and clean up.
          */           */
   
         if ( ! read_whole_file(curp, &blk, &with_mmap))          if (tag_files != NULL) {
                 return;                  fclose(stdout);
                   tag_write();
                   man_pgid = getpgid(0);
                   tag_files->tcpgid = man_pgid == getpid() ?
                       getpgid(getppid()) : man_pgid;
                   pager_pid = 0;
                   signum = SIGSTOP;
                   for (;;) {
   
         if (NULL == curp->roff)                          /* Stop here until moved to the foreground. */
                 curp->roff = roff_init(curp);  
         if (NULL == (roff = curp->roff))  
                 goto bailout;  
   
         for (i = 0, lnn = 1; i < (int)blk.sz;) {                          tc_pgid = tcgetpgrp(tag_files->ofd);
                 pos = 0;                          if (tc_pgid != man_pgid) {
                 lnn_start = lnn;                                  if (tc_pgid == pager_pid) {
                 while (i < (int)blk.sz) {                                          (void)tcsetpgrp(tag_files->ofd,
                         if ('\n' == blk.buf[i]) {                                              man_pgid);
                                 ++i;                                          if (signum == SIGTTIN)
                                 ++lnn;                                                  continue;
                                 break;                                  } else
                         }                                          tag_files->tcpgid = tc_pgid;
                         /* Trailing backslash is like a plain character. */                                  kill(0, signum);
                         if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {  
                                 if (pos >= (int)ln.sz)  
                                         if (! resize_buf(&ln, 256))  
                                                 goto bailout;  
                                 ln.buf[pos++] = blk.buf[i++];  
                                 continue;                                  continue;
                         }                          }
                         /* Found an escape and at least one other character. */  
                         if ('\n' == blk.buf[i + 1]) {                          /* Once in the foreground, activate the pager. */
                                 /* Escaped newlines are skipped over */  
                                 i += 2;                          if (pager_pid) {
                                 ++lnn;                                  (void)tcsetpgrp(tag_files->ofd, pager_pid);
                                   kill(pager_pid, SIGCONT);
                           } else
                                   pager_pid = spawn_pager(tag_files);
   
                           /* Wait for the pager to stop or exit. */
   
                           while ((pid = waitpid(pager_pid, &status,
                               WUNTRACED)) == -1 && errno == EINTR)
                                 continue;                                  continue;
                         }  
                         if ('"' == blk.buf[i + 1]) {                          if (pid == -1) {
                                 i += 2;                                  mandoc_msg(MANDOCERR_WAIT, 0, 0,
                                 /* Comment, skip to end of line */                                      "%s", strerror(errno));
                                 for (; i < (int)blk.sz; ++i) {  
                                         if ('\n' == blk.buf[i]) {  
                                                 ++i;  
                                                 ++lnn;  
                                                 break;  
                                         }  
                                 }  
                                 /* Backout trailing whitespaces */  
                                 for (; pos > 0; --pos) {  
                                         if (ln.buf[pos - 1] != ' ')  
                                                 break;  
                                         if (pos > 2 && ln.buf[pos - 2] == '\\')  
                                                 break;  
                                 }  
                                 break;                                  break;
                         }                          }
                         /* Some other escape sequence, copy and continue. */                          if (!WIFSTOPPED(status))
                         if (pos + 1 >= (int)ln.sz)                                  break;
                                 if (! resize_buf(&ln, 256))  
                                         goto bailout;  
   
                         ln.buf[pos++] = blk.buf[i++];                          signum = WSTOPSIG(status);
                         ln.buf[pos++] = blk.buf[i++];  
                 }                  }
                   tag_unlink();
           }
           return (int)mandoc_msg_getrc();
   }
   
                 if (pos >= (int)ln.sz)  static void
                         if (! resize_buf(&ln, 256))  usage(enum argmode argmode)
                                 goto bailout;  {
                 ln.buf[pos] = '\0';          switch (argmode) {
           case ARG_FILE:
                   fputs("usage: mandoc [-ac] [-I os=name] "
                       "[-K encoding] [-mdoc | -man] [-O options]\n"
                       "\t      [-T output] [-W level] [file ...]\n", stderr);
                   break;
           case ARG_NAME:
                   fputs("usage: man [-acfhklw] [-C file] [-M path] "
                       "[-m path] [-S subsection]\n"
                       "\t   [[-s] section] name ...\n", stderr);
                   break;
           case ARG_WORD:
                   fputs("usage: whatis [-afk] [-C file] "
                       "[-M path] [-m path] [-O outkey] [-S arch]\n"
                       "\t      [-s section] name ...\n", stderr);
                   break;
           case ARG_EXPR:
                   fputs("usage: apropos [-afk] [-C file] "
                       "[-M path] [-m path] [-O outkey] [-S arch]\n"
                       "\t       [-s section] expression ...\n", stderr);
                   break;
           }
           exit((int)MANDOCLEVEL_BADARG);
   }
   
                 /*  static int
                  * A significant amount of complexity is contained by  fs_lookup(const struct manpaths *paths, size_t ipath,
                  * the roff preprocessor.  It's line-oriented but can be          const char *sec, const char *arch, const char *name,
                  * expressed on one line, so we need at times to          struct manpage **res, size_t *ressz)
                  * readjust our starting point and re-run it.  The roff  {
                  * preprocessor can also readjust the buffers with new          struct stat      sb;
                  * data, so we pass them in wholesale.          glob_t           globinfo;
                  */          struct manpage  *page;
           char            *file;
           int              globres;
           enum form        form;
   
                 of = 0;          form = FORM_SRC;
                 do {          mandoc_asprintf(&file, "%s/man%s/%s.%s",
                         re = roff_parseln(roff, lnn_start,              paths->paths[ipath], sec, name, sec);
                                         &ln.buf, &ln.sz, of, &of);          if (stat(file, &sb) != -1)
                 } while (ROFF_RERUN == re);                  goto found;
           free(file);
   
                 if (ROFF_IGN == re)          mandoc_asprintf(&file, "%s/cat%s/%s.0",
                         continue;              paths->paths[ipath], sec, name);
                 else if (ROFF_ERR == re)          if (stat(file, &sb) != -1) {
                         goto bailout;                  form = FORM_CAT;
                   goto found;
           }
           free(file);
   
                 /*          if (arch != NULL) {
                  * If input parsers have not been allocated, do so now.                  mandoc_asprintf(&file, "%s/man%s/%s/%s.%s",
                  * We keep these instanced betwen parsers, but set them                      paths->paths[ipath], sec, arch, name, sec);
                  * locally per parse routine since we can use different                  if (stat(file, &sb) != -1)
                  * parsers with each one.                          goto found;
                  */                  free(file);
           }
   
                 if ( ! (man || mdoc))          mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*",
                         if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc))              paths->paths[ipath], sec, name);
                                 goto bailout;          globres = glob(file, 0, NULL, &globinfo);
           if (globres != 0 && globres != GLOB_NOMATCH)
                   mandoc_msg(MANDOCERR_GLOB, 0, 0,
                       "%s: %s", file, strerror(errno));
           free(file);
           if (globres == 0)
                   file = mandoc_strdup(*globinfo.gl_pathv);
           globfree(&globinfo);
           if (globres == 0) {
                   if (stat(file, &sb) != -1)
                           goto found;
                   free(file);
           }
           if (res != NULL || ipath + 1 != paths->sz)
                   return -1;
   
                 /* Lastly, push down into the parsers themselves. */          mandoc_asprintf(&file, "%s.%s", name, sec);
           globres = stat(file, &sb);
           free(file);
           return globres;
   
                 if (man && ! man_parseln(man, lnn_start, ln.buf, of))  found:
                         goto bailout;          warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s",
                 if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of))              name, sec, BINM_MAKEWHATIS, paths->paths[ipath]);
                         goto bailout;          if (res == NULL) {
                   free(file);
                   return 0;
         }          }
           *res = mandoc_reallocarray(*res, ++*ressz, sizeof(**res));
           page = *res + (*ressz - 1);
           page->file = file;
           page->names = NULL;
           page->output = NULL;
           page->bits = NAME_FILE & NAME_MASK;
           page->ipath = ipath;
           page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10;
           page->form = form;
           return 0;
   }
   
         /* NOTE a parser may not have been assigned, yet. */  static int
   fs_search(const struct mansearch *cfg, const struct manpaths *paths,
           int argc, char **argv, struct manpage **res, size_t *ressz)
   {
           const char *const sections[] =
               {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"};
           const size_t nsec = sizeof(sections)/sizeof(sections[0]);
   
         if ( ! (man || mdoc)) {          size_t           ipath, isec, lastsz;
                 fprintf(stderr, "%s: Not a manual\n", curp->file);  
                 goto bailout;          assert(cfg->argmode == ARG_NAME);
   
           if (res != NULL)
                   *res = NULL;
           *ressz = lastsz = 0;
           while (argc) {
                   for (ipath = 0; ipath < paths->sz; ipath++) {
                           if (cfg->sec != NULL) {
                                   if (fs_lookup(paths, ipath, cfg->sec,
                                       cfg->arch, *argv, res, ressz) != -1 &&
                                       cfg->firstmatch)
                                           return 0;
                           } else for (isec = 0; isec < nsec; isec++)
                                   if (fs_lookup(paths, ipath, sections[isec],
                                       cfg->arch, *argv, res, ressz) != -1 &&
                                       cfg->firstmatch)
                                           return 0;
                   }
                   if (res != NULL && *ressz == lastsz &&
                       strchr(*argv, '/') == NULL) {
                           if (cfg->arch != NULL &&
                               arch_valid(cfg->arch, OSENUM) == 0)
                                   warnx("Unknown architecture \"%s\".",
                                       cfg->arch);
                           else if (cfg->sec == NULL)
                                   warnx("No entry for %s in the manual.",
                                       *argv);
                           else
                                   warnx("No entry for %s in section %s "
                                       "of the manual.", *argv, cfg->sec);
                   }
                   lastsz = *ressz;
                   argv++;
                   argc--;
         }          }
           return -1;
   }
   
         /* Clean up the parse routine ASTs. */  static void
   parse(struct curparse *curp, int fd, const char *file)
   {
           struct roff_meta *meta;
   
         if (mdoc && ! mdoc_endparse(mdoc))          /* Begin by parsing the file itself. */
                 goto bailout;  
         if (man && ! man_endparse(man))  
                 goto bailout;  
         if (roff && ! roff_endparse(roff))  
                 goto bailout;  
   
         /* If unset, allocate output dev now (if applicable). */          assert(file);
           assert(fd >= 0);
   
         if ( ! (curp->outman && curp->outmdoc)) {          mparse_readfd(curp->mp, fd, file);
           if (fd != STDIN_FILENO)
                   close(fd);
   
           /*
            * With -Wstop and warnings or errors of at least the requested
            * level, do not produce output.
            */
   
           if (curp->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK)
                   return;
   
           if (curp->outdata == NULL)
                   outdata_alloc(curp);
           else if (curp->outtype == OUTT_HTML)
                   html_reset(curp);
   
           mandoc_xr_reset();
           meta = mparse_result(curp->mp);
   
           /* Execute the out device, if it exists. */
   
           if (meta->macroset == MACROSET_MDOC) {
                 switch (curp->outtype) {                  switch (curp->outtype) {
                 case (OUTT_XHTML):                  case OUTT_HTML:
                         curp->outdata = xhtml_alloc(curp->outopts);                          html_mdoc(curp->outdata, meta);
                         break;                          break;
                 case (OUTT_HTML):                  case OUTT_TREE:
                         curp->outdata = html_alloc(curp->outopts);                          tree_mdoc(curp->outdata, meta);
                         break;                          break;
                 case (OUTT_ASCII):                  case OUTT_MAN:
                         curp->outdata = ascii_alloc(curp->outopts);                          man_mdoc(curp->outdata, meta);
                         curp->outfree = ascii_free;  
                         break;                          break;
                 case (OUTT_PS):                  case OUTT_PDF:
                         curp->outdata = ps_alloc(curp->outopts);                  case OUTT_ASCII:
                         curp->outfree = ps_free;                  case OUTT_UTF8:
                   case OUTT_LOCALE:
                   case OUTT_PS:
                           terminal_mdoc(curp->outdata, meta);
                         break;                          break;
                   case OUTT_MARKDOWN:
                           markdown_mdoc(curp->outdata, meta);
                           break;
                 default:                  default:
                         break;                          break;
                 }                  }
           }
           if (meta->macroset == MACROSET_MAN) {
                 switch (curp->outtype) {                  switch (curp->outtype) {
                 case (OUTT_HTML):                  case OUTT_HTML:
                         /* FALLTHROUGH */                          html_man(curp->outdata, meta);
                 case (OUTT_XHTML):  
                         curp->outman = html_man;  
                         curp->outmdoc = html_mdoc;  
                         curp->outfree = html_free;  
                         break;                          break;
                 case (OUTT_TREE):                  case OUTT_TREE:
                         curp->outman = tree_man;                          tree_man(curp->outdata, meta);
                         curp->outmdoc = tree_mdoc;  
                         break;                          break;
                 case (OUTT_ASCII):                  case OUTT_MAN:
                         /* FALLTHROUGH */                          mparse_copy(curp->mp);
                 case (OUTT_PS):  
                         curp->outman = terminal_man;  
                         curp->outmdoc = terminal_mdoc;  
                         break;                          break;
                   case OUTT_PDF:
                   case OUTT_ASCII:
                   case OUTT_UTF8:
                   case OUTT_LOCALE:
                   case OUTT_PS:
                           terminal_man(curp->outdata, meta);
                           break;
                 default:                  default:
                         break;                          break;
                 }                  }
         }          }
           if (mandoc_msg_getmin() < MANDOCERR_STYLE)
         /* Execute the out device, if it exists. */                  check_xr();
   
         if (man && curp->outman)  
                 (*curp->outman)(curp->outdata, man);  
         if (mdoc && curp->outmdoc)  
                 (*curp->outmdoc)(curp->outdata, mdoc);  
   
  cleanup:  
         memset(&curp->regs, 0, sizeof(struct regset));  
         if (mdoc)  
                 mdoc_reset(mdoc);  
         if (man)  
                 man_reset(man);  
         if (roff)  
                 roff_reset(roff);  
         if (ln.buf)  
                 free(ln.buf);  
         if (with_mmap)  
                 munmap(blk.buf, blk.sz);  
         else  
                 free(blk.buf);  
   
         return;  
   
  bailout:  
         with_fatal = 1;  
         goto cleanup;  
 }  }
   
   static void
 static int  check_xr(void)
 pset(const char *buf, int pos, struct curparse *curp,  
                 struct man **man, struct mdoc **mdoc)  
 {  {
         int              i;          static struct manpaths   paths;
           struct mansearch         search;
           struct mandoc_xr        *xr;
           size_t                   sz;
   
         /*          if (paths.sz == 0)
          * Try to intuit which kind of manual parser should be used.  If                  manpath_base(&paths);
          * passed in by command-line (-man, -mdoc), then use that  
          * explicitly.  If passed as -mandoc, then try to guess from the  
          * line: either skip dot-lines, use -mdoc when finding `.Dt', or  
          * default to -man, which is more lenient.  
          */  
   
         if ('.' == buf[0] || '\'' == buf[0]) {          for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) {
                 for (i = 1; buf[i]; i++)                  if (xr->line == -1)
                         if (' ' != buf[i] && '\t' != buf[i])                          continue;
                                 break;                  search.arch = NULL;
                 if (0 == buf[i])                  search.sec = xr->sec;
                         return(1);                  search.outkey = NULL;
                   search.argmode = ARG_NAME;
                   search.firstmatch = 1;
                   if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz))
                           continue;
                   if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz) != -1)
                           continue;
                   if (xr->count == 1)
                           mandoc_msg(MANDOCERR_XR_BAD, xr->line,
                               xr->pos + 1, "Xr %s %s", xr->name, xr->sec);
                   else
                           mandoc_msg(MANDOCERR_XR_BAD, xr->line,
                               xr->pos + 1, "Xr %s %s (%d times)",
                               xr->name, xr->sec, xr->count);
         }          }
   }
   
         switch (curp->inttype) {  static void
         case (INTT_MDOC):  outdata_alloc(struct curparse *curp)
                 if (NULL == curp->mdoc)  {
                         curp->mdoc = mdoc_init(curp);          switch (curp->outtype) {
                 if (NULL == (*mdoc = curp->mdoc))          case OUTT_HTML:
                         return(0);                  curp->outdata = html_alloc(curp->outopts);
                 return(1);                  break;
         case (INTT_MAN):          case OUTT_UTF8:
                 if (NULL == curp->man)                  curp->outdata = utf8_alloc(curp->outopts);
                         curp->man = man_init(curp);                  break;
                 if (NULL == (*man = curp->man))          case OUTT_LOCALE:
                         return(0);                  curp->outdata = locale_alloc(curp->outopts);
                 return(1);                  break;
           case OUTT_ASCII:
                   curp->outdata = ascii_alloc(curp->outopts);
                   break;
           case OUTT_PDF:
                   curp->outdata = pdf_alloc(curp->outopts);
                   break;
           case OUTT_PS:
                   curp->outdata = ps_alloc(curp->outopts);
                   break;
         default:          default:
                 break;                  break;
         }          }
   
         if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {  
                 if (NULL == curp->mdoc)  
                         curp->mdoc = mdoc_init(curp);  
                 if (NULL == (*mdoc = curp->mdoc))  
                         return(0);  
                 return(1);  
         }  
   
         if (NULL == curp->man)  
                 curp->man = man_init(curp);  
         if (NULL == (*man = curp->man))  
                 return(0);  
         return(1);  
 }  }
   
   static void
 static int  passthrough(int fd, int synopsis_only)
 moptions(enum intt *tflags, char *arg)  
 {  {
           const char       synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS";
           const char       synr[] = "SYNOPSIS";
   
         if (0 == strcmp(arg, "doc"))          FILE            *stream;
                 *tflags = INTT_MDOC;          char            *line, *cp;
         else if (0 == strcmp(arg, "andoc"))          size_t           linesz;
                 *tflags = INTT_AUTO;          ssize_t          len, written;
         else if (0 == strcmp(arg, "an"))          int              lno, print;
                 *tflags = INTT_MAN;  
         else {  
                 fprintf(stderr, "%s: Bad argument\n", arg);  
                 return(0);  
         }  
   
         return(1);          stream = NULL;
 }          line = NULL;
           linesz = 0;
   
           if (fflush(stdout) == EOF) {
 static int                  mandoc_msg(MANDOCERR_FFLUSH, 0, 0, "%s", strerror(errno));
 toptions(struct curparse *curp, char *arg)                  goto done;
 {  
   
         if (0 == strcmp(arg, "ascii"))  
                 curp->outtype = OUTT_ASCII;  
         else if (0 == strcmp(arg, "lint")) {  
                 curp->outtype = OUTT_LINT;  
                 curp->wflags |= WARN_WALL;  
                 curp->fflags |= FL_STRICT;  
         }          }
         else if (0 == strcmp(arg, "tree"))          if ((stream = fdopen(fd, "r")) == NULL) {
                 curp->outtype = OUTT_TREE;                  close(fd);
         else if (0 == strcmp(arg, "html"))                  mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno));
                 curp->outtype = OUTT_HTML;                  goto done;
         else if (0 == strcmp(arg, "xhtml"))  
                 curp->outtype = OUTT_XHTML;  
         else if (0 == strcmp(arg, "ps"))  
                 curp->outtype = OUTT_PS;  
         else {  
                 fprintf(stderr, "%s: Bad argument\n", arg);  
                 return(0);  
         }          }
   
         return(1);          lno = print = 0;
           while ((len = getline(&line, &linesz, stream)) != -1) {
                   lno++;
                   cp = line;
                   if (synopsis_only) {
                           if (print) {
                                   if ( ! isspace((unsigned char)*cp))
                                           goto done;
                                   while (isspace((unsigned char)*cp)) {
                                           cp++;
                                           len--;
                                   }
                           } else {
                                   if (strcmp(cp, synb) == 0 ||
                                       strcmp(cp, synr) == 0)
                                           print = 1;
                                   continue;
                           }
                   }
                   for (; len > 0; len -= written) {
                           if ((written = write(STDOUT_FILENO, cp, len)) == -1) {
                                   mandoc_msg(MANDOCERR_WRITE, 0, 0,
                                       "%s", strerror(errno));
                                   goto done;
                           }
                   }
           }
           if (ferror(stream))
                   mandoc_msg(MANDOCERR_GETLINE, lno, 0, "%s", strerror(errno));
   
   done:
           free(line);
           if (stream != NULL)
                   fclose(stream);
 }  }
   
   
 static int  static int
 foptions(int *fflags, char *arg)  woptions(struct curparse *curp, char *arg)
 {  {
         char            *v, *o;          char            *v, *o;
         const char      *toks[8];          const char      *toks[11];
   
         toks[0] = "ign-scope";          toks[0] = "stop";
         toks[1] = "no-ign-escape";          toks[1] = "all";
         toks[2] = "no-ign-macro";          toks[2] = "base";
         toks[3] = "ign-errors";          toks[3] = "style";
         toks[4] = "strict";          toks[4] = "warning";
         toks[5] = "ign-escape";          toks[5] = "error";
         toks[6] = NULL;          toks[6] = "unsupp";
           toks[7] = "fatal";
           toks[8] = "openbsd";
           toks[9] = "netbsd";
           toks[10] = NULL;
   
         while (*arg) {          while (*arg) {
                 o = arg;                  o = arg;
                 switch (getsubopt(&arg, UNCONST(toks), &v)) {                  switch (getsubopt(&arg, (char * const *)toks, &v)) {
                 case (0):                  case 0:
                         *fflags |= FL_IGN_SCOPE;                          curp->wstop = 1;
                         break;                          break;
                 case (1):                  case 1:
                         *fflags |= FL_NIGN_ESCAPE;                  case 2:
                           mandoc_msg_setmin(MANDOCERR_BASE);
                         break;                          break;
                 case (2):                  case 3:
                         *fflags |= FL_NIGN_MACRO;                          mandoc_msg_setmin(MANDOCERR_STYLE);
                         break;                          break;
                 case (3):                  case 4:
                         *fflags |= FL_IGN_ERRORS;                          mandoc_msg_setmin(MANDOCERR_WARNING);
                         break;                          break;
                 case (4):                  case 5:
                         *fflags |= FL_STRICT;                          mandoc_msg_setmin(MANDOCERR_ERROR);
                         break;                          break;
                 case (5):                  case 6:
                         *fflags &= ~FL_NIGN_ESCAPE;                          mandoc_msg_setmin(MANDOCERR_UNSUPP);
                         break;                          break;
                   case 7:
                           mandoc_msg_setmin(MANDOCERR_BADARG);
                           break;
                   case 8:
                           mandoc_msg_setmin(MANDOCERR_BASE);
                           curp->os_e = MANDOC_OS_OPENBSD;
                           break;
                   case 9:
                           mandoc_msg_setmin(MANDOCERR_BASE);
                           curp->os_e = MANDOC_OS_NETBSD;
                           break;
                 default:                  default:
                         fprintf(stderr, "%s: Bad argument\n", o);                          mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-W %s", o);
                         return(0);                          return -1;
                 }                  }
         }          }
           return 0;
         return(1);  
 }  }
   
   static pid_t
 static int  spawn_pager(struct tag_files *tag_files)
 woptions(int *wflags, char *arg)  
 {  {
         char            *v, *o;          const struct timespec timeout = { 0, 100000000 };  /* 0.1s */
         const char      *toks[3];  #define MAX_PAGER_ARGS 16
           char            *argv[MAX_PAGER_ARGS];
           const char      *pager;
           char            *cp;
   #if HAVE_LESS_T
           size_t           cmdlen;
   #endif
           int              argc, use_ofn;
           pid_t            pager_pid;
   
         toks[0] = "all";          pager = getenv("MANPAGER");
         toks[1] = "error";          if (pager == NULL || *pager == '\0')
         toks[2] = NULL;                  pager = getenv("PAGER");
           if (pager == NULL || *pager == '\0')
                   pager = "more -s";
           cp = mandoc_strdup(pager);
   
         while (*arg) {          /*
                 o = arg;           * Parse the pager command into words.
                 switch (getsubopt(&arg, UNCONST(toks), &v)) {           * Intentionally do not do anything fancy here.
                 case (0):           */
                         *wflags |= WARN_WALL;  
           argc = 0;
           while (argc + 5 < MAX_PAGER_ARGS) {
                   argv[argc++] = cp;
                   cp = strchr(cp, ' ');
                   if (cp == NULL)
                         break;                          break;
                 case (1):                  *cp++ = '\0';
                         *wflags |= WARN_WERR;                  while (*cp == ' ')
                           cp++;
                   if (*cp == '\0')
                         break;                          break;
                 default:  
                         fprintf(stderr, "%s: Bad argument\n", o);  
                         return(0);  
                 }  
         }          }
   
         return(1);          /* For less(1), use the tag file. */
 }  
   
           use_ofn = 1;
   #if HAVE_LESS_T
           if ((cmdlen = strlen(argv[0])) >= 4) {
                   cp = argv[0] + cmdlen - 4;
                   if (strcmp(cp, "less") == 0) {
                           argv[argc++] = mandoc_strdup("-T");
                           argv[argc++] = tag_files->tfn;
                           if (tag_files->tagname != NULL) {
                                   argv[argc++] = mandoc_strdup("-t");
                                   argv[argc++] = tag_files->tagname;
                                   use_ofn = 0;
                           }
                   }
           }
   #endif
           if (use_ofn)
                   argv[argc++] = tag_files->ofn;
           argv[argc] = NULL;
   
 static int          switch (pager_pid = fork()) {
 mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)          case -1:
 {                  mandoc_msg(MANDOCERR_FORK, 0, 0, "%s", strerror(errno));
         struct curparse *cp;                  exit(mandoc_msg_getrc());
         const char *level;          case 0:
         int rc;                  break;
           default:
                   (void)setpgid(pager_pid, 0);
                   (void)tcsetpgrp(tag_files->ofd, pager_pid);
   #if HAVE_PLEDGE
                   if (pledge("stdio rpath tmppath tty proc", NULL) == -1) {
                           mandoc_msg(MANDOCERR_PLEDGE, 0, 0,
                               "%s", strerror(errno));
                           exit(mandoc_msg_getrc());
                   }
   #endif
                   tag_files->pager_pid = pager_pid;
                   return pager_pid;
           }
   
         cp = (struct curparse *)arg;          /* The child process becomes the pager. */
         level = NULL;  
         rc = 1;  
   
         if (t >= MANDOCERR_FATAL) {          if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) {
                 with_fatal = 1;                  mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno));
                 level = "FATAL";                  _exit(mandoc_msg_getrc());
                 rc = 0;  
         } else {  
                 if ( ! (WARN_WALL & cp->wflags))  
                         return(1);  
                 if (t >= MANDOCERR_ERROR) {  
                         with_error = 1;  
                         level = "ERROR";  
                 }  
                 if (WARN_WERR & cp->wflags) {  
                         with_fatal = 1;  
                         rc = 0;  
                 }  
         }          }
           close(tag_files->ofd);
           assert(tag_files->tfd == -1);
   
         fprintf(stderr, "%s:%d:%d:", cp->file, ln, col + 1);          /* Do not start the pager before controlling the terminal. */
         if (level)  
                 fprintf(stderr, " %s:", level);  
         fprintf(stderr, " %s", mandocerrs[t]);  
         if (msg)  
                 fprintf(stderr, ": %s", msg);  
         fputc('\n', stderr);  
   
         return(rc);          while (tcgetpgrp(STDOUT_FILENO) != getpid())
                   nanosleep(&timeout, NULL);
   
           execvp(argv[0], argv);
           mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", argv[0], strerror(errno));
           _exit(mandoc_msg_getrc());
 }  }

Legend:
Removed from v.1.96  
changed lines
  Added in v.1.329

CVSweb