1241675Suqs/* $Id: main.c,v 1.165 2011/10/06 22:29:12 kristaps Exp $ */ 2241675Suqs/* 3241675Suqs * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4241675Suqs * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org> 5241675Suqs * 6241675Suqs * Permission to use, copy, modify, and distribute this software for any 7241675Suqs * purpose with or without fee is hereby granted, provided that the above 8241675Suqs * copyright notice and this permission notice appear in all copies. 9241675Suqs * 10241675Suqs * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11241675Suqs * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12241675Suqs * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13241675Suqs * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14241675Suqs * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15241675Suqs * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16241675Suqs * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17241675Suqs */ 18241675Suqs#ifdef HAVE_CONFIG_H 19241675Suqs#include "config.h" 20241675Suqs#endif 21241675Suqs 22241675Suqs#include <assert.h> 23241675Suqs#include <stdio.h> 24241675Suqs#include <stdint.h> 25241675Suqs#include <stdlib.h> 26241675Suqs#include <string.h> 27241675Suqs#include <unistd.h> 28241675Suqs 29241675Suqs#include "mandoc.h" 30241675Suqs#include "main.h" 31241675Suqs#include "mdoc.h" 32241675Suqs#include "man.h" 33241675Suqs 34241675Suqs#if !defined(__GNUC__) || (__GNUC__ < 2) 35241675Suqs# if !defined(lint) 36241675Suqs# define __attribute__(x) 37241675Suqs# endif 38241675Suqs#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ 39241675Suqs 40241675Suqstypedef void (*out_mdoc)(void *, const struct mdoc *); 41241675Suqstypedef void (*out_man)(void *, const struct man *); 42241675Suqstypedef void (*out_free)(void *); 43241675Suqs 44241675Suqsenum outt { 45241675Suqs OUTT_ASCII = 0, /* -Tascii */ 46241675Suqs OUTT_LOCALE, /* -Tlocale */ 47241675Suqs OUTT_UTF8, /* -Tutf8 */ 48241675Suqs OUTT_TREE, /* -Ttree */ 49241675Suqs OUTT_MAN, /* -Tman */ 50241675Suqs OUTT_HTML, /* -Thtml */ 51241675Suqs OUTT_XHTML, /* -Txhtml */ 52241675Suqs OUTT_LINT, /* -Tlint */ 53241675Suqs OUTT_PS, /* -Tps */ 54241675Suqs OUTT_PDF /* -Tpdf */ 55241675Suqs}; 56241675Suqs 57241675Suqsstruct curparse { 58241675Suqs struct mparse *mp; 59241675Suqs enum mandoclevel wlevel; /* ignore messages below this */ 60241675Suqs int wstop; /* stop after a file with a warning */ 61241675Suqs enum outt outtype; /* which output to use */ 62241675Suqs out_mdoc outmdoc; /* mdoc output ptr */ 63241675Suqs out_man outman; /* man output ptr */ 64241675Suqs out_free outfree; /* free output ptr */ 65241675Suqs void *outdata; /* data for output */ 66241675Suqs char outopts[BUFSIZ]; /* buf of output opts */ 67241675Suqs}; 68241675Suqs 69241675Suqsstatic int moptions(enum mparset *, char *); 70241675Suqsstatic void mmsg(enum mandocerr, enum mandoclevel, 71241675Suqs const char *, int, int, const char *); 72241675Suqsstatic void parse(struct curparse *, int, 73241675Suqs const char *, enum mandoclevel *); 74241675Suqsstatic int toptions(struct curparse *, char *); 75241675Suqsstatic void usage(void) __attribute__((noreturn)); 76241675Suqsstatic void version(void) __attribute__((noreturn)); 77241675Suqsstatic int woptions(struct curparse *, char *); 78241675Suqs 79241675Suqsstatic const char *progname; 80241675Suqs 81241675Suqsint 82241675Suqsmain(int argc, char *argv[]) 83241675Suqs{ 84241675Suqs int c; 85241675Suqs struct curparse curp; 86241675Suqs enum mparset type; 87241675Suqs enum mandoclevel rc; 88241675Suqs 89241675Suqs progname = strrchr(argv[0], '/'); 90241675Suqs if (progname == NULL) 91241675Suqs progname = argv[0]; 92241675Suqs else 93241675Suqs ++progname; 94241675Suqs 95241675Suqs memset(&curp, 0, sizeof(struct curparse)); 96241675Suqs 97241675Suqs type = MPARSE_AUTO; 98241675Suqs curp.outtype = OUTT_ASCII; 99241675Suqs curp.wlevel = MANDOCLEVEL_FATAL; 100241675Suqs 101241675Suqs /* LINTED */ 102241675Suqs while (-1 != (c = getopt(argc, argv, "m:O:T:VW:"))) 103241675Suqs switch (c) { 104241675Suqs case ('m'): 105241675Suqs if ( ! moptions(&type, optarg)) 106241675Suqs return((int)MANDOCLEVEL_BADARG); 107241675Suqs break; 108241675Suqs case ('O'): 109241675Suqs (void)strlcat(curp.outopts, optarg, BUFSIZ); 110241675Suqs (void)strlcat(curp.outopts, ",", BUFSIZ); 111241675Suqs break; 112241675Suqs case ('T'): 113241675Suqs if ( ! toptions(&curp, optarg)) 114241675Suqs return((int)MANDOCLEVEL_BADARG); 115241675Suqs break; 116241675Suqs case ('W'): 117241675Suqs if ( ! woptions(&curp, optarg)) 118241675Suqs return((int)MANDOCLEVEL_BADARG); 119241675Suqs break; 120241675Suqs case ('V'): 121241675Suqs version(); 122241675Suqs /* NOTREACHED */ 123241675Suqs default: 124241675Suqs usage(); 125241675Suqs /* NOTREACHED */ 126241675Suqs } 127241675Suqs 128241675Suqs curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp); 129241675Suqs 130241675Suqs /* 131241675Suqs * Conditionally start up the lookaside buffer before parsing. 132241675Suqs */ 133241675Suqs if (OUTT_MAN == curp.outtype) 134241675Suqs mparse_keep(curp.mp); 135241675Suqs 136241675Suqs argc -= optind; 137241675Suqs argv += optind; 138241675Suqs 139241675Suqs rc = MANDOCLEVEL_OK; 140241675Suqs 141241675Suqs if (NULL == *argv) 142241675Suqs parse(&curp, STDIN_FILENO, "<stdin>", &rc); 143241675Suqs 144241675Suqs while (*argv) { 145241675Suqs parse(&curp, -1, *argv, &rc); 146241675Suqs if (MANDOCLEVEL_OK != rc && curp.wstop) 147241675Suqs break; 148241675Suqs ++argv; 149241675Suqs } 150241675Suqs 151241675Suqs if (curp.outfree) 152241675Suqs (*curp.outfree)(curp.outdata); 153241675Suqs if (curp.mp) 154241675Suqs mparse_free(curp.mp); 155241675Suqs 156241675Suqs return((int)rc); 157241675Suqs} 158241675Suqs 159241675Suqsstatic void 160241675Suqsversion(void) 161241675Suqs{ 162241675Suqs 163241675Suqs printf("%s %s\n", progname, VERSION); 164241675Suqs exit((int)MANDOCLEVEL_OK); 165241675Suqs} 166241675Suqs 167241675Suqsstatic void 168241675Suqsusage(void) 169241675Suqs{ 170241675Suqs 171241675Suqs fprintf(stderr, "usage: %s " 172241675Suqs "[-V] " 173241675Suqs "[-foption] " 174241675Suqs "[-mformat] " 175241675Suqs "[-Ooption] " 176241675Suqs "[-Toutput] " 177241675Suqs "[-Wlevel] " 178241675Suqs "[file...]\n", 179241675Suqs progname); 180241675Suqs 181241675Suqs exit((int)MANDOCLEVEL_BADARG); 182241675Suqs} 183241675Suqs 184241675Suqsstatic void 185241675Suqsparse(struct curparse *curp, int fd, 186241675Suqs const char *file, enum mandoclevel *level) 187241675Suqs{ 188241675Suqs enum mandoclevel rc; 189241675Suqs struct mdoc *mdoc; 190241675Suqs struct man *man; 191241675Suqs 192241675Suqs /* Begin by parsing the file itself. */ 193241675Suqs 194241675Suqs assert(file); 195241675Suqs assert(fd >= -1); 196241675Suqs 197241675Suqs rc = mparse_readfd(curp->mp, fd, file); 198241675Suqs 199241675Suqs /* Stop immediately if the parse has failed. */ 200241675Suqs 201241675Suqs if (MANDOCLEVEL_FATAL <= rc) 202241675Suqs goto cleanup; 203241675Suqs 204241675Suqs /* 205241675Suqs * With -Wstop and warnings or errors of at least the requested 206241675Suqs * level, do not produce output. 207241675Suqs */ 208241675Suqs 209241675Suqs if (MANDOCLEVEL_OK != rc && curp->wstop) 210241675Suqs goto cleanup; 211241675Suqs 212241675Suqs /* If unset, allocate output dev now (if applicable). */ 213241675Suqs 214241675Suqs if ( ! (curp->outman && curp->outmdoc)) { 215241675Suqs switch (curp->outtype) { 216241675Suqs case (OUTT_XHTML): 217241675Suqs curp->outdata = xhtml_alloc(curp->outopts); 218241675Suqs curp->outfree = html_free; 219241675Suqs break; 220241675Suqs case (OUTT_HTML): 221241675Suqs curp->outdata = html_alloc(curp->outopts); 222241675Suqs curp->outfree = html_free; 223241675Suqs break; 224241675Suqs case (OUTT_UTF8): 225241675Suqs curp->outdata = utf8_alloc(curp->outopts); 226241675Suqs curp->outfree = ascii_free; 227241675Suqs break; 228241675Suqs case (OUTT_LOCALE): 229241675Suqs curp->outdata = locale_alloc(curp->outopts); 230241675Suqs curp->outfree = ascii_free; 231241675Suqs break; 232241675Suqs case (OUTT_ASCII): 233241675Suqs curp->outdata = ascii_alloc(curp->outopts); 234241675Suqs curp->outfree = ascii_free; 235241675Suqs break; 236241675Suqs case (OUTT_PDF): 237241675Suqs curp->outdata = pdf_alloc(curp->outopts); 238241675Suqs curp->outfree = pspdf_free; 239241675Suqs break; 240241675Suqs case (OUTT_PS): 241241675Suqs curp->outdata = ps_alloc(curp->outopts); 242241675Suqs curp->outfree = pspdf_free; 243241675Suqs break; 244241675Suqs default: 245241675Suqs break; 246241675Suqs } 247241675Suqs 248241675Suqs switch (curp->outtype) { 249241675Suqs case (OUTT_HTML): 250241675Suqs /* FALLTHROUGH */ 251241675Suqs case (OUTT_XHTML): 252241675Suqs curp->outman = html_man; 253241675Suqs curp->outmdoc = html_mdoc; 254241675Suqs break; 255241675Suqs case (OUTT_TREE): 256241675Suqs curp->outman = tree_man; 257241675Suqs curp->outmdoc = tree_mdoc; 258241675Suqs break; 259241675Suqs case (OUTT_MAN): 260241675Suqs curp->outmdoc = man_mdoc; 261241675Suqs curp->outman = man_man; 262241675Suqs break; 263241675Suqs case (OUTT_PDF): 264241675Suqs /* FALLTHROUGH */ 265241675Suqs case (OUTT_ASCII): 266241675Suqs /* FALLTHROUGH */ 267241675Suqs case (OUTT_UTF8): 268241675Suqs /* FALLTHROUGH */ 269241675Suqs case (OUTT_LOCALE): 270241675Suqs /* FALLTHROUGH */ 271241675Suqs case (OUTT_PS): 272241675Suqs curp->outman = terminal_man; 273241675Suqs curp->outmdoc = terminal_mdoc; 274241675Suqs break; 275241675Suqs default: 276241675Suqs break; 277241675Suqs } 278241675Suqs } 279241675Suqs 280241675Suqs mparse_result(curp->mp, &mdoc, &man); 281241675Suqs 282241675Suqs /* Execute the out device, if it exists. */ 283241675Suqs 284241675Suqs if (man && curp->outman) 285241675Suqs (*curp->outman)(curp->outdata, man); 286241675Suqs if (mdoc && curp->outmdoc) 287241675Suqs (*curp->outmdoc)(curp->outdata, mdoc); 288241675Suqs 289241675Suqs cleanup: 290241675Suqs 291241675Suqs mparse_reset(curp->mp); 292241675Suqs 293241675Suqs if (*level < rc) 294241675Suqs *level = rc; 295241675Suqs} 296241675Suqs 297241675Suqsstatic int 298241675Suqsmoptions(enum mparset *tflags, char *arg) 299241675Suqs{ 300241675Suqs 301241675Suqs if (0 == strcmp(arg, "doc")) 302241675Suqs *tflags = MPARSE_MDOC; 303241675Suqs else if (0 == strcmp(arg, "andoc")) 304241675Suqs *tflags = MPARSE_AUTO; 305241675Suqs else if (0 == strcmp(arg, "an")) 306241675Suqs *tflags = MPARSE_MAN; 307241675Suqs else { 308241675Suqs fprintf(stderr, "%s: Bad argument\n", arg); 309241675Suqs return(0); 310241675Suqs } 311241675Suqs 312241675Suqs return(1); 313241675Suqs} 314241675Suqs 315241675Suqsstatic int 316241675Suqstoptions(struct curparse *curp, char *arg) 317241675Suqs{ 318241675Suqs 319241675Suqs if (0 == strcmp(arg, "ascii")) 320241675Suqs curp->outtype = OUTT_ASCII; 321241675Suqs else if (0 == strcmp(arg, "lint")) { 322241675Suqs curp->outtype = OUTT_LINT; 323241675Suqs curp->wlevel = MANDOCLEVEL_WARNING; 324241675Suqs } else if (0 == strcmp(arg, "tree")) 325241675Suqs curp->outtype = OUTT_TREE; 326241675Suqs else if (0 == strcmp(arg, "man")) 327241675Suqs curp->outtype = OUTT_MAN; 328241675Suqs else if (0 == strcmp(arg, "html")) 329241675Suqs curp->outtype = OUTT_HTML; 330241675Suqs else if (0 == strcmp(arg, "utf8")) 331241675Suqs curp->outtype = OUTT_UTF8; 332241675Suqs else if (0 == strcmp(arg, "locale")) 333241675Suqs curp->outtype = OUTT_LOCALE; 334241675Suqs else if (0 == strcmp(arg, "xhtml")) 335241675Suqs curp->outtype = OUTT_XHTML; 336241675Suqs else if (0 == strcmp(arg, "ps")) 337241675Suqs curp->outtype = OUTT_PS; 338241675Suqs else if (0 == strcmp(arg, "pdf")) 339241675Suqs curp->outtype = OUTT_PDF; 340241675Suqs else { 341241675Suqs fprintf(stderr, "%s: Bad argument\n", arg); 342241675Suqs return(0); 343241675Suqs } 344241675Suqs 345241675Suqs return(1); 346241675Suqs} 347241675Suqs 348241675Suqsstatic int 349241675Suqswoptions(struct curparse *curp, char *arg) 350241675Suqs{ 351241675Suqs char *v, *o; 352241675Suqs const char *toks[6]; 353241675Suqs 354241675Suqs toks[0] = "stop"; 355241675Suqs toks[1] = "all"; 356241675Suqs toks[2] = "warning"; 357241675Suqs toks[3] = "error"; 358241675Suqs toks[4] = "fatal"; 359241675Suqs toks[5] = NULL; 360241675Suqs 361241675Suqs while (*arg) { 362241675Suqs o = arg; 363241675Suqs switch (getsubopt(&arg, UNCONST(toks), &v)) { 364241675Suqs case (0): 365241675Suqs curp->wstop = 1; 366241675Suqs break; 367241675Suqs case (1): 368241675Suqs /* FALLTHROUGH */ 369241675Suqs case (2): 370241675Suqs curp->wlevel = MANDOCLEVEL_WARNING; 371241675Suqs break; 372241675Suqs case (3): 373241675Suqs curp->wlevel = MANDOCLEVEL_ERROR; 374241675Suqs break; 375241675Suqs case (4): 376241675Suqs curp->wlevel = MANDOCLEVEL_FATAL; 377241675Suqs break; 378241675Suqs default: 379241675Suqs fprintf(stderr, "-W%s: Bad argument\n", o); 380241675Suqs return(0); 381241675Suqs } 382241675Suqs } 383241675Suqs 384241675Suqs return(1); 385241675Suqs} 386241675Suqs 387241675Suqsstatic void 388241675Suqsmmsg(enum mandocerr t, enum mandoclevel lvl, 389241675Suqs const char *file, int line, int col, const char *msg) 390241675Suqs{ 391241675Suqs 392241675Suqs fprintf(stderr, "%s:%d:%d: %s: %s", 393241675Suqs file, line, col + 1, 394241675Suqs mparse_strlevel(lvl), 395241675Suqs mparse_strerror(t)); 396241675Suqs 397241675Suqs if (msg) 398241675Suqs fprintf(stderr, ": %s", msg); 399241675Suqs 400241675Suqs fputc('\n', stderr); 401241675Suqs} 402