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