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