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