mandoc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +7 -0
  3. data/COPYING +674 -0
  4. data/README.md +117 -0
  5. data/ext/mandoc/extconf.rb +59 -0
  6. data/ext/mandoc/rb_mandoc.c +548 -0
  7. data/ext/mandoc/rb_mandoc.h +22 -0
  8. data/lib/mandoc/version.rb +19 -0
  9. data/lib/mandoc.rb +26 -0
  10. data/mandoc-1.14.6/LICENSE +55 -0
  11. data/mandoc-1.14.6/arch.c +54 -0
  12. data/mandoc-1.14.6/att.c +49 -0
  13. data/mandoc-1.14.6/catman.c +260 -0
  14. data/mandoc-1.14.6/cgi.c +1279 -0
  15. data/mandoc-1.14.6/chars.c +507 -0
  16. data/mandoc-1.14.6/compat_err.c +103 -0
  17. data/mandoc-1.14.6/compat_fts.c +696 -0
  18. data/mandoc-1.14.6/compat_fts.h +106 -0
  19. data/mandoc-1.14.6/compat_getline.c +59 -0
  20. data/mandoc-1.14.6/compat_getsubopt.c +87 -0
  21. data/mandoc-1.14.6/compat_isblank.c +23 -0
  22. data/mandoc-1.14.6/compat_mkdtemp.c +50 -0
  23. data/mandoc-1.14.6/compat_mkstemps.c +63 -0
  24. data/mandoc-1.14.6/compat_ohash.c +330 -0
  25. data/mandoc-1.14.6/compat_ohash.h +72 -0
  26. data/mandoc-1.14.6/compat_progname.c +31 -0
  27. data/mandoc-1.14.6/compat_reallocarray.c +40 -0
  28. data/mandoc-1.14.6/compat_recallocarray.c +99 -0
  29. data/mandoc-1.14.6/compat_strcasestr.c +64 -0
  30. data/mandoc-1.14.6/compat_stringlist.c +135 -0
  31. data/mandoc-1.14.6/compat_stringlist.h +48 -0
  32. data/mandoc-1.14.6/compat_strlcat.c +57 -0
  33. data/mandoc-1.14.6/compat_strlcpy.c +52 -0
  34. data/mandoc-1.14.6/compat_strndup.c +42 -0
  35. data/mandoc-1.14.6/compat_strsep.c +70 -0
  36. data/mandoc-1.14.6/compat_strtonum.c +67 -0
  37. data/mandoc-1.14.6/compat_vasprintf.c +47 -0
  38. data/mandoc-1.14.6/config.h +52 -0
  39. data/mandoc-1.14.6/dba.c +508 -0
  40. data/mandoc-1.14.6/dba.h +50 -0
  41. data/mandoc-1.14.6/dba_array.c +190 -0
  42. data/mandoc-1.14.6/dba_array.h +47 -0
  43. data/mandoc-1.14.6/dba_read.c +74 -0
  44. data/mandoc-1.14.6/dba_write.c +127 -0
  45. data/mandoc-1.14.6/dba_write.h +30 -0
  46. data/mandoc-1.14.6/dbm.c +480 -0
  47. data/mandoc-1.14.6/dbm.h +68 -0
  48. data/mandoc-1.14.6/dbm_map.c +194 -0
  49. data/mandoc-1.14.6/dbm_map.h +29 -0
  50. data/mandoc-1.14.6/demandoc.c +260 -0
  51. data/mandoc-1.14.6/eqn.c +1132 -0
  52. data/mandoc-1.14.6/eqn.h +72 -0
  53. data/mandoc-1.14.6/eqn_html.c +246 -0
  54. data/mandoc-1.14.6/eqn_parse.h +48 -0
  55. data/mandoc-1.14.6/eqn_term.c +174 -0
  56. data/mandoc-1.14.6/html.c +1102 -0
  57. data/mandoc-1.14.6/html.h +142 -0
  58. data/mandoc-1.14.6/lib.c +35 -0
  59. data/mandoc-1.14.6/libman.h +42 -0
  60. data/mandoc-1.14.6/libmandoc.h +85 -0
  61. data/mandoc-1.14.6/libmdoc.h +87 -0
  62. data/mandoc-1.14.6/main.c +1375 -0
  63. data/mandoc-1.14.6/main.h +53 -0
  64. data/mandoc-1.14.6/man.c +345 -0
  65. data/mandoc-1.14.6/man.h +21 -0
  66. data/mandoc-1.14.6/man_html.c +640 -0
  67. data/mandoc-1.14.6/man_macro.c +470 -0
  68. data/mandoc-1.14.6/man_term.c +1143 -0
  69. data/mandoc-1.14.6/man_validate.c +660 -0
  70. data/mandoc-1.14.6/manconf.h +58 -0
  71. data/mandoc-1.14.6/mandoc.c +669 -0
  72. data/mandoc-1.14.6/mandoc.h +329 -0
  73. data/mandoc-1.14.6/mandoc_aux.c +118 -0
  74. data/mandoc-1.14.6/mandoc_aux.h +27 -0
  75. data/mandoc-1.14.6/mandoc_msg.c +375 -0
  76. data/mandoc-1.14.6/mandoc_ohash.c +65 -0
  77. data/mandoc-1.14.6/mandoc_ohash.h +23 -0
  78. data/mandoc-1.14.6/mandoc_parse.h +44 -0
  79. data/mandoc-1.14.6/mandoc_xr.c +123 -0
  80. data/mandoc-1.14.6/mandoc_xr.h +31 -0
  81. data/mandoc-1.14.6/mandocd.c +282 -0
  82. data/mandoc-1.14.6/mandocdb.c +2448 -0
  83. data/mandoc-1.14.6/manpath.c +363 -0
  84. data/mandoc-1.14.6/mansearch.c +851 -0
  85. data/mandoc-1.14.6/mansearch.h +118 -0
  86. data/mandoc-1.14.6/mdoc.c +433 -0
  87. data/mandoc-1.14.6/mdoc.h +158 -0
  88. data/mandoc-1.14.6/mdoc_argv.c +682 -0
  89. data/mandoc-1.14.6/mdoc_html.c +1762 -0
  90. data/mandoc-1.14.6/mdoc_macro.c +1600 -0
  91. data/mandoc-1.14.6/mdoc_man.c +1850 -0
  92. data/mandoc-1.14.6/mdoc_markdown.c +1610 -0
  93. data/mandoc-1.14.6/mdoc_state.c +256 -0
  94. data/mandoc-1.14.6/mdoc_term.c +1964 -0
  95. data/mandoc-1.14.6/mdoc_validate.c +3062 -0
  96. data/mandoc-1.14.6/msec.c +37 -0
  97. data/mandoc-1.14.6/out.c +544 -0
  98. data/mandoc-1.14.6/out.h +70 -0
  99. data/mandoc-1.14.6/preconv.c +179 -0
  100. data/mandoc-1.14.6/read.c +732 -0
  101. data/mandoc-1.14.6/roff.c +4390 -0
  102. data/mandoc-1.14.6/roff.h +561 -0
  103. data/mandoc-1.14.6/roff_html.c +119 -0
  104. data/mandoc-1.14.6/roff_int.h +94 -0
  105. data/mandoc-1.14.6/roff_term.c +266 -0
  106. data/mandoc-1.14.6/roff_validate.c +151 -0
  107. data/mandoc-1.14.6/soelim.c +182 -0
  108. data/mandoc-1.14.6/st.c +82 -0
  109. data/mandoc-1.14.6/tag.c +327 -0
  110. data/mandoc-1.14.6/tag.h +35 -0
  111. data/mandoc-1.14.6/tbl.c +183 -0
  112. data/mandoc-1.14.6/tbl.h +121 -0
  113. data/mandoc-1.14.6/tbl_data.c +323 -0
  114. data/mandoc-1.14.6/tbl_html.c +293 -0
  115. data/mandoc-1.14.6/tbl_int.h +47 -0
  116. data/mandoc-1.14.6/tbl_layout.c +376 -0
  117. data/mandoc-1.14.6/tbl_opts.c +173 -0
  118. data/mandoc-1.14.6/tbl_parse.h +30 -0
  119. data/mandoc-1.14.6/tbl_term.c +948 -0
  120. data/mandoc-1.14.6/term.c +1113 -0
  121. data/mandoc-1.14.6/term.h +158 -0
  122. data/mandoc-1.14.6/term_ascii.c +424 -0
  123. data/mandoc-1.14.6/term_ps.c +1362 -0
  124. data/mandoc-1.14.6/term_tab.c +130 -0
  125. data/mandoc-1.14.6/term_tag.c +227 -0
  126. data/mandoc-1.14.6/term_tag.h +34 -0
  127. data/mandoc-1.14.6/tree.c +536 -0
  128. metadata +170 -0
