1 /* $Id: main.c,v 1.167 2012/11/19 17:22:26 schwarze Exp $ */ 2 /* 3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2010, 2011, 2012 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 char *defos; 89 90 progname = strrchr(argv[0], '/'); 91 if (progname == NULL) 92 progname = argv[0]; 93 else 94 ++progname; 95 96 memset(&curp, 0, sizeof(struct curparse)); 97 98 type = MPARSE_AUTO; 99 curp.outtype = OUTT_ASCII; 100 curp.wlevel = MANDOCLEVEL_FATAL; 101 defos = NULL; 102 103 /* LINTED */ 104 while (-1 != (c = getopt(argc, argv, "I:m:O:T:VW:"))) 105 switch (c) { 106 case ('I'): 107 if (strncmp(optarg, "os=", 3)) { 108 fprintf(stderr, "-I%s: Bad argument\n", 109 optarg); 110 return((int)MANDOCLEVEL_BADARG); 111 } 112 if (defos) { 113 fprintf(stderr, "-I%s: Duplicate argument\n", 114 optarg); 115 return((int)MANDOCLEVEL_BADARG); 116 } 117 defos = mandoc_strdup(optarg + 3); 118 break; 119 case ('m'): 120 if ( ! moptions(&type, optarg)) 121 return((int)MANDOCLEVEL_BADARG); 122 break; 123 case ('O'): 124 (void)strlcat(curp.outopts, optarg, BUFSIZ); 125 (void)strlcat(curp.outopts, ",", BUFSIZ); 126 break; 127 case ('T'): 128 if ( ! toptions(&curp, optarg)) 129 return((int)MANDOCLEVEL_BADARG); 130 break; 131 case ('W'): 132 if ( ! woptions(&curp, optarg)) 133 return((int)MANDOCLEVEL_BADARG); 134 break; 135 case ('V'): 136 version(); 137 /* NOTREACHED */ 138 default: 139 usage(); 140 /* NOTREACHED */ 141 } 142 143 curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp, defos); 144 145 /* 146 * Conditionally start up the lookaside buffer before parsing. 147 */ 148 if (OUTT_MAN == curp.outtype) 149 mparse_keep(curp.mp); 150 151 argc -= optind; 152 argv += optind; 153 154 rc = MANDOCLEVEL_OK; 155 156 if (NULL == *argv) 157 parse(&curp, STDIN_FILENO, "<stdin>", &rc); 158 159 while (*argv) { 160 parse(&curp, -1, *argv, &rc); 161 if (MANDOCLEVEL_OK != rc && curp.wstop) 162 break; 163 ++argv; 164 } 165 166 if (curp.outfree) 167 (*curp.outfree)(curp.outdata); 168 if (curp.mp) 169 mparse_free(curp.mp); 170 free(defos); 171 172 return((int)rc); 173 } 174 175 static void 176 version(void) 177 { 178 179 printf("%s %s\n", progname, VERSION); 180 exit((int)MANDOCLEVEL_OK); 181 } 182 183 static void 184 usage(void) 185 { 186 187 fprintf(stderr, "usage: %s " 188 "[-V] " 189 "[-Ios=name] " 190 "[-mformat] " 191 "[-Ooption] " 192 "[-Toutput] " 193 "[-Wlevel]\n" 194 "\t [file ...]\n", 195 progname); 196 197 exit((int)MANDOCLEVEL_BADARG); 198 } 199 200 static void 201 parse(struct curparse *curp, int fd, 202 const char *file, enum mandoclevel *level) 203 { 204 enum mandoclevel rc; 205 struct mdoc *mdoc; 206 struct man *man; 207 208 /* Begin by parsing the file itself. */ 209 210 assert(file); 211 assert(fd >= -1); 212 213 rc = mparse_readfd(curp->mp, fd, file); 214 215 /* Stop immediately if the parse has failed. */ 216 217 if (MANDOCLEVEL_FATAL <= rc) 218 goto cleanup; 219 220 /* 221 * With -Wstop and warnings or errors of at least the requested 222 * level, do not produce output. 223 */ 224 225 if (MANDOCLEVEL_OK != rc && curp->wstop) 226 goto cleanup; 227 228 /* If unset, allocate output dev now (if applicable). */ 229 230 if ( ! (curp->outman && curp->outmdoc)) { 231 switch (curp->outtype) { 232 case (OUTT_XHTML): 233 curp->outdata = xhtml_alloc(curp->outopts); 234 curp->outfree = html_free; 235 break; 236 case (OUTT_HTML): 237 curp->outdata = html_alloc(curp->outopts); 238 curp->outfree = html_free; 239 break; 240 case (OUTT_UTF8): 241 curp->outdata = utf8_alloc(curp->outopts); 242 curp->outfree = ascii_free; 243 break; 244 case (OUTT_LOCALE): 245 curp->outdata = locale_alloc(curp->outopts); 246 curp->outfree = ascii_free; 247 break; 248 case (OUTT_ASCII): 249 curp->outdata = ascii_alloc(curp->outopts); 250 curp->outfree = ascii_free; 251 break; 252 case (OUTT_PDF): 253 curp->outdata = pdf_alloc(curp->outopts); 254 curp->outfree = pspdf_free; 255 break; 256 case (OUTT_PS): 257 curp->outdata = ps_alloc(curp->outopts); 258 curp->outfree = pspdf_free; 259 break; 260 default: 261 break; 262 } 263 264 switch (curp->outtype) { 265 case (OUTT_HTML): 266 /* FALLTHROUGH */ 267 case (OUTT_XHTML): 268 curp->outman = html_man; 269 curp->outmdoc = html_mdoc; 270 break; 271 case (OUTT_TREE): 272 curp->outman = tree_man; 273 curp->outmdoc = tree_mdoc; 274 break; 275 case (OUTT_MAN): 276 curp->outmdoc = man_mdoc; 277 curp->outman = man_man; 278 break; 279 case (OUTT_PDF): 280 /* FALLTHROUGH */ 281 case (OUTT_ASCII): 282 /* FALLTHROUGH */ 283 case (OUTT_UTF8): 284 /* FALLTHROUGH */ 285 case (OUTT_LOCALE): 286 /* FALLTHROUGH */ 287 case (OUTT_PS): 288 curp->outman = terminal_man; 289 curp->outmdoc = terminal_mdoc; 290 break; 291 default: 292 break; 293 } 294 } 295 296 mparse_result(curp->mp, &mdoc, &man); 297 298 /* Execute the out device, if it exists. */ 299 300 if (man && curp->outman) 301 (*curp->outman)(curp->outdata, man); 302 if (mdoc && curp->outmdoc) 303 (*curp->outmdoc)(curp->outdata, mdoc); 304 305 cleanup: 306 307 mparse_reset(curp->mp); 308 309 if (*level < rc) 310 *level = rc; 311 } 312 313 static int 314 moptions(enum mparset *tflags, char *arg) 315 { 316 317 if (0 == strcmp(arg, "doc")) 318 *tflags = MPARSE_MDOC; 319 else if (0 == strcmp(arg, "andoc")) 320 *tflags = MPARSE_AUTO; 321 else if (0 == strcmp(arg, "an")) 322 *tflags = MPARSE_MAN; 323 else { 324 fprintf(stderr, "%s: Bad argument\n", arg); 325 return(0); 326 } 327 328 return(1); 329 } 330 331 static int 332 toptions(struct curparse *curp, char *arg) 333 { 334 335 if (0 == strcmp(arg, "ascii")) 336 curp->outtype = OUTT_ASCII; 337 else if (0 == strcmp(arg, "lint")) { 338 curp->outtype = OUTT_LINT; 339 curp->wlevel = MANDOCLEVEL_WARNING; 340 } else if (0 == strcmp(arg, "tree")) 341 curp->outtype = OUTT_TREE; 342 else if (0 == strcmp(arg, "man")) 343 curp->outtype = OUTT_MAN; 344 else if (0 == strcmp(arg, "html")) 345 curp->outtype = OUTT_HTML; 346 else if (0 == strcmp(arg, "utf8")) 347 curp->outtype = OUTT_UTF8; 348 else if (0 == strcmp(arg, "locale")) 349 curp->outtype = OUTT_LOCALE; 350 else if (0 == strcmp(arg, "xhtml")) 351 curp->outtype = OUTT_XHTML; 352 else if (0 == strcmp(arg, "ps")) 353 curp->outtype = OUTT_PS; 354 else if (0 == strcmp(arg, "pdf")) 355 curp->outtype = OUTT_PDF; 356 else { 357 fprintf(stderr, "%s: Bad argument\n", arg); 358 return(0); 359 } 360 361 return(1); 362 } 363 364 static int 365 woptions(struct curparse *curp, char *arg) 366 { 367 char *v, *o; 368 const char *toks[6]; 369 370 toks[0] = "stop"; 371 toks[1] = "all"; 372 toks[2] = "warning"; 373 toks[3] = "error"; 374 toks[4] = "fatal"; 375 toks[5] = NULL; 376 377 while (*arg) { 378 o = arg; 379 switch (getsubopt(&arg, UNCONST(toks), &v)) { 380 case (0): 381 curp->wstop = 1; 382 break; 383 case (1): 384 /* FALLTHROUGH */ 385 case (2): 386 curp->wlevel = MANDOCLEVEL_WARNING; 387 break; 388 case (3): 389 curp->wlevel = MANDOCLEVEL_ERROR; 390 break; 391 case (4): 392 curp->wlevel = MANDOCLEVEL_FATAL; 393 break; 394 default: 395 fprintf(stderr, "-W%s: Bad argument\n", o); 396 return(0); 397 } 398 } 399 400 return(1); 401 } 402 403 static void 404 mmsg(enum mandocerr t, enum mandoclevel lvl, 405 const char *file, int line, int col, const char *msg) 406 { 407 408 fprintf(stderr, "%s:%d:%d: %s: %s", 409 file, line, col + 1, 410 mparse_strlevel(lvl), 411 mparse_strerror(t)); 412 413 if (msg) 414 fprintf(stderr, ": %s", msg); 415 416 fputc('\n', stderr); 417 }