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