1 /* $Id: main.c,v 1.163 2011/05/20 15:51:18 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_HTML, /* -Thtml */ 50 OUTT_XHTML, /* -Txhtml */ 51 OUTT_LINT, /* -Tlint */ 52 OUTT_PS, /* -Tps */ 53 OUTT_PDF /* -Tpdf */ 54 }; 55 56 struct curparse { 57 struct mparse *mp; 58 enum mandoclevel wlevel; /* ignore messages below this */ 59 int wstop; /* stop after a file with a warning */ 60 enum outt outtype; /* which output to use */ 61 out_mdoc outmdoc; /* mdoc output ptr */ 62 out_man outman; /* man output ptr */ 63 out_free outfree; /* free output ptr */ 64 void *outdata; /* data for output */ 65 char outopts[BUFSIZ]; /* buf of output opts */ 66 }; 67 68 static int moptions(enum mparset *, char *); 69 static void mmsg(enum mandocerr, enum mandoclevel, 70 const char *, int, int, const char *); 71 static void parse(struct curparse *, int, 72 const char *, enum mandoclevel *); 73 static int toptions(struct curparse *, char *); 74 static void usage(void) __attribute__((noreturn)); 75 static void version(void) __attribute__((noreturn)); 76 static int woptions(struct curparse *, char *); 77 78 static const char *progname; 79 80 int 81 main(int argc, char *argv[]) 82 { 83 int c; 84 struct curparse curp; 85 enum mparset type; 86 enum mandoclevel rc; 87 88 progname = strrchr(argv[0], '/'); 89 if (progname == NULL) 90 progname = argv[0]; 91 else 92 ++progname; 93 94 memset(&curp, 0, sizeof(struct curparse)); 95 96 type = MPARSE_AUTO; 97 curp.outtype = OUTT_ASCII; 98 curp.wlevel = MANDOCLEVEL_FATAL; 99 100 /* LINTED */ 101 while (-1 != (c = getopt(argc, argv, "m:O:T:VW:"))) 102 switch (c) { 103 case ('m'): 104 if ( ! moptions(&type, optarg)) 105 return((int)MANDOCLEVEL_BADARG); 106 break; 107 case ('O'): 108 (void)strlcat(curp.outopts, optarg, BUFSIZ); 109 (void)strlcat(curp.outopts, ",", BUFSIZ); 110 break; 111 case ('T'): 112 if ( ! toptions(&curp, optarg)) 113 return((int)MANDOCLEVEL_BADARG); 114 break; 115 case ('W'): 116 if ( ! woptions(&curp, optarg)) 117 return((int)MANDOCLEVEL_BADARG); 118 break; 119 case ('V'): 120 version(); 121 /* NOTREACHED */ 122 default: 123 usage(); 124 /* NOTREACHED */ 125 } 126 127 curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp); 128 129 argc -= optind; 130 argv += optind; 131 132 rc = MANDOCLEVEL_OK; 133 134 if (NULL == *argv) 135 parse(&curp, STDIN_FILENO, "<stdin>", &rc); 136 137 while (*argv) { 138 parse(&curp, -1, *argv, &rc); 139 if (MANDOCLEVEL_OK != rc && curp.wstop) 140 break; 141 ++argv; 142 } 143 144 if (curp.outfree) 145 (*curp.outfree)(curp.outdata); 146 if (curp.mp) 147 mparse_free(curp.mp); 148 149 return((int)rc); 150 } 151 152 static void 153 version(void) 154 { 155 156 printf("%s %s\n", progname, VERSION); 157 exit((int)MANDOCLEVEL_OK); 158 } 159 160 static void 161 usage(void) 162 { 163 164 fprintf(stderr, "usage: %s " 165 "[-V] " 166 "[-foption] " 167 "[-mformat] " 168 "[-Ooption] " 169 "[-Toutput] " 170 "[-Wlevel] " 171 "[file...]\n", 172 progname); 173 174 exit((int)MANDOCLEVEL_BADARG); 175 } 176 177 static void 178 parse(struct curparse *curp, int fd, 179 const char *file, enum mandoclevel *level) 180 { 181 enum mandoclevel rc; 182 struct mdoc *mdoc; 183 struct man *man; 184 185 /* Begin by parsing the file itself. */ 186 187 assert(file); 188 assert(fd >= -1); 189 190 rc = mparse_readfd(curp->mp, fd, file); 191 192 /* Stop immediately if the parse has failed. */ 193 194 if (MANDOCLEVEL_FATAL <= rc) 195 goto cleanup; 196 197 /* 198 * With -Wstop and warnings or errors of at least the requested 199 * level, do not produce output. 200 */ 201 202 if (MANDOCLEVEL_OK != rc && curp->wstop) 203 goto cleanup; 204 205 /* If unset, allocate output dev now (if applicable). */ 206 207 if ( ! (curp->outman && curp->outmdoc)) { 208 switch (curp->outtype) { 209 case (OUTT_XHTML): 210 curp->outdata = xhtml_alloc(curp->outopts); 211 curp->outfree = html_free; 212 break; 213 case (OUTT_HTML): 214 curp->outdata = html_alloc(curp->outopts); 215 curp->outfree = html_free; 216 break; 217 case (OUTT_UTF8): 218 curp->outdata = utf8_alloc(curp->outopts); 219 curp->outfree = ascii_free; 220 break; 221 case (OUTT_LOCALE): 222 curp->outdata = locale_alloc(curp->outopts); 223 curp->outfree = ascii_free; 224 break; 225 case (OUTT_ASCII): 226 curp->outdata = ascii_alloc(curp->outopts); 227 curp->outfree = ascii_free; 228 break; 229 case (OUTT_PDF): 230 curp->outdata = pdf_alloc(curp->outopts); 231 curp->outfree = pspdf_free; 232 break; 233 case (OUTT_PS): 234 curp->outdata = ps_alloc(curp->outopts); 235 curp->outfree = pspdf_free; 236 break; 237 default: 238 break; 239 } 240 241 switch (curp->outtype) { 242 case (OUTT_HTML): 243 /* FALLTHROUGH */ 244 case (OUTT_XHTML): 245 curp->outman = html_man; 246 curp->outmdoc = html_mdoc; 247 break; 248 case (OUTT_TREE): 249 curp->outman = tree_man; 250 curp->outmdoc = tree_mdoc; 251 break; 252 case (OUTT_PDF): 253 /* FALLTHROUGH */ 254 case (OUTT_ASCII): 255 /* FALLTHROUGH */ 256 case (OUTT_UTF8): 257 /* FALLTHROUGH */ 258 case (OUTT_LOCALE): 259 /* FALLTHROUGH */ 260 case (OUTT_PS): 261 curp->outman = terminal_man; 262 curp->outmdoc = terminal_mdoc; 263 break; 264 default: 265 break; 266 } 267 } 268 269 mparse_result(curp->mp, &mdoc, &man); 270 271 /* Execute the out device, if it exists. */ 272 273 if (man && curp->outman) 274 (*curp->outman)(curp->outdata, man); 275 if (mdoc && curp->outmdoc) 276 (*curp->outmdoc)(curp->outdata, mdoc); 277 278 cleanup: 279 280 mparse_reset(curp->mp); 281 282 if (*level < rc) 283 *level = rc; 284 } 285 286 static int 287 moptions(enum mparset *tflags, char *arg) 288 { 289 290 if (0 == strcmp(arg, "doc")) 291 *tflags = MPARSE_MDOC; 292 else if (0 == strcmp(arg, "andoc")) 293 *tflags = MPARSE_AUTO; 294 else if (0 == strcmp(arg, "an")) 295 *tflags = MPARSE_MAN; 296 else { 297 fprintf(stderr, "%s: Bad argument\n", arg); 298 return(0); 299 } 300 301 return(1); 302 } 303 304 static int 305 toptions(struct curparse *curp, char *arg) 306 { 307 308 if (0 == strcmp(arg, "ascii")) 309 curp->outtype = OUTT_ASCII; 310 else if (0 == strcmp(arg, "lint")) { 311 curp->outtype = OUTT_LINT; 312 curp->wlevel = MANDOCLEVEL_WARNING; 313 } else if (0 == strcmp(arg, "tree")) 314 curp->outtype = OUTT_TREE; 315 else if (0 == strcmp(arg, "html")) 316 curp->outtype = OUTT_HTML; 317 else if (0 == strcmp(arg, "utf8")) 318 curp->outtype = OUTT_UTF8; 319 else if (0 == strcmp(arg, "locale")) 320 curp->outtype = OUTT_LOCALE; 321 else if (0 == strcmp(arg, "xhtml")) 322 curp->outtype = OUTT_XHTML; 323 else if (0 == strcmp(arg, "ps")) 324 curp->outtype = OUTT_PS; 325 else if (0 == strcmp(arg, "pdf")) 326 curp->outtype = OUTT_PDF; 327 else { 328 fprintf(stderr, "%s: Bad argument\n", arg); 329 return(0); 330 } 331 332 return(1); 333 } 334 335 static int 336 woptions(struct curparse *curp, char *arg) 337 { 338 char *v, *o; 339 const char *toks[6]; 340 341 toks[0] = "stop"; 342 toks[1] = "all"; 343 toks[2] = "warning"; 344 toks[3] = "error"; 345 toks[4] = "fatal"; 346 toks[5] = NULL; 347 348 while (*arg) { 349 o = arg; 350 switch (getsubopt(&arg, UNCONST(toks), &v)) { 351 case (0): 352 curp->wstop = 1; 353 break; 354 case (1): 355 /* FALLTHROUGH */ 356 case (2): 357 curp->wlevel = MANDOCLEVEL_WARNING; 358 break; 359 case (3): 360 curp->wlevel = MANDOCLEVEL_ERROR; 361 break; 362 case (4): 363 curp->wlevel = MANDOCLEVEL_FATAL; 364 break; 365 default: 366 fprintf(stderr, "-W%s: Bad argument\n", o); 367 return(0); 368 } 369 } 370 371 return(1); 372 } 373 374 static void 375 mmsg(enum mandocerr t, enum mandoclevel lvl, 376 const char *file, int line, int col, const char *msg) 377 { 378 379 fprintf(stderr, "%s:%d:%d: %s: %s", 380 file, line, col + 1, 381 mparse_strlevel(lvl), 382 mparse_strerror(t)); 383 384 if (msg) 385 fprintf(stderr, ": %s", msg); 386 387 fputc('\n', stderr); 388 } 389