Annotation of mandoc/main.c, Revision 1.202
1.202 ! schwarze 1: /* $Id: main.c,v 1.201 2014/12/02 11:31:51 schwarze Exp $ */
1.1 kristaps 2: /*
1.183 schwarze 3: * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
1.170 schwarze 4: * Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze <schwarze@openbsd.org>
1.169 schwarze 5: * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
1.1 kristaps 6: *
7: * Permission to use, copy, modify, and distribute this software for any
1.25 kristaps 8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 10: *
1.25 kristaps 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.
1.1 kristaps 18: */
1.58 kristaps 19: #include "config.h"
1.178 schwarze 20:
21: #include <sys/types.h>
1.58 kristaps 22:
1.1 kristaps 23: #include <assert.h>
1.185 schwarze 24: #include <ctype.h>
1.183 schwarze 25: #include <errno.h>
1.186 schwarze 26: #include <fcntl.h>
1.1 kristaps 27: #include <stdio.h>
1.45 kristaps 28: #include <stdint.h>
1.1 kristaps 29: #include <stdlib.h>
30: #include <string.h>
31: #include <unistd.h>
32:
1.71 kristaps 33: #include "mandoc.h"
1.172 schwarze 34: #include "mandoc_aux.h"
1.90 kristaps 35: #include "main.h"
1.1 kristaps 36: #include "mdoc.h"
1.10 kristaps 37: #include "man.h"
1.180 schwarze 38: #include "manpath.h"
39: #include "mansearch.h"
1.101 joerg 40:
1.55 kristaps 41: #if !defined(__GNUC__) || (__GNUC__ < 2)
1.56 kristaps 42: # if !defined(lint)
43: # define __attribute__(x)
44: # endif
1.55 kristaps 45: #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
1.16 kristaps 46:
1.181 schwarze 47: enum outmode {
48: OUTMODE_DEF = 0,
49: OUTMODE_FLN,
50: OUTMODE_LST,
51: OUTMODE_ALL,
52: OUTMODE_INT,
53: OUTMODE_ONE
54: };
55:
1.42 kristaps 56: typedef void (*out_mdoc)(void *, const struct mdoc *);
57: typedef void (*out_man)(void *, const struct man *);
1.22 kristaps 58: typedef void (*out_free)(void *);
59:
1.19 kristaps 60: enum outt {
1.154 kristaps 61: OUTT_ASCII = 0, /* -Tascii */
1.162 kristaps 62: OUTT_LOCALE, /* -Tlocale */
1.163 kristaps 63: OUTT_UTF8, /* -Tutf8 */
1.154 kristaps 64: OUTT_TREE, /* -Ttree */
1.164 schwarze 65: OUTT_MAN, /* -Tman */
1.154 kristaps 66: OUTT_HTML, /* -Thtml */
67: OUTT_LINT, /* -Tlint */
68: OUTT_PS, /* -Tps */
69: OUTT_PDF /* -Tpdf */
1.19 kristaps 70: };
71:
1.5 kristaps 72: struct curparse {
1.154 kristaps 73: struct mparse *mp;
1.195 schwarze 74: struct mchars *mchars; /* character table */
1.148 kristaps 75: enum mandoclevel wlevel; /* ignore messages below this */
76: int wstop; /* stop after a file with a warning */
1.173 schwarze 77: enum outt outtype; /* which output to use */
1.79 kristaps 78: out_mdoc outmdoc; /* mdoc output ptr */
1.173 schwarze 79: out_man outman; /* man output ptr */
1.79 kristaps 80: out_free outfree; /* free output ptr */
81: void *outdata; /* data for output */
82: char outopts[BUFSIZ]; /* buf of output opts */
83: };
84:
1.194 schwarze 85: static int koptions(int *, char *);
1.170 schwarze 86: static int moptions(int *, char *);
1.155 kristaps 87: static void mmsg(enum mandocerr, enum mandoclevel,
88: const char *, int, int, const char *);
1.173 schwarze 89: static void parse(struct curparse *, int,
1.154 kristaps 90: const char *, enum mandoclevel *);
1.197 schwarze 91: static enum mandoclevel passthrough(const char *, int, int);
1.183 schwarze 92: static void spawn_pager(void);
1.66 kristaps 93: static int toptions(struct curparse *, char *);
1.180 schwarze 94: static void usage(enum argmode) __attribute__((noreturn));
1.55 kristaps 95: static void version(void) __attribute__((noreturn));
1.103 schwarze 96: static int woptions(struct curparse *, char *);
1.1 kristaps 97:
1.182 schwarze 98: static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
1.202 ! schwarze 99: static char help_arg[] = "help";
! 100: static char *help_argv[] = {help_arg, NULL};
1.54 kristaps 101: static const char *progname;
1.1 kristaps 102:
1.173 schwarze 103:
1.1 kristaps 104: int
105: main(int argc, char *argv[])
106: {
1.5 kristaps 107: struct curparse curp;
1.180 schwarze 108: struct mansearch search;
109: struct manpaths paths;
110: char *conf_file, *defpaths, *auxpaths;
111: char *defos;
112: #if HAVE_SQLITE3
1.186 schwarze 113: struct manpage *res, *resp;
1.182 schwarze 114: size_t isec, i, sz;
115: int prio, best_prio;
116: char sec;
1.180 schwarze 117: #endif
118: enum mandoclevel rc;
1.181 schwarze 119: enum outmode outmode;
1.192 schwarze 120: int fd;
1.180 schwarze 121: int show_usage;
1.183 schwarze 122: int use_pager;
1.197 schwarze 123: int synopsis_only;
1.170 schwarze 124: int options;
1.180 schwarze 125: int c;
1.1 kristaps 126:
1.54 kristaps 127: progname = strrchr(argv[0], '/');
128: if (progname == NULL)
129: progname = argv[0];
130: else
131: ++progname;
1.179 schwarze 132:
1.180 schwarze 133: /* Search options. */
134:
135: memset(&paths, 0, sizeof(struct manpaths));
136: conf_file = defpaths = auxpaths = NULL;
137:
138: memset(&search, 0, sizeof(struct mansearch));
139: search.outkey = "Nd";
140:
141: if (strcmp(progname, "man") == 0)
142: search.argmode = ARG_NAME;
143: else if (strncmp(progname, "apropos", 7) == 0)
144: search.argmode = ARG_EXPR;
145: else if (strncmp(progname, "whatis", 6) == 0)
146: search.argmode = ARG_WORD;
1.202 ! schwarze 147: else if (strncmp(progname, "help", 4) == 0)
! 148: search.argmode = ARG_NAME;
1.180 schwarze 149: else
150: search.argmode = ARG_FILE;
151:
152: /* Parser and formatter options. */
1.54 kristaps 153:
1.51 kristaps 154: memset(&curp, 0, sizeof(struct curparse));
1.201 schwarze 155: curp.outtype = OUTT_LOCALE;
1.103 schwarze 156: curp.wlevel = MANDOCLEVEL_FATAL;
1.194 schwarze 157: options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1;
1.166 schwarze 158: defos = NULL;
1.19 kristaps 159:
1.183 schwarze 160: use_pager = 1;
1.180 schwarze 161: show_usage = 0;
1.197 schwarze 162: synopsis_only = 0;
1.181 schwarze 163: outmode = OUTMODE_DEF;
1.183 schwarze 164:
1.194 schwarze 165: while (-1 != (c = getopt(argc, argv,
166: "aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) {
1.1 kristaps 167: switch (c) {
1.181 schwarze 168: case 'a':
169: outmode = OUTMODE_ALL;
170: break;
1.180 schwarze 171: case 'C':
172: conf_file = optarg;
173: break;
1.183 schwarze 174: case 'c':
175: use_pager = 0;
176: break;
1.180 schwarze 177: case 'f':
178: search.argmode = ARG_WORD;
179: break;
1.190 schwarze 180: case 'h':
181: (void)strlcat(curp.outopts, "synopsis,", BUFSIZ);
1.197 schwarze 182: synopsis_only = 1;
1.198 schwarze 183: use_pager = 0;
1.190 schwarze 184: outmode = OUTMODE_ALL;
185: break;
1.173 schwarze 186: case 'I':
1.166 schwarze 187: if (strncmp(optarg, "os=", 3)) {
1.173 schwarze 188: fprintf(stderr,
1.176 schwarze 189: "%s: -I%s: Bad argument\n",
190: progname, optarg);
1.166 schwarze 191: return((int)MANDOCLEVEL_BADARG);
192: }
193: if (defos) {
1.173 schwarze 194: fprintf(stderr,
1.176 schwarze 195: "%s: -I%s: Duplicate argument\n",
196: progname, optarg);
1.166 schwarze 197: return((int)MANDOCLEVEL_BADARG);
198: }
199: defos = mandoc_strdup(optarg + 3);
200: break;
1.181 schwarze 201: case 'i':
202: outmode = OUTMODE_INT;
203: break;
1.194 schwarze 204: case 'K':
205: if ( ! koptions(&options, optarg))
206: return((int)MANDOCLEVEL_BADARG);
207: break;
1.180 schwarze 208: case 'k':
209: search.argmode = ARG_EXPR;
210: break;
1.188 schwarze 211: case 'l':
212: search.argmode = ARG_FILE;
213: outmode = OUTMODE_ALL;
214: break;
1.180 schwarze 215: case 'M':
216: defpaths = optarg;
217: break;
1.173 schwarze 218: case 'm':
1.180 schwarze 219: auxpaths = optarg;
1.10 kristaps 220: break;
1.173 schwarze 221: case 'O':
1.180 schwarze 222: search.outkey = optarg;
1.49 kristaps 223: (void)strlcat(curp.outopts, optarg, BUFSIZ);
224: (void)strlcat(curp.outopts, ",", BUFSIZ);
1.44 kristaps 225: break;
1.180 schwarze 226: case 'S':
227: search.arch = optarg;
228: break;
229: case 's':
230: search.sec = optarg;
231: break;
1.173 schwarze 232: case 'T':
1.60 kristaps 233: if ( ! toptions(&curp, optarg))
1.105 kristaps 234: return((int)MANDOCLEVEL_BADARG);
1.1 kristaps 235: break;
1.173 schwarze 236: case 'W':
1.103 schwarze 237: if ( ! woptions(&curp, optarg))
1.105 kristaps 238: return((int)MANDOCLEVEL_BADARG);
1.1 kristaps 239: break;
1.181 schwarze 240: case 'w':
241: outmode = OUTMODE_FLN;
242: break;
1.173 schwarze 243: case 'V':
1.1 kristaps 244: version();
245: /* NOTREACHED */
246: default:
1.180 schwarze 247: show_usage = 1;
248: break;
1.1 kristaps 249: }
1.180 schwarze 250: }
251:
252: if (show_usage)
253: usage(search.argmode);
254:
1.185 schwarze 255: /* Postprocess options. */
256:
1.181 schwarze 257: if (outmode == OUTMODE_DEF) {
258: switch (search.argmode) {
259: case ARG_FILE:
260: outmode = OUTMODE_ALL;
1.183 schwarze 261: use_pager = 0;
1.181 schwarze 262: break;
263: case ARG_NAME:
264: outmode = OUTMODE_ONE;
265: break;
266: default:
267: outmode = OUTMODE_LST;
268: break;
269: }
270: }
271:
1.185 schwarze 272: /* Parse arguments. */
273:
1.180 schwarze 274: argc -= optind;
275: argv += optind;
1.182 schwarze 276: #if HAVE_SQLITE3
1.186 schwarze 277: resp = NULL;
1.182 schwarze 278: #endif
1.185 schwarze 279:
1.202 ! schwarze 280: /*
! 281: * Quirks for help(1)
! 282: * and for a man(1) section argument without -s.
! 283: */
! 284:
! 285: if (search.argmode == ARG_NAME) {
! 286: if (*progname == 'h') {
! 287: if (argc == 0) {
! 288: argv = help_argv;
! 289: argc = 1;
! 290: }
! 291: } else if (argv[0] != NULL &&
! 292: isdigit((unsigned char)argv[0][0]) &&
! 293: (argv[0][1] == '\0' || !strcmp(argv[0], "3p"))) {
! 294: search.sec = argv[0];
! 295: argv++;
! 296: argc--;
! 297: }
1.185 schwarze 298: }
1.182 schwarze 299:
300: rc = MANDOCLEVEL_OK;
1.180 schwarze 301:
302: /* man(1), whatis(1), apropos(1) */
303:
304: if (search.argmode != ARG_FILE) {
305: #if HAVE_SQLITE3
306: if (argc == 0)
307: usage(search.argmode);
1.199 schwarze 308:
309: if (search.argmode == ARG_NAME &&
310: outmode == OUTMODE_ONE)
311: search.firstmatch = 1;
1.182 schwarze 312:
313: /* Access the mandoc database. */
314:
1.180 schwarze 315: manpath_parse(&paths, conf_file, defpaths, auxpaths);
316: mansearch_setup(1);
317: if( ! mansearch(&search, &paths, argc, argv, &res, &sz))
318: usage(search.argmode);
1.186 schwarze 319: resp = res;
1.187 schwarze 320:
321: if (sz == 0) {
322: if (search.argmode == ARG_NAME)
323: fprintf(stderr, "%s: No entry for %s "
324: "in the manual.\n", progname, argv[0]);
325: rc = MANDOCLEVEL_BADARG;
326: goto out;
327: }
1.182 schwarze 328:
329: /*
330: * For standard man(1) and -a output mode,
331: * prepare for copying filename pointers
332: * into the program parameter array.
333: */
334:
335: if (outmode == OUTMODE_ONE) {
336: argc = 1;
337: best_prio = 10;
1.186 schwarze 338: } else if (outmode == OUTMODE_ALL)
1.182 schwarze 339: argc = (int)sz;
340:
341: /* Iterate all matching manuals. */
342:
1.181 schwarze 343: for (i = 0; i < sz; i++) {
344: if (outmode == OUTMODE_FLN)
345: puts(res[i].file);
1.182 schwarze 346: else if (outmode == OUTMODE_LST)
1.181 schwarze 347: printf("%s - %s\n", res[i].names,
348: res[i].output == NULL ? "" :
349: res[i].output);
1.186 schwarze 350: else if (outmode == OUTMODE_ONE) {
1.182 schwarze 351: /* Search for the best section. */
352: isec = strcspn(res[i].file, "123456789");
353: sec = res[i].file[isec];
354: if ('\0' == sec)
355: continue;
356: prio = sec_prios[sec - '1'];
357: if (prio >= best_prio)
358: continue;
359: best_prio = prio;
1.186 schwarze 360: resp = res + i;
1.182 schwarze 361: }
1.181 schwarze 362: }
1.182 schwarze 363:
364: /*
365: * For man(1), -a and -i output mode, fall through
366: * to the main mandoc(1) code iterating files
367: * and running the parsers on each of them.
368: */
369:
370: if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST)
371: goto out;
1.180 schwarze 372: #else
373: fputs("mandoc: database support not compiled in\n",
374: stderr);
375: return((int)MANDOCLEVEL_BADARG);
376: #endif
377: }
378:
379: /* mandoc(1) */
380:
381: if ( ! moptions(&options, auxpaths))
382: return((int)MANDOCLEVEL_BADARG);
1.1 kristaps 383:
1.183 schwarze 384: if (use_pager && isatty(STDOUT_FILENO))
385: spawn_pager();
386:
1.195 schwarze 387: curp.mchars = mchars_alloc();
388: curp.mp = mparse_alloc(options, curp.wlevel, mmsg,
389: curp.mchars, defos);
1.154 kristaps 390:
1.165 kristaps 391: /*
392: * Conditionally start up the lookaside buffer before parsing.
393: */
394: if (OUTT_MAN == curp.outtype)
395: mparse_keep(curp.mp);
396:
1.186 schwarze 397: if (argc == 0)
1.154 kristaps 398: parse(&curp, STDIN_FILENO, "<stdin>", &rc);
1.64 kristaps 399:
1.186 schwarze 400: while (argc) {
401: #if HAVE_SQLITE3
402: if (resp != NULL) {
1.200 schwarze 403: rc = mparse_open(curp.mp, &fd, resp->file);
1.192 schwarze 404: if (fd == -1)
405: /* nothing */;
406: else if (resp->form & FORM_SRC) {
1.189 schwarze 407: /* For .so only; ignore failure. */
408: chdir(paths.paths[resp->ipath]);
1.192 schwarze 409: parse(&curp, fd, resp->file, &rc);
1.189 schwarze 410: } else
1.197 schwarze 411: rc = passthrough(resp->file, fd,
412: synopsis_only);
1.186 schwarze 413: resp++;
414: } else
415: #endif
1.192 schwarze 416: {
1.200 schwarze 417: rc = mparse_open(curp.mp, &fd, *argv++);
1.192 schwarze 418: if (fd != -1)
419: parse(&curp, fd, argv[-1], &rc);
420: }
421:
1.200 schwarze 422: if (mparse_wait(curp.mp) != MANDOCLEVEL_OK)
1.192 schwarze 423: rc = MANDOCLEVEL_SYSERR;
424:
1.154 kristaps 425: if (MANDOCLEVEL_OK != rc && curp.wstop)
1.64 kristaps 426: break;
1.186 schwarze 427: argc--;
1.1 kristaps 428: }
429:
1.22 kristaps 430: if (curp.outfree)
431: (*curp.outfree)(curp.outdata);
1.195 schwarze 432: mparse_free(curp.mp);
433: mchars_free(curp.mchars);
1.182 schwarze 434:
435: #if HAVE_SQLITE3
436: out:
437: if (search.argmode != ARG_FILE) {
1.189 schwarze 438: manpath_free(&paths);
1.182 schwarze 439: mansearch_free(res, sz);
440: mansearch_setup(0);
441: }
442: #endif
443:
1.166 schwarze 444: free(defos);
1.1 kristaps 445:
1.154 kristaps 446: return((int)rc);
1.1 kristaps 447: }
448:
1.55 kristaps 449: static void
1.1 kristaps 450: version(void)
451: {
452:
1.180 schwarze 453: printf("mandoc %s\n", VERSION);
1.105 kristaps 454: exit((int)MANDOCLEVEL_OK);
1.1 kristaps 455: }
456:
1.55 kristaps 457: static void
1.180 schwarze 458: usage(enum argmode argmode)
1.1 kristaps 459: {
460:
1.180 schwarze 461: switch (argmode) {
462: case ARG_FILE:
1.190 schwarze 463: fputs("usage: mandoc [-acfhklV] [-Ios=name] "
1.196 schwarze 464: "[-Kencoding] [-mformat] [-Ooption]\n"
465: "\t [-Toutput] [-Wlevel] [file ...]\n", stderr);
1.180 schwarze 466: break;
467: case ARG_NAME:
1.188 schwarze 468: fputs("usage: man [-acfhklVw] [-C file] "
1.180 schwarze 469: "[-M path] [-m path] [-S arch] [-s section]\n"
470: "\t [section] name ...\n", stderr);
471: break;
472: case ARG_WORD:
1.190 schwarze 473: fputs("usage: whatis [-acfhklVw] [-C file] "
1.188 schwarze 474: "[-M path] [-m path] [-O outkey] [-S arch]\n"
475: "\t [-s section] name ...\n", stderr);
1.180 schwarze 476: break;
477: case ARG_EXPR:
1.190 schwarze 478: fputs("usage: apropos [-acfhklVw] [-C file] "
1.188 schwarze 479: "[-M path] [-m path] [-O outkey] [-S arch]\n"
1.180 schwarze 480: "\t [-s section] expression ...\n", stderr);
481: break;
482: }
1.105 kristaps 483: exit((int)MANDOCLEVEL_BADARG);
1.68 joerg 484: }
485:
1.64 kristaps 486: static void
1.173 schwarze 487: parse(struct curparse *curp, int fd, const char *file,
488: enum mandoclevel *level)
1.1 kristaps 489: {
1.154 kristaps 490: enum mandoclevel rc;
491: struct mdoc *mdoc;
492: struct man *man;
1.112 kristaps 493:
1.154 kristaps 494: /* Begin by parsing the file itself. */
1.112 kristaps 495:
1.154 kristaps 496: assert(file);
497: assert(fd >= -1);
1.112 kristaps 498:
1.154 kristaps 499: rc = mparse_readfd(curp->mp, fd, file);
1.112 kristaps 500:
1.154 kristaps 501: /* Stop immediately if the parse has failed. */
1.92 kristaps 502:
1.154 kristaps 503: if (MANDOCLEVEL_FATAL <= rc)
1.111 kristaps 504: goto cleanup;
505:
1.1 kristaps 506: /*
1.154 kristaps 507: * With -Wstop and warnings or errors of at least the requested
508: * level, do not produce output.
1.1 kristaps 509: */
510:
1.154 kristaps 511: if (MANDOCLEVEL_OK != rc && curp->wstop)
1.111 kristaps 512: goto cleanup;
513:
514: /* If unset, allocate output dev now (if applicable). */
515:
516: if ( ! (curp->outman && curp->outmdoc)) {
517: switch (curp->outtype) {
1.173 schwarze 518: case OUTT_HTML:
1.195 schwarze 519: curp->outdata = html_alloc(curp->mchars,
520: curp->outopts);
1.162 kristaps 521: curp->outfree = html_free;
522: break;
1.173 schwarze 523: case OUTT_UTF8:
1.195 schwarze 524: curp->outdata = utf8_alloc(curp->mchars,
525: curp->outopts);
1.163 kristaps 526: curp->outfree = ascii_free;
527: break;
1.173 schwarze 528: case OUTT_LOCALE:
1.195 schwarze 529: curp->outdata = locale_alloc(curp->mchars,
530: curp->outopts);
1.162 kristaps 531: curp->outfree = ascii_free;
1.111 kristaps 532: break;
1.173 schwarze 533: case OUTT_ASCII:
1.195 schwarze 534: curp->outdata = ascii_alloc(curp->mchars,
535: curp->outopts);
1.111 kristaps 536: curp->outfree = ascii_free;
537: break;
1.173 schwarze 538: case OUTT_PDF:
1.195 schwarze 539: curp->outdata = pdf_alloc(curp->mchars,
540: curp->outopts);
1.111 kristaps 541: curp->outfree = pspdf_free;
542: break;
1.173 schwarze 543: case OUTT_PS:
1.195 schwarze 544: curp->outdata = ps_alloc(curp->mchars,
545: curp->outopts);
1.111 kristaps 546: curp->outfree = pspdf_free;
547: break;
548: default:
549: break;
550: }
551:
552: switch (curp->outtype) {
1.173 schwarze 553: case OUTT_HTML:
1.111 kristaps 554: curp->outman = html_man;
555: curp->outmdoc = html_mdoc;
556: break;
1.173 schwarze 557: case OUTT_TREE:
1.111 kristaps 558: curp->outman = tree_man;
559: curp->outmdoc = tree_mdoc;
560: break;
1.173 schwarze 561: case OUTT_MAN:
1.164 schwarze 562: curp->outmdoc = man_mdoc;
1.165 kristaps 563: curp->outman = man_man;
1.164 schwarze 564: break;
1.173 schwarze 565: case OUTT_PDF:
1.111 kristaps 566: /* FALLTHROUGH */
1.173 schwarze 567: case OUTT_ASCII:
1.111 kristaps 568: /* FALLTHROUGH */
1.173 schwarze 569: case OUTT_UTF8:
1.163 kristaps 570: /* FALLTHROUGH */
1.173 schwarze 571: case OUTT_LOCALE:
1.162 kristaps 572: /* FALLTHROUGH */
1.173 schwarze 573: case OUTT_PS:
1.111 kristaps 574: curp->outman = terminal_man;
575: curp->outmdoc = terminal_mdoc;
576: break;
577: default:
578: break;
579: }
580: }
581:
1.171 schwarze 582: mparse_result(curp->mp, &mdoc, &man, NULL);
1.154 kristaps 583:
1.111 kristaps 584: /* Execute the out device, if it exists. */
585:
1.154 kristaps 586: if (man && curp->outman)
587: (*curp->outman)(curp->outdata, man);
588: if (mdoc && curp->outmdoc)
589: (*curp->outmdoc)(curp->outdata, mdoc);
1.111 kristaps 590:
591: cleanup:
1.112 kristaps 592:
1.154 kristaps 593: mparse_reset(curp->mp);
1.111 kristaps 594:
1.154 kristaps 595: if (*level < rc)
596: *level = rc;
1.186 schwarze 597: }
598:
599: static enum mandoclevel
1.197 schwarze 600: passthrough(const char *file, int fd, int synopsis_only)
1.186 schwarze 601: {
1.197 schwarze 602: const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS";
603: const char synr[] = "SYNOPSIS";
604:
605: FILE *stream;
1.186 schwarze 606: const char *syscall;
1.197 schwarze 607: char *line;
608: size_t len, off;
609: ssize_t nw;
610: int print;
611:
612: if ((stream = fdopen(fd, "r")) == NULL) {
613: close(fd);
614: syscall = "fdopen";
615: goto fail;
616: }
1.186 schwarze 617:
1.197 schwarze 618: print = 0;
619: while ((line = fgetln(stream, &len)) != NULL) {
620: if (synopsis_only) {
621: if (print) {
622: if ( ! isspace((unsigned char)*line))
623: goto done;
624: while (len &&
625: isspace((unsigned char)*line)) {
626: line++;
627: len--;
628: }
629: } else {
630: if ((len == sizeof(synb) &&
631: ! strncmp(line, synb, len - 1)) ||
632: (len == sizeof(synr) &&
633: ! strncmp(line, synr, len - 1)))
634: print = 1;
635: continue;
636: }
637: }
638: for (off = 0; off < len; off += nw)
639: if ((nw = write(STDOUT_FILENO, line + off,
640: len - off)) == -1 || nw == 0) {
641: fclose(stream);
1.186 schwarze 642: syscall = "write";
643: goto fail;
644: }
1.197 schwarze 645: }
1.186 schwarze 646:
1.197 schwarze 647: if (ferror(stream)) {
648: fclose(stream);
649: syscall = "fgetln";
650: goto fail;
651: }
1.193 schwarze 652:
1.197 schwarze 653: done:
654: fclose(stream);
655: return(MANDOCLEVEL_OK);
1.186 schwarze 656:
657: fail:
658: fprintf(stderr, "%s: %s: SYSERR: %s: %s",
659: progname, file, syscall, strerror(errno));
660: return(MANDOCLEVEL_SYSERR);
1.194 schwarze 661: }
662:
663: static int
664: koptions(int *options, char *arg)
665: {
666:
667: if ( ! strcmp(arg, "utf-8")) {
668: *options |= MPARSE_UTF8;
669: *options &= ~MPARSE_LATIN1;
670: } else if ( ! strcmp(arg, "iso-8859-1")) {
671: *options |= MPARSE_LATIN1;
672: *options &= ~MPARSE_UTF8;
673: } else if ( ! strcmp(arg, "us-ascii")) {
674: *options &= ~(MPARSE_UTF8 | MPARSE_LATIN1);
675: } else {
676: fprintf(stderr, "%s: -K%s: Bad argument\n",
677: progname, arg);
678: return(0);
679: }
680: return(1);
1.10 kristaps 681: }
682:
683: static int
1.170 schwarze 684: moptions(int *options, char *arg)
1.10 kristaps 685: {
686:
1.180 schwarze 687: if (arg == NULL)
688: /* nothing to do */;
689: else if (0 == strcmp(arg, "doc"))
1.170 schwarze 690: *options |= MPARSE_MDOC;
1.19 kristaps 691: else if (0 == strcmp(arg, "andoc"))
1.170 schwarze 692: /* nothing to do */;
1.17 kristaps 693: else if (0 == strcmp(arg, "an"))
1.170 schwarze 694: *options |= MPARSE_MAN;
1.10 kristaps 695: else {
1.176 schwarze 696: fprintf(stderr, "%s: -m%s: Bad argument\n",
697: progname, arg);
1.10 kristaps 698: return(0);
699: }
700:
701: return(1);
1.1 kristaps 702: }
703:
704: static int
1.60 kristaps 705: toptions(struct curparse *curp, char *arg)
1.1 kristaps 706: {
707:
708: if (0 == strcmp(arg, "ascii"))
1.60 kristaps 709: curp->outtype = OUTT_ASCII;
710: else if (0 == strcmp(arg, "lint")) {
711: curp->outtype = OUTT_LINT;
1.103 schwarze 712: curp->wlevel = MANDOCLEVEL_WARNING;
1.154 kristaps 713: } else if (0 == strcmp(arg, "tree"))
1.60 kristaps 714: curp->outtype = OUTT_TREE;
1.164 schwarze 715: else if (0 == strcmp(arg, "man"))
716: curp->outtype = OUTT_MAN;
1.43 kristaps 717: else if (0 == strcmp(arg, "html"))
1.60 kristaps 718: curp->outtype = OUTT_HTML;
1.163 kristaps 719: else if (0 == strcmp(arg, "utf8"))
720: curp->outtype = OUTT_UTF8;
1.162 kristaps 721: else if (0 == strcmp(arg, "locale"))
722: curp->outtype = OUTT_LOCALE;
1.59 kristaps 723: else if (0 == strcmp(arg, "xhtml"))
1.195 schwarze 724: curp->outtype = OUTT_HTML;
1.85 kristaps 725: else if (0 == strcmp(arg, "ps"))
726: curp->outtype = OUTT_PS;
1.100 kristaps 727: else if (0 == strcmp(arg, "pdf"))
728: curp->outtype = OUTT_PDF;
1.1 kristaps 729: else {
1.176 schwarze 730: fprintf(stderr, "%s: -T%s: Bad argument\n",
731: progname, arg);
1.1 kristaps 732: return(0);
733: }
734:
735: return(1);
736: }
737:
738: static int
1.103 schwarze 739: woptions(struct curparse *curp, char *arg)
1.1 kristaps 740: {
1.34 kristaps 741: char *v, *o;
1.173 schwarze 742: const char *toks[6];
1.1 kristaps 743:
1.103 schwarze 744: toks[0] = "stop";
745: toks[1] = "all";
746: toks[2] = "warning";
747: toks[3] = "error";
748: toks[4] = "fatal";
749: toks[5] = NULL;
1.1 kristaps 750:
1.34 kristaps 751: while (*arg) {
752: o = arg;
1.45 kristaps 753: switch (getsubopt(&arg, UNCONST(toks), &v)) {
1.173 schwarze 754: case 0:
1.103 schwarze 755: curp->wstop = 1;
1.1 kristaps 756: break;
1.173 schwarze 757: case 1:
1.103 schwarze 758: /* FALLTHROUGH */
1.173 schwarze 759: case 2:
1.103 schwarze 760: curp->wlevel = MANDOCLEVEL_WARNING;
1.1 kristaps 761: break;
1.173 schwarze 762: case 3:
1.103 schwarze 763: curp->wlevel = MANDOCLEVEL_ERROR;
1.24 kristaps 764: break;
1.173 schwarze 765: case 4:
1.103 schwarze 766: curp->wlevel = MANDOCLEVEL_FATAL;
1.50 kristaps 767: break;
1.1 kristaps 768: default:
1.176 schwarze 769: fprintf(stderr, "%s: -W%s: Bad argument\n",
770: progname, o);
1.1 kristaps 771: return(0);
772: }
1.34 kristaps 773: }
1.1 kristaps 774:
775: return(1);
776: }
777:
1.153 kristaps 778: static void
1.173 schwarze 779: mmsg(enum mandocerr t, enum mandoclevel lvl,
1.155 kristaps 780: const char *file, int line, int col, const char *msg)
1.71 kristaps 781: {
1.177 schwarze 782: const char *mparse_msg;
1.103 schwarze 783:
1.175 schwarze 784: fprintf(stderr, "%s: %s:", progname, file);
785:
786: if (line)
787: fprintf(stderr, "%d:%d:", line, col + 1);
788:
1.177 schwarze 789: fprintf(stderr, " %s", mparse_strlevel(lvl));
790:
791: if (NULL != (mparse_msg = mparse_strerror(t)))
792: fprintf(stderr, ": %s", mparse_msg);
1.154 kristaps 793:
1.73 kristaps 794: if (msg)
795: fprintf(stderr, ": %s", msg);
1.154 kristaps 796:
1.73 kristaps 797: fputc('\n', stderr);
1.183 schwarze 798: }
799:
800: static void
801: spawn_pager(void)
802: {
1.184 schwarze 803: #define MAX_PAGER_ARGS 16
804: char *argv[MAX_PAGER_ARGS];
805: const char *pager;
806: char *cp;
807: int fildes[2];
808: int argc;
1.183 schwarze 809:
810: if (pipe(fildes) == -1) {
811: fprintf(stderr, "%s: pipe: %s\n",
812: progname, strerror(errno));
813: return;
814: }
815:
816: switch (fork()) {
817: case -1:
818: fprintf(stderr, "%s: fork: %s\n",
819: progname, strerror(errno));
820: exit((int)MANDOCLEVEL_SYSERR);
821: case 0:
822: close(fildes[0]);
823: if (dup2(fildes[1], STDOUT_FILENO) == -1) {
824: fprintf(stderr, "%s: dup output: %s\n",
825: progname, strerror(errno));
826: exit((int)MANDOCLEVEL_SYSERR);
827: }
828: return;
829: default:
1.184 schwarze 830: break;
831: }
832:
833: /* The original process becomes the pager. */
834:
835: close(fildes[1]);
836: if (dup2(fildes[0], STDIN_FILENO) == -1) {
837: fprintf(stderr, "%s: dup input: %s\n",
838: progname, strerror(errno));
1.183 schwarze 839: exit((int)MANDOCLEVEL_SYSERR);
840: }
1.184 schwarze 841:
842: pager = getenv("MANPAGER");
843: if (pager == NULL || *pager == '\0')
844: pager = getenv("PAGER");
845: if (pager == NULL || *pager == '\0')
846: pager = "/usr/bin/more -s";
847: cp = mandoc_strdup(pager);
848:
849: /*
850: * Parse the pager command into words.
851: * Intentionally do not do anything fancy here.
852: */
853:
854: argc = 0;
855: while (argc + 1 < MAX_PAGER_ARGS) {
856: argv[argc++] = cp;
857: cp = strchr(cp, ' ');
858: if (cp == NULL)
859: break;
860: *cp++ = '\0';
861: while (*cp == ' ')
862: cp++;
863: if (*cp == '\0')
864: break;
865: }
866: argv[argc] = NULL;
867:
868: /* Hand over to the pager. */
869:
870: execvp(argv[0], argv);
871: fprintf(stderr, "%s: exec: %s\n",
872: progname, strerror(errno));
873: exit((int)MANDOCLEVEL_SYSERR);
1.71 kristaps 874: }
CVSweb