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