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