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