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 }