1 /*      $Id: main.c,v 1.165 2011/10/06 22:29:12 kristaps Exp $ */
   2 /*
   3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
   4  * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
   5  *
   6  * Permission to use, copy, modify, and distribute this software for any
   7  * purpose with or without fee is hereby granted, provided that the above
   8  * copyright notice and this permission notice appear in all copies.
   9  *
  10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17  */
  18 #ifdef HAVE_CONFIG_H
  19 #include "config.h"
  20 #endif
  21 
  22 #include <assert.h>
  23 #include <stdio.h>
  24 #include <stdint.h>
  25 #include <stdlib.h>
  26 #include <string.h>
  27 #include <unistd.h>
  28 
  29 #include "mandoc.h"
  30 #include "main.h"
  31 #include "mdoc.h"
  32 #include "man.h"
  33 
  34 #if !defined(__GNUC__) || (__GNUC__ < 2)
  35 # if !defined(lint)
  36 #  define __attribute__(x)
  37 # endif
  38 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
  39 
  40 typedef void            (*out_mdoc)(void *, const struct mdoc *);
  41 typedef void            (*out_man)(void *, const struct man *);
  42 typedef void            (*out_free)(void *);
  43 
  44 enum    outt {
  45         OUTT_ASCII = 0, /* -Tascii */
  46         OUTT_LOCALE,    /* -Tlocale */
  47         OUTT_UTF8,      /* -Tutf8 */
  48         OUTT_TREE,      /* -Ttree */
  49         OUTT_MAN,       /* -Tman */
  50         OUTT_HTML,      /* -Thtml */
  51         OUTT_XHTML,     /* -Txhtml */
  52         OUTT_LINT,      /* -Tlint */
  53         OUTT_PS,        /* -Tps */
  54         OUTT_PDF        /* -Tpdf */
  55 };
  56 
  57 struct  curparse {
  58         struct mparse    *mp;
  59         enum mandoclevel  wlevel;       /* ignore messages below this */
  60         int               wstop;        /* stop after a file with a warning */
  61         enum outt         outtype;      /* which output to use */
  62         out_mdoc          outmdoc;      /* mdoc output ptr */
  63         out_man           outman;       /* man output ptr */
  64         out_free          outfree;      /* free output ptr */
  65         void             *outdata;      /* data for output */
  66         char              outopts[BUFSIZ]; /* buf of output opts */
  67 };
  68 
  69 static  int               moptions(enum mparset *, char *);
  70 static  void              mmsg(enum mandocerr, enum mandoclevel,
  71                                 const char *, int, int, const char *);
  72 static  void              parse(struct curparse *, int, 
  73                                 const char *, enum mandoclevel *);
  74 static  int               toptions(struct curparse *, char *);
  75 static  void              usage(void) __attribute__((noreturn));
  76 static  void              version(void) __attribute__((noreturn));
  77 static  int               woptions(struct curparse *, char *);
  78 
  79 static  const char       *progname;
  80 
  81 int
  82 main(int argc, char *argv[])
  83 {
  84         int              c;
  85         struct curparse  curp;
  86         enum mparset     type;
  87         enum mandoclevel rc;
  88 
  89         progname = strrchr(argv[0], '/');
  90         if (progname == NULL)
  91                 progname = argv[0];
  92         else
  93                 ++progname;
  94 
  95         memset(&curp, 0, sizeof(struct curparse));
  96 
  97         type = MPARSE_AUTO;
  98         curp.outtype = OUTT_ASCII;
  99         curp.wlevel  = MANDOCLEVEL_FATAL;
 100 
 101         /* LINTED */
 102         while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
 103                 switch (c) {
 104                 case ('m'):
 105                         if ( ! moptions(&type, optarg))
 106                                 return((int)MANDOCLEVEL_BADARG);
 107                         break;
 108                 case ('O'):
 109                         (void)strlcat(curp.outopts, optarg, BUFSIZ);
 110                         (void)strlcat(curp.outopts, ",", BUFSIZ);
 111                         break;
 112                 case ('T'):
 113                         if ( ! toptions(&curp, optarg))
 114                                 return((int)MANDOCLEVEL_BADARG);
 115                         break;
 116                 case ('W'):
 117                         if ( ! woptions(&curp, optarg))
 118                                 return((int)MANDOCLEVEL_BADARG);
 119                         break;
 120                 case ('V'):
 121                         version();
 122                         /* NOTREACHED */
 123                 default:
 124                         usage();
 125                         /* NOTREACHED */
 126                 }
 127 
 128         curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp);
 129 
 130         /*
 131          * Conditionally start up the lookaside buffer before parsing.
 132          */
 133         if (OUTT_MAN == curp.outtype)
 134                 mparse_keep(curp.mp);
 135 
 136         argc -= optind;
 137         argv += optind;
 138 
 139         rc = MANDOCLEVEL_OK;
 140 
 141         if (NULL == *argv)
 142                 parse(&curp, STDIN_FILENO, "<stdin>", &rc);
 143 
 144         while (*argv) {
 145                 parse(&curp, -1, *argv, &rc);
 146                 if (MANDOCLEVEL_OK != rc && curp.wstop)
 147                         break;
 148                 ++argv;
 149         }
 150 
 151         if (curp.outfree)
 152                 (*curp.outfree)(curp.outdata);
 153         if (curp.mp)
 154                 mparse_free(curp.mp);
 155 
 156         return((int)rc);
 157 }
 158 
 159 static void
 160 version(void)
 161 {
 162 
 163         printf("%s %s\n", progname, VERSION);
 164         exit((int)MANDOCLEVEL_OK);
 165 }
 166 
 167 static void
 168 usage(void)
 169 {
 170 
 171         fprintf(stderr, "usage: %s "
 172                         "[-V] "
 173                         "[-foption] "
 174                         "[-mformat] "
 175                         "[-Ooption] "
 176                         "[-Toutput] "
 177                         "[-Wlevel] "
 178                         "[file...]\n", 
 179                         progname);
 180 
 181         exit((int)MANDOCLEVEL_BADARG);
 182 }
 183 
 184 static void
 185 parse(struct curparse *curp, int fd, 
 186                 const char *file, enum mandoclevel *level)
 187 {
 188         enum mandoclevel  rc;
 189         struct mdoc      *mdoc;
 190         struct man       *man;
 191 
 192         /* Begin by parsing the file itself. */
 193 
 194         assert(file);
 195         assert(fd >= -1);
 196 
 197         rc = mparse_readfd(curp->mp, fd, file);
 198 
 199         /* Stop immediately if the parse has failed. */
 200 
 201         if (MANDOCLEVEL_FATAL <= rc)
 202                 goto cleanup;
 203 
 204         /*
 205          * With -Wstop and warnings or errors of at least the requested
 206          * level, do not produce output.
 207          */
 208 
 209         if (MANDOCLEVEL_OK != rc && curp->wstop)
 210                 goto cleanup;
 211 
 212         /* If unset, allocate output dev now (if applicable). */
 213 
 214         if ( ! (curp->outman && curp->outmdoc)) {
 215                 switch (curp->outtype) {
 216                 case (OUTT_XHTML):
 217                         curp->outdata = xhtml_alloc(curp->outopts);
 218                         curp->outfree = html_free;
 219                         break;
 220                 case (OUTT_HTML):
 221                         curp->outdata = html_alloc(curp->outopts);
 222                         curp->outfree = html_free;
 223                         break;
 224                 case (OUTT_UTF8):
 225                         curp->outdata = utf8_alloc(curp->outopts);
 226                         curp->outfree = ascii_free;
 227                         break;
 228                 case (OUTT_LOCALE):
 229                         curp->outdata = locale_alloc(curp->outopts);
 230                         curp->outfree = ascii_free;
 231                         break;
 232                 case (OUTT_ASCII):
 233                         curp->outdata = ascii_alloc(curp->outopts);
 234                         curp->outfree = ascii_free;
 235                         break;
 236                 case (OUTT_PDF):
 237                         curp->outdata = pdf_alloc(curp->outopts);
 238                         curp->outfree = pspdf_free;
 239                         break;
 240                 case (OUTT_PS):
 241                         curp->outdata = ps_alloc(curp->outopts);
 242                         curp->outfree = pspdf_free;
 243                         break;
 244                 default:
 245                         break;
 246                 }
 247 
 248                 switch (curp->outtype) {
 249                 case (OUTT_HTML):
 250                         /* FALLTHROUGH */
 251                 case (OUTT_XHTML):
 252                         curp->outman = html_man;
 253                         curp->outmdoc = html_mdoc;
 254                         break;
 255                 case (OUTT_TREE):
 256                         curp->outman = tree_man;
 257                         curp->outmdoc = tree_mdoc;
 258                         break;
 259                 case (OUTT_MAN):
 260                         curp->outmdoc = man_mdoc;
 261                         curp->outman = man_man;
 262                         break;
 263                 case (OUTT_PDF):
 264                         /* FALLTHROUGH */
 265                 case (OUTT_ASCII):
 266                         /* FALLTHROUGH */
 267                 case (OUTT_UTF8):
 268                         /* FALLTHROUGH */
 269                 case (OUTT_LOCALE):
 270                         /* FALLTHROUGH */
 271                 case (OUTT_PS):
 272                         curp->outman = terminal_man;
 273                         curp->outmdoc = terminal_mdoc;
 274                         break;
 275                 default:
 276                         break;
 277                 }
 278         }
 279 
 280         mparse_result(curp->mp, &mdoc, &man);
 281 
 282         /* Execute the out device, if it exists. */
 283 
 284         if (man && curp->outman)
 285                 (*curp->outman)(curp->outdata, man);
 286         if (mdoc && curp->outmdoc)
 287                 (*curp->outmdoc)(curp->outdata, mdoc);
 288 
 289  cleanup:
 290 
 291         mparse_reset(curp->mp);
 292 
 293         if (*level < rc)
 294                 *level = rc;
 295 }
 296 
 297 static int
 298 moptions(enum mparset *tflags, char *arg)
 299 {
 300 
 301         if (0 == strcmp(arg, "doc"))
 302                 *tflags = MPARSE_MDOC;
 303         else if (0 == strcmp(arg, "andoc"))
 304                 *tflags = MPARSE_AUTO;
 305         else if (0 == strcmp(arg, "an"))
 306                 *tflags = MPARSE_MAN;
 307         else {
 308                 fprintf(stderr, "%s: Bad argument\n", arg);
 309                 return(0);
 310         }
 311 
 312         return(1);
 313 }
 314 
 315 static int
 316 toptions(struct curparse *curp, char *arg)
 317 {
 318 
 319         if (0 == strcmp(arg, "ascii"))
 320                 curp->outtype = OUTT_ASCII;
 321         else if (0 == strcmp(arg, "lint")) {
 322                 curp->outtype = OUTT_LINT;
 323                 curp->wlevel  = MANDOCLEVEL_WARNING;
 324         } else if (0 == strcmp(arg, "tree"))
 325                 curp->outtype = OUTT_TREE;
 326         else if (0 == strcmp(arg, "man"))
 327                 curp->outtype = OUTT_MAN;
 328         else if (0 == strcmp(arg, "html"))
 329                 curp->outtype = OUTT_HTML;
 330         else if (0 == strcmp(arg, "utf8"))
 331                 curp->outtype = OUTT_UTF8;
 332         else if (0 == strcmp(arg, "locale"))
 333                 curp->outtype = OUTT_LOCALE;
 334         else if (0 == strcmp(arg, "xhtml"))
 335                 curp->outtype = OUTT_XHTML;
 336         else if (0 == strcmp(arg, "ps"))
 337                 curp->outtype = OUTT_PS;
 338         else if (0 == strcmp(arg, "pdf"))
 339                 curp->outtype = OUTT_PDF;
 340         else {
 341                 fprintf(stderr, "%s: Bad argument\n", arg);
 342                 return(0);
 343         }
 344 
 345         return(1);
 346 }
 347 
 348 static int
 349 woptions(struct curparse *curp, char *arg)
 350 {
 351         char            *v, *o;
 352         const char      *toks[6]; 
 353 
 354         toks[0] = "stop";
 355         toks[1] = "all";
 356         toks[2] = "warning";
 357         toks[3] = "error";
 358         toks[4] = "fatal";
 359         toks[5] = NULL;
 360 
 361         while (*arg) {
 362                 o = arg;
 363                 switch (getsubopt(&arg, UNCONST(toks), &v)) {
 364                 case (0):
 365                         curp->wstop = 1;
 366                         break;
 367                 case (1):
 368                         /* FALLTHROUGH */
 369                 case (2):
 370                         curp->wlevel = MANDOCLEVEL_WARNING;
 371                         break;
 372                 case (3):
 373                         curp->wlevel = MANDOCLEVEL_ERROR;
 374                         break;
 375                 case (4):
 376                         curp->wlevel = MANDOCLEVEL_FATAL;
 377                         break;
 378                 default:
 379                         fprintf(stderr, "-W%s: Bad argument\n", o);
 380                         return(0);
 381                 }
 382         }
 383 
 384         return(1);
 385 }
 386 
 387 static void
 388 mmsg(enum mandocerr t, enum mandoclevel lvl, 
 389                 const char *file, int line, int col, const char *msg)
 390 {
 391 
 392         fprintf(stderr, "%s:%d:%d: %s: %s", 
 393                         file, line, col + 1, 
 394                         mparse_strlevel(lvl),
 395                         mparse_strerror(t));
 396 
 397         if (msg)
 398                 fprintf(stderr, ": %s", msg);
 399 
 400         fputc('\n', stderr);
 401 }