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