@@ -0,0 +1,1375 @@
1
+ /* $Id: main.c,v 1.358 2021/09/04 22:38:46 schwarze Exp $ */
2
+ /*
3
+ * Copyright (c) 2010-2012, 2014-2021 Ingo Schwarze <schwarze@openbsd.org>
4
+ * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
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
+ * Main program for mandoc(1), man(1), apropos(1), whatis(1), and help(1).
20
+ */
21
+ #include "config.h"
22
+
23
+ #include <sys/types.h>
24
+ #include <sys/ioctl.h>
25
+ #include <sys/param.h> /* MACHINE */
26
+ #include <sys/stat.h>
27
+ #include <sys/wait.h>
28
+
29
+ #include <assert.h>
30
+ #include <ctype.h>
31
+ #if HAVE_ERR
32
+ #include <err.h>
33
+ #endif
34
+ #include <errno.h>
35
+ #include <fcntl.h>
36
+ #include <glob.h>
37
+ #include <limits.h>
38
+ #if HAVE_SANDBOX_INIT
39
+ #include <sandbox.h>
40
+ #endif
41
+ #include <signal.h>
42
+ #include <stdio.h>
43
+ #include <stdint.h>
44
+ #include <stdlib.h>
45
+ #include <string.h>
46
+ #include <termios.h>
47
+ #include <time.h>
48
+ #include <unistd.h>
49
+
50
+ #include "mandoc_aux.h"
51
+ #include "mandoc.h"
52
+ #include "mandoc_xr.h"
53
+ #include "roff.h"
54
+ #include "mdoc.h"
55
+ #include "man.h"
56
+ #include "mandoc_parse.h"
57
+ #include "tag.h"
58
+ #include "term_tag.h"
59
+ #include "main.h"
60
+ #include "manconf.h"
61
+ #include "mansearch.h"
62
+
63
+ enum outmode {
64
+ OUTMODE_DEF = 0,
65
+ OUTMODE_FLN,
66
+ OUTMODE_LST,
67
+ OUTMODE_ALL,
68
+ OUTMODE_ONE
69
+ };
70
+
71
+ enum outt {
72
+ OUTT_ASCII = 0, /* -Tascii */
73
+ OUTT_LOCALE, /* -Tlocale */
74
+ OUTT_UTF8, /* -Tutf8 */
75
+ OUTT_TREE, /* -Ttree */
76
+ OUTT_MAN, /* -Tman */
77
+ OUTT_HTML, /* -Thtml */
78
+ OUTT_MARKDOWN, /* -Tmarkdown */
79
+ OUTT_LINT, /* -Tlint */
80
+ OUTT_PS, /* -Tps */
81
+ OUTT_PDF /* -Tpdf */
82
+ };
83
+
84
+ struct outstate {
85
+ struct tag_files *tag_files; /* Tagging state variables. */
86
+ void *outdata; /* data for output */
87
+ int use_pager;
88
+ int wstop; /* stop after a file with a warning */
89
+ int had_output; /* Some output was generated. */
90
+ enum outt outtype; /* which output to use */
91
+ };
92
+
93
+
94
+ int mandocdb(int, char *[]);
95
+
96
+ static void check_xr(struct manpaths *);
97
+ static void fs_append(char **, size_t, int,
98
+ size_t, const char *, enum form,
99
+ struct manpage **, size_t *);
100
+ static int fs_lookup(const struct manpaths *, size_t,
101
+ const char *, const char *, const char *,
102
+ struct manpage **, size_t *);
103
+ static int fs_search(const struct mansearch *,
104
+ const struct manpaths *, const char *,
105
+ struct manpage **, size_t *);
106
+ static void glob_esc(char **, const char *, const char *);
107
+ static void outdata_alloc(struct outstate *, struct manoutput *);
108
+ static void parse(struct mparse *, int, const char *,
109
+ struct outstate *, struct manconf *);
110
+ static void passthrough(int, int);
111
+ static void process_onefile(struct mparse *, struct manpage *,
112
+ int, struct outstate *, struct manconf *);
113
+ static void run_pager(struct outstate *, char *);
114
+ static pid_t spawn_pager(struct outstate *, char *);
115
+ static void usage(enum argmode) __attribute__((__noreturn__));
116
+ static int woptions(char *, enum mandoc_os *, int *);
117
+
118
+ static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
119
+ static char help_arg[] = "help";
120
+ static char *help_argv[] = {help_arg, NULL};
121
+
122
+
123
+ int
124
+ main(int argc, char *argv[])
125
+ {
126
+ struct manconf conf; /* Manpaths and output options. */
127
+ struct outstate outst; /* Output state. */
128
+ struct winsize ws; /* Result of ioctl(TIOCGWINSZ). */
129
+ struct mansearch search; /* Search options. */
130
+ struct manpage *res; /* Complete list of search results. */
131
+ struct manpage *resn; /* Search results for one name. */
132
+ struct mparse *mp; /* Opaque parser object. */
133
+ const char *conf_file; /* -C: alternate config file. */
134
+ const char *os_s; /* -I: Operating system for display. */
135
+ const char *progname, *sec, *ep;
136
+ char *defpaths; /* -M: override manpaths. */
137
+ char *auxpaths; /* -m: additional manpaths. */
138
+ char *oarg; /* -O: output option string. */
139
+ char *tagarg; /* -O tag: default value. */
140
+ unsigned char *uc;
141
+ size_t ressz; /* Number of elements in res[]. */
142
+ size_t resnsz; /* Number of elements in resn[]. */
143
+ size_t i, ib, ssz;
144
+ int options; /* Parser options. */
145
+ int show_usage; /* Invalid argument: give up. */
146
+ int prio, best_prio;
147
+ int startdir;
148
+ int c;
149
+ enum mandoc_os os_e; /* Check base system conventions. */
150
+ enum outmode outmode; /* According to command line. */
151
+
152
+ #if HAVE_PROGNAME
153
+ progname = getprogname();
154
+ #else
155
+ if (argc < 1)
156
+ progname = mandoc_strdup("mandoc");
157
+ else if ((progname = strrchr(argv[0], '/')) == NULL)
158
+ progname = argv[0];
159
+ else
160
+ ++progname;
161
+ setprogname(progname);
162
+ #endif
163
+
164
+ mandoc_msg_setoutfile(stderr);
165
+ if (strncmp(progname, "mandocdb", 8) == 0 ||
166
+ strcmp(progname, BINM_MAKEWHATIS) == 0)
167
+ return mandocdb(argc, argv);
168
+
169
+ #if HAVE_PLEDGE
170
+ if (pledge("stdio rpath wpath cpath tmppath tty proc exec", NULL) == -1) {
171
+ mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno));
172
+ return mandoc_msg_getrc();
173
+ }
174
+ #endif
175
+ #if HAVE_SANDBOX_INIT
176
+ if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1)
177
+ errx((int)MANDOCLEVEL_SYSERR, "sandbox_init");
178
+ #endif
179
+
180
+ /* Search options. */
181
+
182
+ memset(&conf, 0, sizeof(conf));
183
+ conf_file = NULL;
184
+ defpaths = auxpaths = NULL;
185
+
186
+ memset(&search, 0, sizeof(struct mansearch));
187
+ search.outkey = "Nd";
188
+ oarg = NULL;
189
+
190
+ if (strcmp(progname, BINM_MAN) == 0)
191
+ search.argmode = ARG_NAME;
192
+ else if (strcmp(progname, BINM_APROPOS) == 0)
193
+ search.argmode = ARG_EXPR;
194
+ else if (strcmp(progname, BINM_WHATIS) == 0)
195
+ search.argmode = ARG_WORD;
196
+ else if (strncmp(progname, "help", 4) == 0)
197
+ search.argmode = ARG_NAME;
198
+ else
199
+ search.argmode = ARG_FILE;
200
+
201
+ /* Parser options. */
202
+
203
+ options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1;
204
+ os_e = MANDOC_OS_OTHER;
205
+ os_s = NULL;
206
+
207
+ /* Formatter options. */
208
+
209
+ memset(&outst, 0, sizeof(outst));
210
+ outst.tag_files = NULL;
211
+ outst.outtype = OUTT_LOCALE;
212
+ outst.use_pager = 1;
213
+
214
+ show_usage = 0;
215
+ outmode = OUTMODE_DEF;
216
+
217
+ while ((c = getopt(argc, argv,
218
+ "aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) {
219
+ if (c == 'i' && search.argmode == ARG_EXPR) {
220
+ optind--;
221
+ break;
222
+ }
223
+ switch (c) {
224
+ case 'a':
225
+ outmode = OUTMODE_ALL;
226
+ break;
227
+ case 'C':
228
+ conf_file = optarg;
229
+ break;
230
+ case 'c':
231
+ outst.use_pager = 0;
232
+ break;
233
+ case 'f':
234
+ search.argmode = ARG_WORD;
235
+ break;
236
+ case 'h':
237
+ conf.output.synopsisonly = 1;
238
+ outst.use_pager = 0;
239
+ outmode = OUTMODE_ALL;
240
+ break;
241
+ case 'I':
242
+ if (strncmp(optarg, "os=", 3) != 0) {
243
+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0,
244
+ "-I %s", optarg);
245
+ return mandoc_msg_getrc();
246
+ }
247
+ if (os_s != NULL) {
248
+ mandoc_msg(MANDOCERR_BADARG_DUPE, 0, 0,
249
+ "-I %s", optarg);
250
+ return mandoc_msg_getrc();
251
+ }
252
+ os_s = optarg + 3;
253
+ break;
254
+ case 'K':
255
+ options &= ~(MPARSE_UTF8 | MPARSE_LATIN1);
256
+ if (strcmp(optarg, "utf-8") == 0)
257
+ options |= MPARSE_UTF8;
258
+ else if (strcmp(optarg, "iso-8859-1") == 0)
259
+ options |= MPARSE_LATIN1;
260
+ else if (strcmp(optarg, "us-ascii") != 0) {
261
+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0,
262
+ "-K %s", optarg);
263
+ return mandoc_msg_getrc();
264
+ }
265
+ break;
266
+ case 'k':
267
+ search.argmode = ARG_EXPR;
268
+ break;
269
+ case 'l':
270
+ search.argmode = ARG_FILE;
271
+ outmode = OUTMODE_ALL;
272
+ break;
273
+ case 'M':
274
+ defpaths = optarg;
275
+ break;
276
+ case 'm':
277
+ auxpaths = optarg;
278
+ break;
279
+ case 'O':
280
+ oarg = optarg;
281
+ break;
282
+ case 'S':
283
+ search.arch = optarg;
284
+ break;
285
+ case 's':
286
+ search.sec = optarg;
287
+ break;
288
+ case 'T':
289
+ if (strcmp(optarg, "ascii") == 0)
290
+ outst.outtype = OUTT_ASCII;
291
+ else if (strcmp(optarg, "lint") == 0) {
292
+ outst.outtype = OUTT_LINT;
293
+ mandoc_msg_setoutfile(stdout);
294
+ mandoc_msg_setmin(MANDOCERR_BASE);
295
+ } else if (strcmp(optarg, "tree") == 0)
296
+ outst.outtype = OUTT_TREE;
297
+ else if (strcmp(optarg, "man") == 0)
298
+ outst.outtype = OUTT_MAN;
299
+ else if (strcmp(optarg, "html") == 0)
300
+ outst.outtype = OUTT_HTML;
301
+ else if (strcmp(optarg, "markdown") == 0)
302
+ outst.outtype = OUTT_MARKDOWN;
303
+ else if (strcmp(optarg, "utf8") == 0)
304
+ outst.outtype = OUTT_UTF8;
305
+ else if (strcmp(optarg, "locale") == 0)
306
+ outst.outtype = OUTT_LOCALE;
307
+ else if (strcmp(optarg, "ps") == 0)
308
+ outst.outtype = OUTT_PS;
309
+ else if (strcmp(optarg, "pdf") == 0)
310
+ outst.outtype = OUTT_PDF;
311
+ else {
312
+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0,
313
+ "-T %s", optarg);
314
+ return mandoc_msg_getrc();
315
+ }
316
+ break;
317
+ case 'W':
318
+ if (woptions(optarg, &os_e, &outst.wstop) == -1)
319
+ return mandoc_msg_getrc();
320
+ break;
321
+ case 'w':
322
+ outmode = OUTMODE_FLN;
323
+ break;
324
+ default:
325
+ show_usage = 1;
326
+ break;
327
+ }
328
+ }
329
+
330
+ if (show_usage)
331
+ usage(search.argmode);
332
+
333
+ /* Postprocess options. */
334
+
335
+ switch (outmode) {
336
+ case OUTMODE_DEF:
337
+ switch (search.argmode) {
338
+ case ARG_FILE:
339
+ outmode = OUTMODE_ALL;
340
+ outst.use_pager = 0;
341
+ break;
342
+ case ARG_NAME:
343
+ outmode = OUTMODE_ONE;
344
+ break;
345
+ default:
346
+ outmode = OUTMODE_LST;
347
+ break;
348
+ }
349
+ break;
350
+ case OUTMODE_FLN:
351
+ if (search.argmode == ARG_FILE)
352
+ outmode = OUTMODE_ALL;
353
+ break;
354
+ case OUTMODE_ALL:
355
+ break;
356
+ case OUTMODE_LST:
357
+ case OUTMODE_ONE:
358
+ abort();
359
+ }
360
+
361
+ if (oarg != NULL) {
362
+ if (outmode == OUTMODE_LST)
363
+ search.outkey = oarg;
364
+ else {
365
+ while (oarg != NULL) {
366
+ if (manconf_output(&conf.output,
367
+ strsep(&oarg, ","), 0) == -1)
368
+ return mandoc_msg_getrc();
369
+ }
370
+ }
371
+ }
372
+
373
+ if (outst.outtype != OUTT_TREE || conf.output.noval == 0)
374
+ options |= MPARSE_VALIDATE;
375
+
376
+ if (outmode == OUTMODE_FLN ||
377
+ outmode == OUTMODE_LST ||
378
+ (conf.output.outfilename == NULL &&
379
+ conf.output.tagfilename == NULL &&
380
+ isatty(STDOUT_FILENO) == 0))
381
+ outst.use_pager = 0;
382
+
383
+ if (outst.use_pager &&
384
+ (conf.output.width == 0 || conf.output.indent == 0) &&
385
+ ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 &&
386
+ ws.ws_col > 1) {
387
+ if (conf.output.width == 0 && ws.ws_col < 79)
388
+ conf.output.width = ws.ws_col - 1;
389
+ if (conf.output.indent == 0 && ws.ws_col < 66)
390
+ conf.output.indent = 3;
391
+ }
392
+
393
+ #if HAVE_PLEDGE
394
+ if (outst.use_pager == 0)
395
+ c = pledge("stdio rpath", NULL);
396
+ else if (conf.output.outfilename != NULL ||
397
+ conf.output.tagfilename != NULL)
398
+ c = pledge("stdio rpath wpath cpath", NULL);
399
+ else
400
+ c = pledge("stdio rpath tmppath tty proc exec", NULL);
401
+ if (c == -1) {
402
+ mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno));
403
+ return mandoc_msg_getrc();
404
+ }
405
+ #endif
406
+
407
+ /* Parse arguments. */
408
+
409
+ if (argc > 0) {
410
+ argc -= optind;
411
+ argv += optind;
412
+ }
413
+
414
+ /*
415
+ * Quirks for help(1) and man(1),
416
+ * in particular for a section argument without -s.
417
+ */
418
+
419
+ if (search.argmode == ARG_NAME) {
420
+ if (*progname == 'h') {
421
+ if (argc == 0) {
422
+ argv = help_argv;
423
+ argc = 1;
424
+ }
425
+ } else if (argc > 1 &&
426
+ ((uc = (unsigned char *)argv[0]) != NULL) &&
427
+ ((isdigit(uc[0]) && (uc[1] == '\0' ||
428
+ isalpha(uc[1]))) ||
429
+ (uc[0] == 'n' && uc[1] == '\0'))) {
430
+ search.sec = (char *)uc;
431
+ argv++;
432
+ argc--;
433
+ }
434
+ if (search.arch == NULL)
435
+ search.arch = getenv("MACHINE");
436
+ #ifdef MACHINE
437
+ if (search.arch == NULL)
438
+ search.arch = MACHINE;
439
+ #endif
440
+ if (outmode == OUTMODE_ONE)
441
+ search.firstmatch = 1;
442
+ }
443
+
444
+ /*
445
+ * Use the first argument for -O tag in addition to
446
+ * using it as a search term for man(1) or apropos(1).
447
+ */
448
+
449
+ if (conf.output.tag != NULL && *conf.output.tag == '\0') {
450
+ tagarg = argc > 0 && search.argmode == ARG_EXPR ?
451
+ strchr(*argv, '=') : NULL;
452
+ conf.output.tag = tagarg == NULL ? *argv : tagarg + 1;
453
+ }
454
+
455
+ /* Read the configuration file. */
456
+
457
+ if (search.argmode != ARG_FILE ||
458
+ mandoc_msg_getmin() == MANDOCERR_STYLE)
459
+ manconf_parse(&conf, conf_file, defpaths, auxpaths);
460
+
461
+ /* man(1): Resolve each name individually. */
462
+
463
+ if (search.argmode == ARG_NAME) {
464
+ if (argc < 1) {
465
+ if (outmode != OUTMODE_FLN)
466
+ usage(ARG_NAME);
467
+ if (conf.manpath.sz == 0) {
468
+ warnx("The manpath is empty.");
469
+ mandoc_msg_setrc(MANDOCLEVEL_BADARG);
470
+ } else {
471
+ for (i = 0; i + 1 < conf.manpath.sz; i++)
472
+ printf("%s:", conf.manpath.paths[i]);
473
+ printf("%s\n", conf.manpath.paths[i]);
474
+ }
475
+ manconf_free(&conf);
476
+ return (int)mandoc_msg_getrc();
477
+ }
478
+ for (res = NULL, ressz = 0; argc > 0; argc--, argv++) {
479
+ (void)mansearch(&search, &conf.manpath,
480
+ 1, argv, &resn, &resnsz);
481
+ if (resnsz == 0)
482
+ (void)fs_search(&search, &conf.manpath,
483
+ *argv, &resn, &resnsz);
484
+ if (resnsz == 0 && strchr(*argv, '/') == NULL) {
485
+ if (search.arch != NULL &&
486
+ arch_valid(search.arch, OSENUM) == 0)
487
+ warnx("Unknown architecture \"%s\".",
488
+ search.arch);
489
+ else if (search.sec != NULL)
490
+ warnx("No entry for %s in "
491
+ "section %s of the manual.",
492
+ *argv, search.sec);
493
+ else
494
+ warnx("No entry for %s in "
495
+ "the manual.", *argv);
496
+ mandoc_msg_setrc(MANDOCLEVEL_BADARG);
497
+ continue;
498
+ }
499
+ if (resnsz == 0) {
500
+ if (access(*argv, R_OK) == -1) {
501
+ mandoc_msg_setinfilename(*argv);
502
+ mandoc_msg(MANDOCERR_BADARG_BAD,
503
+ 0, 0, "%s", strerror(errno));
504
+ mandoc_msg_setinfilename(NULL);
505
+ continue;
506
+ }
507
+ resnsz = 1;
508
+ resn = mandoc_calloc(resnsz, sizeof(*res));
509
+ resn->file = mandoc_strdup(*argv);
510
+ resn->ipath = SIZE_MAX;
511
+ resn->form = FORM_SRC;
512
+ }
513
+ if (outmode != OUTMODE_ONE || resnsz == 1) {
514
+ res = mandoc_reallocarray(res,
515
+ ressz + resnsz, sizeof(*res));
516
+ memcpy(res + ressz, resn,
517
+ sizeof(*resn) * resnsz);
518
+ ressz += resnsz;
519
+ continue;
520
+ }
521
+
522
+ /* Search for the best section. */
523
+
524
+ best_prio = 40;
525
+ for (ib = i = 0; i < resnsz; i++) {
526
+ sec = resn[i].file;
527
+ sec += strcspn(sec, "123456789");
528
+ if (sec[0] == '\0')
529
+ continue; /* No section at all. */
530
+ prio = sec_prios[sec[0] - '1'];
531
+ if (search.sec != NULL) {
532
+ ssz = strlen(search.sec);
533
+ if (strncmp(sec, search.sec, ssz) == 0)
534
+ sec += ssz;
535
+ } else
536
+ sec++; /* Prefer without suffix. */
537
+ if (*sec != '/')
538
+ prio += 10; /* Wrong dir name. */
539
+ if (search.sec != NULL) {
540
+ ep = strchr(sec, '\0');
541
+ if (ep - sec > 3 &&
542
+ strncmp(ep - 3, ".gz", 3) == 0)
543
+ ep -= 3;
544
+ if ((size_t)(ep - sec) < ssz + 3 ||
545
+ strncmp(ep - ssz, search.sec,
546
+ ssz) != 0) /* Wrong file */
547
+ prio += 20; /* extension. */
548
+ }
549
+ if (prio >= best_prio)
550
+ continue;
551
+ best_prio = prio;
552
+ ib = i;
553
+ }
554
+ res = mandoc_reallocarray(res, ressz + 1,
555
+ sizeof(*res));
556
+ memcpy(res + ressz++, resn + ib, sizeof(*resn));
557
+ }
558
+
559
+ /* apropos(1), whatis(1): Process the full search expression. */
560
+
561
+ } else if (search.argmode != ARG_FILE) {
562
+ if (mansearch(&search, &conf.manpath,
563
+ argc, argv, &res, &ressz) == 0)
564
+ usage(search.argmode);
565
+
566
+ if (ressz == 0) {
567
+ warnx("nothing appropriate");
568
+ mandoc_msg_setrc(MANDOCLEVEL_BADARG);
569
+ goto out;
570
+ }
571
+
572
+ /* mandoc(1): Take command line arguments as file names. */
573
+
574
+ } else {
575
+ ressz = argc > 0 ? argc : 1;
576
+ res = mandoc_calloc(ressz, sizeof(*res));
577
+ for (i = 0; i < ressz; i++) {
578
+ if (argc > 0)
579
+ res[i].file = mandoc_strdup(argv[i]);
580
+ res[i].ipath = SIZE_MAX;
581
+ res[i].form = FORM_SRC;
582
+ }
583
+ }
584
+
585
+ switch (outmode) {
586
+ case OUTMODE_FLN:
587
+ for (i = 0; i < ressz; i++)
588
+ puts(res[i].file);
589
+ goto out;
590
+ case OUTMODE_LST:
591
+ for (i = 0; i < ressz; i++)
592
+ printf("%s - %s\n", res[i].names,
593
+ res[i].output == NULL ? "" :
594
+ res[i].output);
595
+ goto out;
596
+ default:
597
+ break;
598
+ }
599
+
600
+ if (search.argmode == ARG_FILE && auxpaths != NULL) {
601
+ if (strcmp(auxpaths, "doc") == 0)
602
+ options |= MPARSE_MDOC;
603
+ else if (strcmp(auxpaths, "an") == 0)
604
+ options |= MPARSE_MAN;
605
+ }
606
+
607
+ mchars_alloc();
608
+ mp = mparse_alloc(options, os_e, os_s);
609
+
610
+ /*
611
+ * Remember the original working directory, if possible.
612
+ * This will be needed if some names on the command line
613
+ * are page names and some are relative file names.
614
+ * Do not error out if the current directory is not
615
+ * readable: Maybe it won't be needed after all.
616
+ */
617
+ startdir = open(".", O_RDONLY | O_DIRECTORY);
618
+ for (i = 0; i < ressz; i++) {
619
+ process_onefile(mp, res + i, startdir, &outst, &conf);
620
+ if (outst.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK)
621
+ break;
622
+ }
623
+ if (startdir != -1) {
624
+ (void)fchdir(startdir);
625
+ close(startdir);
626
+ }
627
+ if (conf.output.tag != NULL && conf.output.tag_found == 0) {
628
+ mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", conf.output.tag);
629
+ conf.output.tag = NULL;
630
+ }
631
+ if (outst.outdata != NULL) {
632
+ switch (outst.outtype) {
633
+ case OUTT_HTML:
634
+ html_free(outst.outdata);
635
+ break;
636
+ case OUTT_UTF8:
637
+ case OUTT_LOCALE:
638
+ case OUTT_ASCII:
639
+ ascii_free(outst.outdata);
640
+ break;
641
+ case OUTT_PDF:
642
+ case OUTT_PS:
643
+ pspdf_free(outst.outdata);
644
+ break;
645
+ default:
646
+ break;
647
+ }
648
+ }
649
+ mandoc_xr_free();
650
+ mparse_free(mp);
651
+ mchars_free();
652
+
653
+ out:
654
+ mansearch_free(res, ressz);
655
+ if (search.argmode != ARG_FILE)
656
+ manconf_free(&conf);
657
+
658
+ if (outst.tag_files != NULL) {
659
+ if (term_tag_close() != -1 &&
660
+ conf.output.outfilename == NULL &&
661
+ conf.output.tagfilename == NULL)
662
+ run_pager(&outst, conf.output.tag);
663
+ term_tag_unlink();
664
+ } else if (outst.had_output && outst.outtype != OUTT_LINT)
665
+ mandoc_msg_summary();
666
+
667
+ return (int)mandoc_msg_getrc();
668
+ }
669
+
670
+ static void
671
+ usage(enum argmode argmode)
672
+ {
673
+ switch (argmode) {
674
+ case ARG_FILE:
675
+ fputs("usage: mandoc [-ac] [-I os=name] "
676
+ "[-K encoding] [-mdoc | -man] [-O options]\n"
677
+ "\t [-T output] [-W level] [file ...]\n", stderr);
678
+ break;
679
+ case ARG_NAME:
680
+ fputs("usage: man [-acfhklw] [-C file] [-M path] "
681
+ "[-m path] [-S subsection]\n"
682
+ "\t [[-s] section] name ...\n", stderr);
683
+ break;
684
+ case ARG_WORD:
685
+ fputs("usage: whatis [-afk] [-C file] "
686
+ "[-M path] [-m path] [-O outkey] [-S arch]\n"
687
+ "\t [-s section] name ...\n", stderr);
688
+ break;
689
+ case ARG_EXPR:
690
+ fputs("usage: apropos [-afk] [-C file] "
691
+ "[-M path] [-m path] [-O outkey] [-S arch]\n"
692
+ "\t [-s section] expression ...\n", stderr);
693
+ break;
694
+ }
695
+ exit((int)MANDOCLEVEL_BADARG);
696
+ }
697
+
698
+ static void
699
+ glob_esc(char **dst, const char *src, const char *suffix)
700
+ {
701
+ while (*src != '\0') {
702
+ if (strchr("*?[", *src) != NULL)
703
+ *(*dst)++ = '\\';
704
+ *(*dst)++ = *src++;
705
+ }
706
+ while (*suffix != '\0')
707
+ *(*dst)++ = *suffix++;
708
+ }
709
+
710
+ static void
711
+ fs_append(char **file, size_t filesz, int copy, size_t ipath,
712
+ const char *sec, enum form form, struct manpage **res, size_t *ressz)
713
+ {
714
+ struct manpage *page;
715
+
716
+ *res = mandoc_reallocarray(*res, *ressz + filesz, sizeof(**res));
717
+ page = *res + *ressz;
718
+ *ressz += filesz;
719
+ for (;;) {
720
+ page->file = copy ? mandoc_strdup(*file) : *file;
721
+ page->names = NULL;
722
+ page->output = NULL;
723
+ page->bits = NAME_FILE & NAME_MASK;
724
+ page->ipath = ipath;
725
+ page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10;
726
+ page->form = form;
727
+ if (--filesz == 0)
728
+ break;
729
+ file++;
730
+ page++;
731
+ }
732
+ }
733
+
734
+ static int
735
+ fs_lookup(const struct manpaths *paths, size_t ipath,
736
+ const char *sec, const char *arch, const char *name,
737
+ struct manpage **res, size_t *ressz)
738
+ {
739
+ struct stat sb;
740
+ glob_t globinfo;
741
+ char *file, *cp, secnum[2];
742
+ int globres;
743
+ enum form form;
744
+
745
+ const char *const slman = "/man";
746
+ const char *const slash = "/";
747
+ const char *const sglob = ".[01-9]*";
748
+ const char *const dot = ".";
749
+ const char *const aster = "*";
750
+
751
+ memset(&globinfo, 0, sizeof(globinfo));
752
+ form = FORM_SRC;
753
+
754
+ mandoc_asprintf(&file, "%s/man%s/%s.%s",
755
+ paths->paths[ipath], sec, name, sec);
756
+ if (stat(file, &sb) != -1)
757
+ goto found;
758
+ free(file);
759
+
760
+ mandoc_asprintf(&file, "%s/cat%s/%s.0",
761
+ paths->paths[ipath], sec, name);
762
+ if (stat(file, &sb) != -1) {
763
+ form = FORM_CAT;
764
+ goto found;
765
+ }
766
+ free(file);
767
+
768
+ if (arch != NULL) {
769
+ mandoc_asprintf(&file, "%s/man%s/%s/%s.%s",
770
+ paths->paths[ipath], sec, arch, name, sec);
771
+ if (stat(file, &sb) != -1)
772
+ goto found;
773
+ free(file);
774
+ }
775
+
776
+ cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 +
777
+ strlen(slman) + strlen(sec) * 2 + strlen(slash) +
778
+ strlen(name) * 2 + strlen(sglob) + 1);
779
+ glob_esc(&cp, paths->paths[ipath], slman);
780
+ glob_esc(&cp, sec, slash);
781
+ glob_esc(&cp, name, sglob);
782
+ *cp = '\0';
783
+ globres = glob(file, 0, NULL, &globinfo);
784
+ if (globres != 0 && globres != GLOB_NOMATCH)
785
+ mandoc_msg(MANDOCERR_GLOB, 0, 0,
786
+ "%s: %s", file, strerror(errno));
787
+ free(file);
788
+ file = NULL;
789
+ if (globres == 0)
790
+ goto found;
791
+ globfree(&globinfo);
792
+
793
+ if (sec[1] != '\0' && *ressz == 0) {
794
+ secnum[0] = sec[0];
795
+ secnum[1] = '\0';
796
+ cp = file = mandoc_malloc(strlen(paths->paths[ipath]) * 2 +
797
+ strlen(slman) + strlen(secnum) * 2 + strlen(slash) +
798
+ strlen(name) * 2 + strlen(dot) +
799
+ strlen(sec) * 2 + strlen(aster) + 1);
800
+ glob_esc(&cp, paths->paths[ipath], slman);
801
+ glob_esc(&cp, secnum, slash);
802
+ glob_esc(&cp, name, dot);
803
+ glob_esc(&cp, sec, aster);
804
+ *cp = '\0';
805
+ globres = glob(file, 0, NULL, &globinfo);
806
+ if (globres != 0 && globres != GLOB_NOMATCH)
807
+ mandoc_msg(MANDOCERR_GLOB, 0, 0,
808
+ "%s: %s", file, strerror(errno));
809
+ free(file);
810
+ file = NULL;
811
+ if (globres == 0)
812
+ goto found;
813
+ globfree(&globinfo);
814
+ }
815
+
816
+ if (res != NULL || ipath + 1 != paths->sz)
817
+ return -1;
818
+
819
+ mandoc_asprintf(&file, "%s.%s", name, sec);
820
+ globres = stat(file, &sb);
821
+ free(file);
822
+ return globres;
823
+
824
+ found:
825
+ warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s",
826
+ name, sec, BINM_MAKEWHATIS, paths->paths[ipath]);
827
+ if (res == NULL)
828
+ free(file);
829
+ else if (file == NULL)
830
+ fs_append(globinfo.gl_pathv, globinfo.gl_pathc, 1,
831
+ ipath, sec, form, res, ressz);
832
+ else
833
+ fs_append(&file, 1, 0, ipath, sec, form, res, ressz);
834
+ globfree(&globinfo);
835
+ return 0;
836
+ }
837
+
838
+ static int
839
+ fs_search(const struct mansearch *cfg, const struct manpaths *paths,
840
+ const char *name, struct manpage **res, size_t *ressz)
841
+ {
842
+ const char *const sections[] =
843
+ {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"};
844
+ const size_t nsec = sizeof(sections)/sizeof(sections[0]);
845
+
846
+ size_t ipath, isec;
847
+
848
+ assert(cfg->argmode == ARG_NAME);
849
+ if (res != NULL)
850
+ *res = NULL;
851
+ *ressz = 0;
852
+ for (ipath = 0; ipath < paths->sz; ipath++) {
853
+ if (cfg->sec != NULL) {
854
+ if (fs_lookup(paths, ipath, cfg->sec, cfg->arch,
855
+ name, res, ressz) != -1 && cfg->firstmatch)
856
+ return 0;
857
+ } else {
858
+ for (isec = 0; isec < nsec; isec++)
859
+ if (fs_lookup(paths, ipath, sections[isec],
860
+ cfg->arch, name, res, ressz) != -1 &&
861
+ cfg->firstmatch)
862
+ return 0;
863
+ }
864
+ }
865
+ return -1;
866
+ }
867
+
868
+ static void
869
+ process_onefile(struct mparse *mp, struct manpage *resp, int startdir,
870
+ struct outstate *outst, struct manconf *conf)
871
+ {
872
+ int fd;
873
+
874
+ /*
875
+ * Changing directories is not needed in ARG_FILE mode.
876
+ * Do it on a best-effort basis. Even in case of
877
+ * failure, some functionality may still work.
878
+ */
879
+ if (resp->ipath != SIZE_MAX)
880
+ (void)chdir(conf->manpath.paths[resp->ipath]);
881
+ else if (startdir != -1)
882
+ (void)fchdir(startdir);
883
+
884
+ mandoc_msg_setinfilename(resp->file);
885
+ if (resp->file != NULL) {
886
+ if ((fd = mparse_open(mp, resp->file)) == -1) {
887
+ mandoc_msg(resp->ipath == SIZE_MAX ?
888
+ MANDOCERR_BADARG_BAD : MANDOCERR_OPEN,
889
+ 0, 0, "%s", strerror(errno));
890
+ mandoc_msg_setinfilename(NULL);
891
+ return;
892
+ }
893
+ } else
894
+ fd = STDIN_FILENO;
895
+
896
+ if (outst->use_pager) {
897
+ outst->use_pager = 0;
898
+ outst->tag_files = term_tag_init(conf->output.outfilename,
899
+ outst->outtype == OUTT_HTML ? ".html" : "",
900
+ conf->output.tagfilename);
901
+ #if HAVE_PLEDGE
902
+ if ((conf->output.outfilename != NULL ||
903
+ conf->output.tagfilename != NULL) &&
904
+ pledge("stdio rpath cpath", NULL) == -1) {
905
+ mandoc_msg(MANDOCERR_PLEDGE, 0, 0,
906
+ "%s", strerror(errno));
907
+ exit(mandoc_msg_getrc());
908
+ }
909
+ #endif
910
+ }
911
+ if (outst->had_output && outst->outtype <= OUTT_UTF8) {
912
+ if (outst->outdata == NULL)
913
+ outdata_alloc(outst, &conf->output);
914
+ terminal_sepline(outst->outdata);
915
+ }
916
+
917
+ if (resp->form == FORM_SRC)
918
+ parse(mp, fd, resp->file, outst, conf);
919
+ else {
920
+ passthrough(fd, conf->output.synopsisonly);
921
+ outst->had_output = 1;
922
+ }
923
+
924
+ if (ferror(stdout)) {
925
+ if (outst->tag_files != NULL) {
926
+ mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s: %s",
927
+ outst->tag_files->ofn, strerror(errno));
928
+ term_tag_unlink();
929
+ outst->tag_files = NULL;
930
+ } else
931
+ mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s",
932
+ strerror(errno));
933
+ }
934
+ mandoc_msg_setinfilename(NULL);
935
+ }
936
+
937
+ static void
938
+ parse(struct mparse *mp, int fd, const char *file,
939
+ struct outstate *outst, struct manconf *conf)
940
+ {
941
+ static struct manpaths basepaths;
942
+ static int previous;
943
+ struct roff_meta *meta;
944
+
945
+ assert(fd >= 0);
946
+ if (file == NULL)
947
+ file = "<stdin>";
948
+
949
+ if (previous)
950
+ mparse_reset(mp);
951
+ else
952
+ previous = 1;
953
+
954
+ mparse_readfd(mp, fd, file);
955
+ if (fd != STDIN_FILENO)
956
+ close(fd);
957
+
958
+ /*
959
+ * With -Wstop and warnings or errors of at least the requested
960
+ * level, do not produce output.
961
+ */
962
+
963
+ if (outst->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK)
964
+ return;
965
+
966
+ if (outst->outdata == NULL)
967
+ outdata_alloc(outst, &conf->output);
968
+ else if (outst->outtype == OUTT_HTML)
969
+ html_reset(outst->outdata);
970
+
971
+ mandoc_xr_reset();
972
+ meta = mparse_result(mp);
973
+
974
+ /* Execute the out device, if it exists. */
975
+
976
+ outst->had_output = 1;
977
+ if (meta->macroset == MACROSET_MDOC) {
978
+ switch (outst->outtype) {
979
+ case OUTT_HTML:
980
+ html_mdoc(outst->outdata, meta);
981
+ break;
982
+ case OUTT_TREE:
983
+ tree_mdoc(outst->outdata, meta);
984
+ break;
985
+ case OUTT_MAN:
986
+ man_mdoc(outst->outdata, meta);
987
+ break;
988
+ case OUTT_PDF:
989
+ case OUTT_ASCII:
990
+ case OUTT_UTF8:
991
+ case OUTT_LOCALE:
992
+ case OUTT_PS:
993
+ terminal_mdoc(outst->outdata, meta);
994
+ break;
995
+ case OUTT_MARKDOWN:
996
+ markdown_mdoc(outst->outdata, meta);
997
+ break;
998
+ default:
999
+ break;
1000
+ }
1001
+ }
1002
+ if (meta->macroset == MACROSET_MAN) {
1003
+ switch (outst->outtype) {
1004
+ case OUTT_HTML:
1005
+ html_man(outst->outdata, meta);
1006
+ break;
1007
+ case OUTT_TREE:
1008
+ tree_man(outst->outdata, meta);
1009
+ break;
1010
+ case OUTT_MAN:
1011
+ mparse_copy(mp);
1012
+ break;
1013
+ case OUTT_PDF:
1014
+ case OUTT_ASCII:
1015
+ case OUTT_UTF8:
1016
+ case OUTT_LOCALE:
1017
+ case OUTT_PS:
1018
+ terminal_man(outst->outdata, meta);
1019
+ break;
1020
+ case OUTT_MARKDOWN:
1021
+ mandoc_msg(MANDOCERR_MAN_TMARKDOWN, 0, 0, NULL);
1022
+ break;
1023
+ default:
1024
+ break;
1025
+ }
1026
+ }
1027
+ if (conf->output.tag != NULL && conf->output.tag_found == 0 &&
1028
+ tag_exists(conf->output.tag))
1029
+ conf->output.tag_found = 1;
1030
+
1031
+ if (mandoc_msg_getmin() < MANDOCERR_STYLE) {
1032
+ if (basepaths.sz == 0)
1033
+ manpath_base(&basepaths);
1034
+ check_xr(&basepaths);
1035
+ } else if (mandoc_msg_getmin() < MANDOCERR_WARNING)
1036
+ check_xr(&conf->manpath);
1037
+ }
1038
+
1039
+ static void
1040
+ check_xr(struct manpaths *paths)
1041
+ {
1042
+ struct mansearch search;
1043
+ struct mandoc_xr *xr;
1044
+ size_t sz;
1045
+
1046
+ for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) {
1047
+ if (xr->line == -1)
1048
+ continue;
1049
+ search.arch = NULL;
1050
+ search.sec = xr->sec;
1051
+ search.outkey = NULL;
1052
+ search.argmode = ARG_NAME;
1053
+ search.firstmatch = 1;
1054
+ if (mansearch(&search, paths, 1, &xr->name, NULL, &sz))
1055
+ continue;
1056
+ if (fs_search(&search, paths, xr->name, NULL, &sz) != -1)
1057
+ continue;
1058
+ if (xr->count == 1)
1059
+ mandoc_msg(MANDOCERR_XR_BAD, xr->line,
1060
+ xr->pos + 1, "Xr %s %s", xr->name, xr->sec);
1061
+ else
1062
+ mandoc_msg(MANDOCERR_XR_BAD, xr->line,
1063
+ xr->pos + 1, "Xr %s %s (%d times)",
1064
+ xr->name, xr->sec, xr->count);
1065
+ }
1066
+ }
1067
+
1068
+ static void
1069
+ outdata_alloc(struct outstate *outst, struct manoutput *outconf)
1070
+ {
1071
+ switch (outst->outtype) {
1072
+ case OUTT_HTML:
1073
+ outst->outdata = html_alloc(outconf);
1074
+ break;
1075
+ case OUTT_UTF8:
1076
+ outst->outdata = utf8_alloc(outconf);
1077
+ break;
1078
+ case OUTT_LOCALE:
1079
+ outst->outdata = locale_alloc(outconf);
1080
+ break;
1081
+ case OUTT_ASCII:
1082
+ outst->outdata = ascii_alloc(outconf);
1083
+ break;
1084
+ case OUTT_PDF:
1085
+ outst->outdata = pdf_alloc(outconf);
1086
+ break;
1087
+ case OUTT_PS:
1088
+ outst->outdata = ps_alloc(outconf);
1089
+ break;
1090
+ default:
1091
+ break;
1092
+ }
1093
+ }
1094
+
1095
+ static void
1096
+ passthrough(int fd, int synopsis_only)
1097
+ {
1098
+ const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS";
1099
+ const char synr[] = "SYNOPSIS";
1100
+
1101
+ FILE *stream;
1102
+ char *line, *cp;
1103
+ size_t linesz;
1104
+ ssize_t len, written;
1105
+ int lno, print;
1106
+
1107
+ stream = NULL;
1108
+ line = NULL;
1109
+ linesz = 0;
1110
+
1111
+ if (fflush(stdout) == EOF) {
1112
+ mandoc_msg(MANDOCERR_FFLUSH, 0, 0, "%s", strerror(errno));
1113
+ goto done;
1114
+ }
1115
+ if ((stream = fdopen(fd, "r")) == NULL) {
1116
+ close(fd);
1117
+ mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno));
1118
+ goto done;
1119
+ }
1120
+
1121
+ lno = print = 0;
1122
+ while ((len = getline(&line, &linesz, stream)) != -1) {
1123
+ lno++;
1124
+ cp = line;
1125
+ if (synopsis_only) {
1126
+ if (print) {
1127
+ if ( ! isspace((unsigned char)*cp))
1128
+ goto done;
1129
+ while (isspace((unsigned char)*cp)) {
1130
+ cp++;
1131
+ len--;
1132
+ }
1133
+ } else {
1134
+ if (strcmp(cp, synb) == 0 ||
1135
+ strcmp(cp, synr) == 0)
1136
+ print = 1;
1137
+ continue;
1138
+ }
1139
+ }
1140
+ for (; len > 0; len -= written) {
1141
+ if ((written = write(STDOUT_FILENO, cp, len)) == -1) {
1142
+ mandoc_msg(MANDOCERR_WRITE, 0, 0,
1143
+ "%s", strerror(errno));
1144
+ goto done;
1145
+ }
1146
+ }
1147
+ }
1148
+ if (ferror(stream))
1149
+ mandoc_msg(MANDOCERR_GETLINE, lno, 0, "%s", strerror(errno));
1150
+
1151
+ done:
1152
+ free(line);
1153
+ if (stream != NULL)
1154
+ fclose(stream);
1155
+ }
1156
+
1157
+ static int
1158
+ woptions(char *arg, enum mandoc_os *os_e, int *wstop)
1159
+ {
1160
+ char *v, *o;
1161
+ const char *toks[11];
1162
+
1163
+ toks[0] = "stop";
1164
+ toks[1] = "all";
1165
+ toks[2] = "base";
1166
+ toks[3] = "style";
1167
+ toks[4] = "warning";
1168
+ toks[5] = "error";
1169
+ toks[6] = "unsupp";
1170
+ toks[7] = "fatal";
1171
+ toks[8] = "openbsd";
1172
+ toks[9] = "netbsd";
1173
+ toks[10] = NULL;
1174
+
1175
+ while (*arg) {
1176
+ o = arg;
1177
+ switch (getsubopt(&arg, (char * const *)toks, &v)) {
1178
+ case 0:
1179
+ *wstop = 1;
1180
+ break;
1181
+ case 1:
1182
+ case 2:
1183
+ mandoc_msg_setmin(MANDOCERR_BASE);
1184
+ break;
1185
+ case 3:
1186
+ mandoc_msg_setmin(MANDOCERR_STYLE);
1187
+ break;
1188
+ case 4:
1189
+ mandoc_msg_setmin(MANDOCERR_WARNING);
1190
+ break;
1191
+ case 5:
1192
+ mandoc_msg_setmin(MANDOCERR_ERROR);
1193
+ break;
1194
+ case 6:
1195
+ mandoc_msg_setmin(MANDOCERR_UNSUPP);
1196
+ break;
1197
+ case 7:
1198
+ mandoc_msg_setmin(MANDOCERR_BADARG);
1199
+ break;
1200
+ case 8:
1201
+ mandoc_msg_setmin(MANDOCERR_BASE);
1202
+ *os_e = MANDOC_OS_OPENBSD;
1203
+ break;
1204
+ case 9:
1205
+ mandoc_msg_setmin(MANDOCERR_BASE);
1206
+ *os_e = MANDOC_OS_NETBSD;
1207
+ break;
1208
+ default:
1209
+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-W %s", o);
1210
+ return -1;
1211
+ }
1212
+ }
1213
+ return 0;
1214
+ }
1215
+
1216
+ /*
1217
+ * Wait until moved to the foreground,
1218
+ * then fork the pager and wait for the user to close it.
1219
+ */
1220
+ static void
1221
+ run_pager(struct outstate *outst, char *tag_target)
1222
+ {
1223
+ int signum, status;
1224
+ pid_t man_pgid, tc_pgid;
1225
+ pid_t pager_pid, wait_pid;
1226
+
1227
+ man_pgid = getpgid(0);
1228
+ outst->tag_files->tcpgid =
1229
+ man_pgid == getpid() ? getpgid(getppid()) : man_pgid;
1230
+ pager_pid = 0;
1231
+ signum = SIGSTOP;
1232
+
1233
+ for (;;) {
1234
+ /* Stop here until moved to the foreground. */
1235
+
1236
+ tc_pgid = tcgetpgrp(STDOUT_FILENO);
1237
+ if (tc_pgid != man_pgid) {
1238
+ if (tc_pgid == pager_pid) {
1239
+ (void)tcsetpgrp(STDOUT_FILENO, man_pgid);
1240
+ if (signum == SIGTTIN)
1241
+ continue;
1242
+ } else
1243
+ outst->tag_files->tcpgid = tc_pgid;
1244
+ kill(0, signum);
1245
+ continue;
1246
+ }
1247
+
1248
+ /* Once in the foreground, activate the pager. */
1249
+
1250
+ if (pager_pid) {
1251
+ (void)tcsetpgrp(STDOUT_FILENO, pager_pid);
1252
+ kill(pager_pid, SIGCONT);
1253
+ } else
1254
+ pager_pid = spawn_pager(outst, tag_target);
1255
+
1256
+ /* Wait for the pager to stop or exit. */
1257
+
1258
+ while ((wait_pid = waitpid(pager_pid, &status,
1259
+ WUNTRACED)) == -1 && errno == EINTR)
1260
+ continue;
1261
+
1262
+ if (wait_pid == -1) {
1263
+ mandoc_msg(MANDOCERR_WAIT, 0, 0,
1264
+ "%s", strerror(errno));
1265
+ break;
1266
+ }
1267
+ if (!WIFSTOPPED(status))
1268
+ break;
1269
+
1270
+ signum = WSTOPSIG(status);
1271
+ }
1272
+ }
1273
+
1274
+ static pid_t
1275
+ spawn_pager(struct outstate *outst, char *tag_target)
1276
+ {
1277
+ const struct timespec timeout = { 0, 100000000 }; /* 0.1s */
1278
+ #define MAX_PAGER_ARGS 16
1279
+ char *argv[MAX_PAGER_ARGS];
1280
+ const char *pager;
1281
+ char *cp;
1282
+ #if HAVE_LESS_T
1283
+ size_t cmdlen;
1284
+ #endif
1285
+ int argc, use_ofn;
1286
+ pid_t pager_pid;
1287
+
1288
+ assert(outst->tag_files->ofd == -1);
1289
+ assert(outst->tag_files->tfs == NULL);
1290
+
1291
+ pager = getenv("MANPAGER");
1292
+ if (pager == NULL || *pager == '\0')
1293
+ pager = getenv("PAGER");
1294
+ if (pager == NULL || *pager == '\0')
1295
+ pager = BINM_PAGER;
1296
+ cp = mandoc_strdup(pager);
1297
+
1298
+ /*
1299
+ * Parse the pager command into words.
1300
+ * Intentionally do not do anything fancy here.
1301
+ */
1302
+
1303
+ argc = 0;
1304
+ while (argc + 5 < MAX_PAGER_ARGS) {
1305
+ argv[argc++] = cp;
1306
+ cp = strchr(cp, ' ');
1307
+ if (cp == NULL)
1308
+ break;
1309
+ *cp++ = '\0';
1310
+ while (*cp == ' ')
1311
+ cp++;
1312
+ if (*cp == '\0')
1313
+ break;
1314
+ }
1315
+
1316
+ /* For less(1), use the tag file. */
1317
+
1318
+ use_ofn = 1;
1319
+ #if HAVE_LESS_T
1320
+ if (*outst->tag_files->tfn != '\0' &&
1321
+ (cmdlen = strlen(argv[0])) >= 4) {
1322
+ cp = argv[0] + cmdlen - 4;
1323
+ if (strcmp(cp, "less") == 0) {
1324
+ argv[argc++] = mandoc_strdup("-T");
1325
+ argv[argc++] = outst->tag_files->tfn;
1326
+ if (tag_target != NULL) {
1327
+ argv[argc++] = mandoc_strdup("-t");
1328
+ argv[argc++] = tag_target;
1329
+ use_ofn = 0;
1330
+ }
1331
+ }
1332
+ }
1333
+ #endif
1334
+ if (use_ofn) {
1335
+ if (outst->outtype == OUTT_HTML && tag_target != NULL)
1336
+ mandoc_asprintf(&argv[argc], "file://%s#%s",
1337
+ outst->tag_files->ofn, tag_target);
1338
+ else
1339
+ argv[argc] = outst->tag_files->ofn;
1340
+ argc++;
1341
+ }
1342
+ argv[argc] = NULL;
1343
+
1344
+ switch (pager_pid = fork()) {
1345
+ case -1:
1346
+ mandoc_msg(MANDOCERR_FORK, 0, 0, "%s", strerror(errno));
1347
+ exit(mandoc_msg_getrc());
1348
+ case 0:
1349
+ break;
1350
+ default:
1351
+ (void)setpgid(pager_pid, 0);
1352
+ (void)tcsetpgrp(STDOUT_FILENO, pager_pid);
1353
+ #if HAVE_PLEDGE
1354
+ if (pledge("stdio rpath tmppath tty proc", NULL) == -1) {
1355
+ mandoc_msg(MANDOCERR_PLEDGE, 0, 0,
1356
+ "%s", strerror(errno));
1357
+ exit(mandoc_msg_getrc());
1358
+ }
1359
+ #endif
1360
+ outst->tag_files->pager_pid = pager_pid;
1361
+ return pager_pid;
1362
+ }
1363
+
1364
+ /*
1365
+ * The child process becomes the pager.
1366
+ * Do not start it before controlling the terminal.
1367
+ */
1368
+
1369
+ while (tcgetpgrp(STDOUT_FILENO) != getpid())
1370
+ nanosleep(&timeout, NULL);
1371
+
1372
+ execvp(argv[0], argv);
1373
+ mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", argv[0], strerror(errno));
1374
+ _exit(mandoc_msg_getrc());
1375
+ }