GCC Code Coverage Report | |||||||||||||||||||||
|
|||||||||||||||||||||
Line | Branch | Exec | Source |
1 |
/* $OpenBSD: main.c,v 1.205 2017/08/21 15:41:26 schwarze Exp $ */ |
||
2 |
/* |
||
3 |
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv> |
||
4 |
* Copyright (c) 2010-2012, 2014-2017 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 |
|||
20 |
#include <sys/types.h> |
||
21 |
#include <sys/param.h> /* MACHINE */ |
||
22 |
#include <sys/wait.h> |
||
23 |
|||
24 |
#include <assert.h> |
||
25 |
#include <ctype.h> |
||
26 |
#include <err.h> |
||
27 |
#include <errno.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 <time.h> |
||
36 |
#include <unistd.h> |
||
37 |
|||
38 |
#include "mandoc_aux.h" |
||
39 |
#include "mandoc.h" |
||
40 |
#include "mandoc_xr.h" |
||
41 |
#include "roff.h" |
||
42 |
#include "mdoc.h" |
||
43 |
#include "man.h" |
||
44 |
#include "tag.h" |
||
45 |
#include "main.h" |
||
46 |
#include "manconf.h" |
||
47 |
#include "mansearch.h" |
||
48 |
|||
49 |
enum outmode { |
||
50 |
OUTMODE_DEF = 0, |
||
51 |
OUTMODE_FLN, |
||
52 |
OUTMODE_LST, |
||
53 |
OUTMODE_ALL, |
||
54 |
OUTMODE_ONE |
||
55 |
}; |
||
56 |
|||
57 |
enum outt { |
||
58 |
OUTT_ASCII = 0, /* -Tascii */ |
||
59 |
OUTT_LOCALE, /* -Tlocale */ |
||
60 |
OUTT_UTF8, /* -Tutf8 */ |
||
61 |
OUTT_TREE, /* -Ttree */ |
||
62 |
OUTT_MAN, /* -Tman */ |
||
63 |
OUTT_HTML, /* -Thtml */ |
||
64 |
OUTT_MARKDOWN, /* -Tmarkdown */ |
||
65 |
OUTT_LINT, /* -Tlint */ |
||
66 |
OUTT_PS, /* -Tps */ |
||
67 |
OUTT_PDF /* -Tpdf */ |
||
68 |
}; |
||
69 |
|||
70 |
struct curparse { |
||
71 |
struct mparse *mp; |
||
72 |
struct manoutput *outopts; /* output options */ |
||
73 |
void *outdata; /* data for output */ |
||
74 |
char *os_s; /* operating system for display */ |
||
75 |
int wstop; /* stop after a file with a warning */ |
||
76 |
enum mandocerr mmin; /* ignore messages below this */ |
||
77 |
enum mandoc_os os_e; /* check base system conventions */ |
||
78 |
enum outt outtype; /* which output to use */ |
||
79 |
}; |
||
80 |
|||
81 |
|||
82 |
int mandocdb(int, char *[]); |
||
83 |
|||
84 |
static void check_xr(const char *); |
||
85 |
static int fs_lookup(const struct manpaths *, |
||
86 |
size_t ipath, const char *, |
||
87 |
const char *, const char *, |
||
88 |
struct manpage **, size_t *); |
||
89 |
static int fs_search(const struct mansearch *, |
||
90 |
const struct manpaths *, int, char**, |
||
91 |
struct manpage **, size_t *); |
||
92 |
static int koptions(int *, char *); |
||
93 |
static void moptions(int *, char *); |
||
94 |
static void mmsg(enum mandocerr, enum mandoclevel, |
||
95 |
const char *, int, int, const char *); |
||
96 |
static void outdata_alloc(struct curparse *); |
||
97 |
static void parse(struct curparse *, int, const char *); |
||
98 |
static void passthrough(const char *, int, int); |
||
99 |
static pid_t spawn_pager(struct tag_files *); |
||
100 |
static int toptions(struct curparse *, char *); |
||
101 |
static void usage(enum argmode) __attribute__((__noreturn__)); |
||
102 |
static int woptions(struct curparse *, char *); |
||
103 |
|||
104 |
static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; |
||
105 |
static char help_arg[] = "help"; |
||
106 |
static char *help_argv[] = {help_arg, NULL}; |
||
107 |
static enum mandoclevel rc; |
||
108 |
static FILE *mmsg_stream; |
||
109 |
|||
110 |
|||
111 |
int |
||
112 |
main(int argc, char *argv[]) |
||
113 |
{ |
||
114 |
25758 |
struct manconf conf; |
|
115 |
12879 |
struct mansearch search; |
|
116 |
12879 |
struct curparse curp; |
|
117 |
struct tag_files *tag_files; |
||
118 |
12879 |
struct manpage *res, *resp; |
|
119 |
const char *progname, *sec, *thisarg; |
||
120 |
char *conf_file, *defpaths, *auxpaths; |
||
121 |
12879 |
char *oarg; |
|
122 |
unsigned char *uc; |
||
123 |
12879 |
size_t i, sz; |
|
124 |
int prio, best_prio; |
||
125 |
enum outmode outmode; |
||
126 |
int fd; |
||
127 |
int show_usage; |
||
128 |
12879 |
int options; |
|
129 |
int use_pager; |
||
130 |
12879 |
int status, signum; |
|
131 |
int c; |
||
132 |
pid_t pager_pid, tc_pgid, man_pgid, pid; |
||
133 |
|||
134 |
12879 |
progname = getprogname(); |
|
135 |
✓✗✓✓ |
25758 |
if (strncmp(progname, "mandocdb", 8) == 0 || |
136 |
12879 |
strncmp(progname, "makewhatis", 10) == 0) |
|
137 |
96 |
return mandocdb(argc, argv); |
|
138 |
|||
139 |
✗✓ | 12783 |
if (pledge("stdio rpath tmppath tty proc exec flock cpath wpath", NULL) == -1) |
140 |
err((int)MANDOCLEVEL_SYSERR, "pledge"); |
||
141 |
|||
142 |
/* Search options. */ |
||
143 |
|||
144 |
12783 |
memset(&conf, 0, sizeof(conf)); |
|
145 |
conf_file = defpaths = NULL; |
||
146 |
auxpaths = NULL; |
||
147 |
|||
148 |
12783 |
memset(&search, 0, sizeof(struct mansearch)); |
|
149 |
12783 |
search.outkey = "Nd"; |
|
150 |
12783 |
oarg = NULL; |
|
151 |
|||
152 |
✓✓ | 12783 |
if (strcmp(progname, "man") == 0) |
153 |
237 |
search.argmode = ARG_NAME; |
|
154 |
✗✓ | 12546 |
else if (strncmp(progname, "apropos", 7) == 0) |
155 |
search.argmode = ARG_EXPR; |
||
156 |
✗✓ | 12546 |
else if (strncmp(progname, "whatis", 6) == 0) |
157 |
search.argmode = ARG_WORD; |
||
158 |
12546 |
else if (strncmp(progname, "help", 4) == 0) |
|
159 |
search.argmode = ARG_NAME; |
||
160 |
else |
||
161 |
search.argmode = ARG_FILE; |
||
162 |
|||
163 |
/* Parser and formatter options. */ |
||
164 |
|||
165 |
12783 |
memset(&curp, 0, sizeof(struct curparse)); |
|
166 |
12783 |
curp.outtype = OUTT_LOCALE; |
|
167 |
12783 |
curp.mmin = MANDOCERR_MAX; |
|
168 |
12783 |
curp.outopts = &conf.output; |
|
169 |
12783 |
options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; |
|
170 |
12783 |
mmsg_stream = stderr; |
|
171 |
|||
172 |
use_pager = 1; |
||
173 |
tag_files = NULL; |
||
174 |
show_usage = 0; |
||
175 |
outmode = OUTMODE_DEF; |
||
176 |
|||
177 |
✓✓ | 97707 |
while ((c = getopt(argc, argv, |
178 |
42462 |
"aC:cfhI:iK:klM:m:O:S:s:T:VW:w")) != -1) { |
|
179 |
✗✓✗✗ |
29679 |
if (c == 'i' && search.argmode == ARG_EXPR) { |
180 |
optind--; |
||
181 |
break; |
||
182 |
} |
||
183 |
✗✗✗✗ ✗✓✗✓ ✗✓✓✓ ✓✓✓✓ ✓✗ |
29679 |
switch (c) { |
184 |
case 'a': |
||
185 |
outmode = OUTMODE_ALL; |
||
186 |
break; |
||
187 |
case 'C': |
||
188 |
conf_file = optarg; |
||
189 |
break; |
||
190 |
case 'c': |
||
191 |
use_pager = 0; |
||
192 |
break; |
||
193 |
case 'f': |
||
194 |
search.argmode = ARG_WORD; |
||
195 |
break; |
||
196 |
case 'h': |
||
197 |
conf.output.synopsisonly = 1; |
||
198 |
use_pager = 0; |
||
199 |
outmode = OUTMODE_ALL; |
||
200 |
break; |
||
201 |
case 'I': |
||
202 |
✗✓ | 10431 |
if (strncmp(optarg, "os=", 3)) { |
203 |
warnx("-I %s: Bad argument", optarg); |
||
204 |
return (int)MANDOCLEVEL_BADARG; |
||
205 |
} |
||
206 |
✗✓ | 10431 |
if (curp.os_s != NULL) { |
207 |
warnx("-I %s: Duplicate argument", optarg); |
||
208 |
return (int)MANDOCLEVEL_BADARG; |
||
209 |
} |
||
210 |
10431 |
curp.os_s = mandoc_strdup(optarg + 3); |
|
211 |
10431 |
break; |
|
212 |
case 'K': |
||
213 |
if ( ! koptions(&options, optarg)) |
||
214 |
return (int)MANDOCLEVEL_BADARG; |
||
215 |
break; |
||
216 |
case 'k': |
||
217 |
140 |
search.argmode = ARG_EXPR; |
|
218 |
140 |
break; |
|
219 |
case 'l': |
||
220 |
search.argmode = ARG_FILE; |
||
221 |
outmode = OUTMODE_ALL; |
||
222 |
break; |
||
223 |
case 'M': |
||
224 |
230 |
defpaths = optarg; |
|
225 |
230 |
break; |
|
226 |
case 'm': |
||
227 |
2142 |
auxpaths = optarg; |
|
228 |
2142 |
break; |
|
229 |
case 'O': |
||
230 |
1904 |
oarg = optarg; |
|
231 |
1904 |
break; |
|
232 |
case 'S': |
||
233 |
30 |
search.arch = optarg; |
|
234 |
30 |
break; |
|
235 |
case 's': |
||
236 |
15 |
search.sec = optarg; |
|
237 |
15 |
break; |
|
238 |
case 'T': |
||
239 |
✓✗ | 12546 |
if ( ! toptions(&curp, optarg)) |
240 |
return (int)MANDOCLEVEL_BADARG; |
||
241 |
break; |
||
242 |
case 'W': |
||
243 |
✓✗ | 2061 |
if ( ! woptions(&curp, optarg)) |
244 |
return (int)MANDOCLEVEL_BADARG; |
||
245 |
break; |
||
246 |
case 'w': |
||
247 |
outmode = OUTMODE_FLN; |
||
248 |
180 |
break; |
|
249 |
default: |
||
250 |
show_usage = 1; |
||
251 |
break; |
||
252 |
} |
||
253 |
} |
||
254 |
|||
255 |
✗✓ | 12783 |
if (show_usage) |
256 |
usage(search.argmode); |
||
257 |
|||
258 |
/* Postprocess options. */ |
||
259 |
|||
260 |
✓✓ | 12783 |
if (outmode == OUTMODE_DEF) { |
261 |
✓✓✓ | 12603 |
switch (search.argmode) { |
262 |
case ARG_FILE: |
||
263 |
outmode = OUTMODE_ALL; |
||
264 |
use_pager = 0; |
||
265 |
12546 |
break; |
|
266 |
case ARG_NAME: |
||
267 |
outmode = OUTMODE_ONE; |
||
268 |
7 |
break; |
|
269 |
default: |
||
270 |
outmode = OUTMODE_LST; |
||
271 |
50 |
break; |
|
272 |
} |
||
273 |
} |
||
274 |
|||
275 |
✓✓ | 12783 |
if (oarg != NULL) { |
276 |
✓✓ | 1904 |
if (outmode == OUTMODE_LST) |
277 |
5 |
search.outkey = oarg; |
|
278 |
else { |
||
279 |
✓✓ | 3798 |
while (oarg != NULL) { |
280 |
thisarg = oarg; |
||
281 |
✓✗ | 3798 |
if (manconf_output(&conf.output, |
282 |
3798 |
strsep(&oarg, ","), 0) == 0) |
|
283 |
continue; |
||
284 |
warnx("-O %s: Bad argument", thisarg); |
||
285 |
return (int)MANDOCLEVEL_BADARG; |
||
286 |
} |
||
287 |
} |
||
288 |
} |
||
289 |
|||
290 |
✓✓✓✓ |
38119 |
if (outmode == OUTMODE_FLN || |
291 |
12783 |
outmode == OUTMODE_LST || |
|
292 |
12553 |
!isatty(STDOUT_FILENO)) |
|
293 |
12776 |
use_pager = 0; |
|
294 |
|||
295 |
✓✓ | 12783 |
if (!use_pager) |
296 |
✗✓ | 12776 |
if (pledge("stdio rpath flock cpath wpath", NULL) == -1) |
297 |
err((int)MANDOCLEVEL_SYSERR, "pledge"); |
||
298 |
|||
299 |
/* Parse arguments. */ |
||
300 |
|||
301 |
✓✗ | 12783 |
if (argc > 0) { |
302 |
12783 |
argc -= optind; |
|
303 |
12783 |
argv += optind; |
|
304 |
12783 |
} |
|
305 |
resp = NULL; |
||
306 |
|||
307 |
/* |
||
308 |
* Quirks for help(1) |
||
309 |
* and for a man(1) section argument without -s. |
||
310 |
*/ |
||
311 |
|||
312 |
✓✓ | 12783 |
if (search.argmode == ARG_NAME) { |
313 |
✗✓ | 97 |
if (*progname == 'h') { |
314 |
if (argc == 0) { |
||
315 |
argv = help_argv; |
||
316 |
argc = 1; |
||
317 |
} |
||
318 |
✗✓✗✗ |
97 |
} else if (argc > 1 && |
319 |
((uc = (unsigned char *)argv[0]) != NULL) && |
||
320 |
((isdigit(uc[0]) && (uc[1] == '\0' || |
||
321 |
(isalpha(uc[1]) && uc[2] == '\0'))) || |
||
322 |
(uc[0] == 'n' && uc[1] == '\0'))) { |
||
323 |
search.sec = (char *)uc; |
||
324 |
argv++; |
||
325 |
argc--; |
||
326 |
} |
||
327 |
✓✓ | 97 |
if (search.arch == NULL) |
328 |
72 |
search.arch = getenv("MACHINE"); |
|
329 |
✓✓ | 97 |
if (search.arch == NULL) |
330 |
72 |
search.arch = MACHINE; |
|
331 |
} |
||
332 |
|||
333 |
12783 |
rc = MANDOCLEVEL_OK; |
|
334 |
|||
335 |
/* man(1), whatis(1), apropos(1) */ |
||
336 |
|||
337 |
✓✓ | 12783 |
if (search.argmode != ARG_FILE) { |
338 |
✓✓ | 474 |
if (search.argmode == ARG_NAME && |
339 |
237 |
outmode == OUTMODE_ONE) |
|
340 |
7 |
search.firstmatch = 1; |
|
341 |
|||
342 |
/* Access the mandoc database. */ |
||
343 |
|||
344 |
237 |
manconf_parse(&conf, conf_file, defpaths, auxpaths); |
|
345 |
✗✓ | 237 |
if ( ! mansearch(&search, &conf.manpath, |
346 |
argc, argv, &res, &sz)) |
||
347 |
usage(search.argmode); |
||
348 |
|||
349 |
✓✓ | 237 |
if (sz == 0) { |
350 |
✓✓ | 27 |
if (search.argmode == ARG_NAME) |
351 |
17 |
fs_search(&search, &conf.manpath, |
|
352 |
argc, argv, &res, &sz); |
||
353 |
else |
||
354 |
10 |
warnx("nothing appropriate"); |
|
355 |
} |
||
356 |
|||
357 |
✓✓ | 237 |
if (sz == 0) { |
358 |
12 |
rc = MANDOCLEVEL_BADARG; |
|
359 |
12 |
goto out; |
|
360 |
} |
||
361 |
|||
362 |
/* |
||
363 |
* For standard man(1) and -a output mode, |
||
364 |
* prepare for copying filename pointers |
||
365 |
* into the program parameter array. |
||
366 |
*/ |
||
367 |
|||
368 |
✓✓ | 225 |
if (outmode == OUTMODE_ONE) { |
369 |
argc = 1; |
||
370 |
best_prio = 20; |
||
371 |
✗✓ | 225 |
} else if (outmode == OUTMODE_ALL) |
372 |
argc = (int)sz; |
||
373 |
|||
374 |
/* Iterate all matching manuals. */ |
||
375 |
|||
376 |
225 |
resp = res; |
|
377 |
✓✓ | 1010 |
for (i = 0; i < sz; i++) { |
378 |
✓✓ | 280 |
if (outmode == OUTMODE_FLN) |
379 |
210 |
puts(res[i].file); |
|
380 |
✓✓ | 70 |
else if (outmode == OUTMODE_LST) |
381 |
130 |
printf("%s - %s\n", res[i].names, |
|
382 |
✓✗ | 195 |
res[i].output == NULL ? "" : |
383 |
res[i].output); |
||
384 |
✓✗ | 5 |
else if (outmode == OUTMODE_ONE) { |
385 |
/* Search for the best section. */ |
||
386 |
5 |
sec = res[i].file; |
|
387 |
5 |
sec += strcspn(sec, "123456789"); |
|
388 |
✓✗ | 5 |
if (sec[0] == '\0') |
389 |
continue; |
||
390 |
5 |
prio = sec_prios[sec[0] - '1']; |
|
391 |
✗✓ | 5 |
if (sec[1] != '/') |
392 |
prio += 10; |
||
393 |
✓✗ | 5 |
if (prio >= best_prio) |
394 |
continue; |
||
395 |
best_prio = prio; |
||
396 |
5 |
resp = res + i; |
|
397 |
5 |
} |
|
398 |
} |
||
399 |
|||
400 |
/* |
||
401 |
* For man(1), -a and -i output mode, fall through |
||
402 |
* to the main mandoc(1) code iterating files |
||
403 |
* and running the parsers on each of them. |
||
404 |
*/ |
||
405 |
|||
406 |
✓✓ | 225 |
if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) |
407 |
goto out; |
||
408 |
} |
||
409 |
|||
410 |
/* mandoc(1) */ |
||
411 |
|||
412 |
✓✓ | 12551 |
if (use_pager) { |
413 |
✗✓ | 5 |
if (pledge("stdio rpath tmppath tty proc exec flock cpath wpath", NULL) == -1) |
414 |
err((int)MANDOCLEVEL_SYSERR, "pledge"); |
||
415 |
} else { |
||
416 |
✗✓ | 12546 |
if (pledge("stdio rpath flock cpath wpath", NULL) == -1) |
417 |
err((int)MANDOCLEVEL_SYSERR, "pledge"); |
||
418 |
} |
||
419 |
|||
420 |
✓✓ | 12551 |
if (search.argmode == ARG_FILE) |
421 |
12546 |
moptions(&options, auxpaths); |
|
422 |
|||
423 |
12551 |
mchars_alloc(); |
|
424 |
25102 |
curp.mp = mparse_alloc(options, curp.mmin, mmsg, |
|
425 |
12551 |
curp.os_e, curp.os_s); |
|
426 |
|||
427 |
/* |
||
428 |
* Conditionally start up the lookaside buffer before parsing. |
||
429 |
*/ |
||
430 |
✓✓ | 12551 |
if (OUTT_MAN == curp.outtype) |
431 |
1899 |
mparse_keep(curp.mp); |
|
432 |
|||
433 |
✗✓ | 12551 |
if (argc < 1) { |
434 |
if (use_pager) |
||
435 |
tag_files = tag_init(); |
||
436 |
parse(&curp, STDIN_FILENO, "<stdin>"); |
||
437 |
} |
||
438 |
|||
439 |
✓✓ | 25102 |
while (argc > 0) { |
440 |
12551 |
fd = mparse_open(curp.mp, resp != NULL ? resp->file : *argv); |
|
441 |
✓✗ | 12551 |
if (fd != -1) { |
442 |
✓✓ | 12551 |
if (use_pager) { |
443 |
5 |
tag_files = tag_init(); |
|
444 |
use_pager = 0; |
||
445 |
5 |
} |
|
446 |
|||
447 |
✓✓ | 12551 |
if (resp == NULL) |
448 |
12546 |
parse(&curp, fd, *argv); |
|
449 |
✓✗ | 5 |
else if (resp->form == FORM_SRC) { |
450 |
/* For .so only; ignore failure. */ |
||
451 |
5 |
(void)chdir(conf.manpath.paths[resp->ipath]); |
|
452 |
5 |
parse(&curp, fd, resp->file); |
|
453 |
5 |
} else |
|
454 |
passthrough(resp->file, fd, |
||
455 |
conf.output.synopsisonly); |
||
456 |
|||
457 |
✓✗✗✓ ✗✗ |
25102 |
if (ferror(stdout)) { |
458 |
if (tag_files != NULL) { |
||
459 |
warn("%s", tag_files->ofn); |
||
460 |
tag_unlink(); |
||
461 |
tag_files = NULL; |
||
462 |
} else |
||
463 |
warn("stdout"); |
||
464 |
rc = MANDOCLEVEL_SYSERR; |
||
465 |
break; |
||
466 |
} |
||
467 |
|||
468 |
✗✓✗✗ |
12551 |
if (argc > 1 && curp.outtype <= OUTT_UTF8) { |
469 |
if (curp.outdata == NULL) |
||
470 |
outdata_alloc(&curp); |
||
471 |
terminal_sepline(curp.outdata); |
||
472 |
} |
||
473 |
} else if (rc < MANDOCLEVEL_ERROR) |
||
474 |
rc = MANDOCLEVEL_ERROR; |
||
475 |
|||
476 |
✓✓✓✗ |
14603 |
if (MANDOCLEVEL_OK != rc && curp.wstop) |
477 |
break; |
||
478 |
|||
479 |
✓✓ | 12551 |
if (resp != NULL) |
480 |
5 |
resp++; |
|
481 |
else |
||
482 |
12546 |
argv++; |
|
483 |
✓✗ | 12551 |
if (--argc) |
484 |
mparse_reset(curp.mp); |
||
485 |
} |
||
486 |
|||
487 |
✓✓ | 12551 |
if (curp.outdata != NULL) { |
488 |
✓✗✗✓ ✗✗✓ |
13258 |
switch (curp.outtype) { |
489 |
case OUTT_HTML: |
||
490 |
216 |
html_free(curp.outdata); |
|
491 |
216 |
break; |
|
492 |
case OUTT_UTF8: |
||
493 |
case OUTT_LOCALE: |
||
494 |
case OUTT_ASCII: |
||
495 |
6413 |
ascii_free(curp.outdata); |
|
496 |
6413 |
break; |
|
497 |
case OUTT_PDF: |
||
498 |
case OUTT_PS: |
||
499 |
pspdf_free(curp.outdata); |
||
500 |
break; |
||
501 |
default: |
||
502 |
break; |
||
503 |
} |
||
504 |
} |
||
505 |
12551 |
mandoc_xr_free(); |
|
506 |
12551 |
mparse_free(curp.mp); |
|
507 |
12551 |
mchars_free(); |
|
508 |
|||
509 |
out: |
||
510 |
✓✓ | 12783 |
if (search.argmode != ARG_FILE) { |
511 |
237 |
manconf_free(&conf); |
|
512 |
237 |
mansearch_free(res, sz); |
|
513 |
237 |
} |
|
514 |
|||
515 |
12783 |
free(curp.os_s); |
|
516 |
|||
517 |
/* |
||
518 |
* When using a pager, finish writing both temporary files, |
||
519 |
* fork it, wait for the user to close it, and clean up. |
||
520 |
*/ |
||
521 |
|||
522 |
✓✓ | 12783 |
if (tag_files != NULL) { |
523 |
5 |
fclose(stdout); |
|
524 |
5 |
tag_write(); |
|
525 |
5 |
man_pgid = getpgid(0); |
|
526 |
✓✗ | 15 |
tag_files->tcpgid = man_pgid == getpid() ? |
527 |
5 |
getpgid(getppid()) : man_pgid; |
|
528 |
pager_pid = 0; |
||
529 |
signum = SIGSTOP; |
||
530 |
5 |
for (;;) { |
|
531 |
|||
532 |
/* Stop here until moved to the foreground. */ |
||
533 |
|||
534 |
5 |
tc_pgid = tcgetpgrp(tag_files->ofd); |
|
535 |
✗✓ | 5 |
if (tc_pgid != man_pgid) { |
536 |
if (tc_pgid == pager_pid) { |
||
537 |
(void)tcsetpgrp(tag_files->ofd, |
||
538 |
man_pgid); |
||
539 |
if (signum == SIGTTIN) |
||
540 |
continue; |
||
541 |
} else |
||
542 |
tag_files->tcpgid = tc_pgid; |
||
543 |
kill(0, signum); |
||
544 |
continue; |
||
545 |
} |
||
546 |
|||
547 |
/* Once in the foreground, activate the pager. */ |
||
548 |
|||
549 |
✗✓ | 5 |
if (pager_pid) { |
550 |
(void)tcsetpgrp(tag_files->ofd, pager_pid); |
||
551 |
kill(pager_pid, SIGCONT); |
||
552 |
} else |
||
553 |
5 |
pager_pid = spawn_pager(tag_files); |
|
554 |
|||
555 |
/* Wait for the pager to stop or exit. */ |
||
556 |
|||
557 |
✗✗ | 5 |
while ((pid = waitpid(pager_pid, &status, |
558 |
✗✓ | 5 |
WUNTRACED)) == -1 && errno == EINTR) |
559 |
continue; |
||
560 |
|||
561 |
✗✓ | 5 |
if (pid == -1) { |
562 |
warn("wait"); |
||
563 |
rc = MANDOCLEVEL_SYSERR; |
||
564 |
break; |
||
565 |
} |
||
566 |
✗✓ | 5 |
if (!WIFSTOPPED(status)) |
567 |
break; |
||
568 |
|||
569 |
signum = WSTOPSIG(status); |
||
570 |
} |
||
571 |
5 |
tag_unlink(); |
|
572 |
5 |
} |
|
573 |
|||
574 |
12783 |
return (int)rc; |
|
575 |
12879 |
} |
|
576 |
|||
577 |
static void |
||
578 |
usage(enum argmode argmode) |
||
579 |
{ |
||
580 |
|||
581 |
switch (argmode) { |
||
582 |
case ARG_FILE: |
||
583 |
fputs("usage: mandoc [-ac] [-I os=name] " |
||
584 |
"[-K encoding] [-mdoc | -man] [-O options]\n" |
||
585 |
"\t [-T output] [-W level] [file ...]\n", stderr); |
||
586 |
break; |
||
587 |
case ARG_NAME: |
||
588 |
fputs("usage: man [-acfhklw] [-C file] [-M path] " |
||
589 |
"[-m path] [-S subsection]\n" |
||
590 |
"\t [[-s] section] name ...\n", stderr); |
||
591 |
break; |
||
592 |
case ARG_WORD: |
||
593 |
fputs("usage: whatis [-afk] [-C file] " |
||
594 |
"[-M path] [-m path] [-O outkey] [-S arch]\n" |
||
595 |
"\t [-s section] name ...\n", stderr); |
||
596 |
break; |
||
597 |
case ARG_EXPR: |
||
598 |
fputs("usage: apropos [-afk] [-C file] " |
||
599 |
"[-M path] [-m path] [-O outkey] [-S arch]\n" |
||
600 |
"\t [-s section] expression ...\n", stderr); |
||
601 |
break; |
||
602 |
} |
||
603 |
exit((int)MANDOCLEVEL_BADARG); |
||
604 |
} |
||
605 |
|||
606 |
static int |
||
607 |
fs_lookup(const struct manpaths *paths, size_t ipath, |
||
608 |
const char *sec, const char *arch, const char *name, |
||
609 |
struct manpage **res, size_t *ressz) |
||
610 |
{ |
||
611 |
366 |
glob_t globinfo; |
|
612 |
struct manpage *page; |
||
613 |
183 |
char *file; |
|
614 |
int globres; |
||
615 |
enum form form; |
||
616 |
|||
617 |
form = FORM_SRC; |
||
618 |
183 |
mandoc_asprintf(&file, "%s/man%s/%s.%s", |
|
619 |
183 |
paths->paths[ipath], sec, name, sec); |
|
620 |
✓✓ | 183 |
if (access(file, R_OK) != -1) |
621 |
goto found; |
||
622 |
168 |
free(file); |
|
623 |
|||
624 |
168 |
mandoc_asprintf(&file, "%s/cat%s/%s.0", |
|
625 |
168 |
paths->paths[ipath], sec, name); |
|
626 |
✗✓ | 168 |
if (access(file, R_OK) != -1) { |
627 |
form = FORM_CAT; |
||
628 |
goto found; |
||
629 |
} |
||
630 |
168 |
free(file); |
|
631 |
|||
632 |
✓✓ | 168 |
if (arch != NULL) { |
633 |
150 |
mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", |
|
634 |
150 |
paths->paths[ipath], sec, arch, name, sec); |
|
635 |
✓✗ | 150 |
if (access(file, R_OK) != -1) |
636 |
goto found; |
||
637 |
150 |
free(file); |
|
638 |
150 |
} |
|
639 |
|||
640 |
168 |
mandoc_asprintf(&file, "%s/man%s/%s.[01-9]*", |
|
641 |
168 |
paths->paths[ipath], sec, name); |
|
642 |
168 |
globres = glob(file, 0, NULL, &globinfo); |
|
643 |
✗✓ | 168 |
if (globres != 0 && globres != GLOB_NOMATCH) |
644 |
warn("%s: glob", file); |
||
645 |
168 |
free(file); |
|
646 |
✗✓ | 168 |
if (globres == 0) |
647 |
file = mandoc_strdup(*globinfo.gl_pathv); |
||
648 |
168 |
globfree(&globinfo); |
|
649 |
✓✗ | 168 |
if (globres == 0) |
650 |
goto found; |
||
651 |
✓✓✓✓ |
186 |
if (res != NULL || ipath + 1 != paths->sz) |
652 |
159 |
return 0; |
|
653 |
|||
654 |
9 |
mandoc_asprintf(&file, "%s.%s", name, sec); |
|
655 |
9 |
globres = access(file, R_OK); |
|
656 |
9 |
free(file); |
|
657 |
9 |
return globres != -1; |
|
658 |
|||
659 |
found: |
||
660 |
15 |
warnx("outdated mandoc.db lacks %s(%s) entry, run makewhatis %s", |
|
661 |
15 |
name, sec, paths->paths[ipath]); |
|
662 |
✗✓ | 15 |
if (res == NULL) { |
663 |
free(file); |
||
664 |
return 1; |
||
665 |
} |
||
666 |
15 |
*res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); |
|
667 |
15 |
page = *res + (*ressz - 1); |
|
668 |
15 |
page->file = file; |
|
669 |
15 |
page->names = NULL; |
|
670 |
15 |
page->output = NULL; |
|
671 |
15 |
page->ipath = ipath; |
|
672 |
15 |
page->bits = NAME_FILE & NAME_MASK; |
|
673 |
✓✗✓✗ |
60 |
page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; |
674 |
15 |
page->form = form; |
|
675 |
15 |
return 1; |
|
676 |
183 |
} |
|
677 |
|||
678 |
static int |
||
679 |
fs_search(const struct mansearch *cfg, const struct manpaths *paths, |
||
680 |
int argc, char **argv, struct manpage **res, size_t *ressz) |
||
681 |
{ |
||
682 |
const char *const sections[] = |
||
683 |
{"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; |
||
684 |
const size_t nsec = sizeof(sections)/sizeof(sections[0]); |
||
685 |
|||
686 |
size_t ipath, isec, lastsz; |
||
687 |
|||
688 |
✗✓ | 52 |
assert(cfg->argmode == ARG_NAME); |
689 |
|||
690 |
✓✓ | 26 |
if (res != NULL) |
691 |
17 |
*res = NULL; |
|
692 |
26 |
*ressz = lastsz = 0; |
|
693 |
✓✓ | 104 |
while (argc) { |
694 |
✓✓ | 130 |
for (ipath = 0; ipath < paths->sz; ipath++) { |
695 |
✓✓ | 39 |
if (cfg->sec != NULL) { |
696 |
✗✓ | 28 |
if (fs_lookup(paths, ipath, cfg->sec, |
697 |
✓✓ | 46 |
cfg->arch, *argv, res, ressz) && |
698 |
5 |
cfg->firstmatch) |
|
699 |
return 1; |
||
700 |
✓✓ | 352 |
} else for (isec = 0; isec < nsec; isec++) |
701 |
✗✓ | 330 |
if (fs_lookup(paths, ipath, sections[isec], |
702 |
✓✓ | 320 |
cfg->arch, *argv, res, ressz) && |
703 |
10 |
cfg->firstmatch) |
|
704 |
return 1; |
||
705 |
} |
||
706 |
✓✓✓✓ |
43 |
if (res != NULL && *ressz == lastsz) |
707 |
2 |
warnx("No entry for %s in the manual.", *argv); |
|
708 |
26 |
lastsz = *ressz; |
|
709 |
26 |
argv++; |
|
710 |
26 |
argc--; |
|
711 |
} |
||
712 |
26 |
return 0; |
|
713 |
26 |
} |
|
714 |
|||
715 |
static void |
||
716 |
parse(struct curparse *curp, int fd, const char *file) |
||
717 |
{ |
||
718 |
enum mandoclevel rctmp; |
||
719 |
25102 |
struct roff_man *man; |
|
720 |
|||
721 |
/* Begin by parsing the file itself. */ |
||
722 |
|||
723 |
✗✓ | 12551 |
assert(file); |
724 |
✗✓ | 12551 |
assert(fd >= 0); |
725 |
|||
726 |
12551 |
rctmp = mparse_readfd(curp->mp, fd, file); |
|
727 |
✓✗ | 12551 |
if (fd != STDIN_FILENO) |
728 |
12551 |
close(fd); |
|
729 |
✓✓ | 12551 |
if (rc < rctmp) |
730 |
1134 |
rc = rctmp; |
|
731 |
|||
732 |
/* |
||
733 |
* With -Wstop and warnings or errors of at least the requested |
||
734 |
* level, do not produce output. |
||
735 |
*/ |
||
736 |
|||
737 |
✓✓✗✓ |
13685 |
if (rctmp != MANDOCLEVEL_OK && curp->wstop) |
738 |
return; |
||
739 |
|||
740 |
✓✗ | 12551 |
if (curp->outdata == NULL) |
741 |
12551 |
outdata_alloc(curp); |
|
742 |
|||
743 |
12551 |
mparse_result(curp->mp, &man, NULL); |
|
744 |
|||
745 |
/* Execute the out device, if it exists. */ |
||
746 |
|||
747 |
✗✓ | 12551 |
if (man == NULL) |
748 |
return; |
||
749 |
12551 |
mandoc_xr_reset(); |
|
750 |
✓✓ | 12551 |
if (man->macroset == MACROSET_MDOC) { |
751 |
✗✓✗✗ |
8456 |
if (curp->outtype != OUTT_TREE || !curp->outopts->noval) |
752 |
8456 |
mdoc_validate(man); |
|
753 |
✓✗✓✗ ✗✗✗✓ ✓✓ |
15445 |
switch (curp->outtype) { |
754 |
case OUTT_HTML: |
||
755 |
153 |
html_mdoc(curp->outdata, man); |
|
756 |
153 |
break; |
|
757 |
case OUTT_TREE: |
||
758 |
tree_mdoc(curp->outdata, man); |
||
759 |
break; |
||
760 |
case OUTT_MAN: |
||
761 |
1899 |
man_mdoc(curp->outdata, man); |
|
762 |
1899 |
break; |
|
763 |
case OUTT_PDF: |
||
764 |
case OUTT_ASCII: |
||
765 |
case OUTT_UTF8: |
||
766 |
case OUTT_LOCALE: |
||
767 |
case OUTT_PS: |
||
768 |
2975 |
terminal_mdoc(curp->outdata, man); |
|
769 |
2975 |
break; |
|
770 |
case OUTT_MARKDOWN: |
||
771 |
1962 |
markdown_mdoc(curp->outdata, man); |
|
772 |
1962 |
break; |
|
773 |
default: |
||
774 |
break; |
||
775 |
} |
||
776 |
} |
||
777 |
✓✓ | 12551 |
if (man->macroset == MACROSET_MAN) { |
778 |
✗✓✗✗ |
4095 |
if (curp->outtype != OUTT_TREE || !curp->outopts->noval) |
779 |
4095 |
man_validate(man); |
|
780 |
✓✗✗✗ ✗✗✗✓ ✓ |
7596 |
switch (curp->outtype) { |
781 |
case OUTT_HTML: |
||
782 |
63 |
html_man(curp->outdata, man); |
|
783 |
63 |
break; |
|
784 |
case OUTT_TREE: |
||
785 |
tree_man(curp->outdata, man); |
||
786 |
break; |
||
787 |
case OUTT_MAN: |
||
788 |
man_man(curp->outdata, man); |
||
789 |
break; |
||
790 |
case OUTT_PDF: |
||
791 |
case OUTT_ASCII: |
||
792 |
case OUTT_UTF8: |
||
793 |
case OUTT_LOCALE: |
||
794 |
case OUTT_PS: |
||
795 |
3438 |
terminal_man(curp->outdata, man); |
|
796 |
3438 |
break; |
|
797 |
default: |
||
798 |
break; |
||
799 |
} |
||
800 |
} |
||
801 |
✓✓ | 12551 |
if (curp->mmin < MANDOCERR_STYLE) |
802 |
2061 |
check_xr(file); |
|
803 |
12551 |
mparse_updaterc(curp->mp, &rc); |
|
804 |
25102 |
} |
|
805 |
|||
806 |
static void |
||
807 |
check_xr(const char *file) |
||
808 |
{ |
||
809 |
static struct manpaths paths; |
||
810 |
4122 |
struct mansearch search; |
|
811 |
struct mandoc_xr *xr; |
||
812 |
2061 |
char *cp; |
|
813 |
2061 |
size_t sz; |
|
814 |
|||
815 |
✓✗ | 2061 |
if (paths.sz == 0) |
816 |
2061 |
manpath_base(&paths); |
|
817 |
|||
818 |
✓✓ | 6930 |
for (xr = mandoc_xr_get(); xr != NULL; xr = xr->next) { |
819 |
✓✓ | 1404 |
if (xr->line == -1) |
820 |
continue; |
||
821 |
18 |
search.arch = NULL; |
|
822 |
18 |
search.sec = xr->sec; |
|
823 |
18 |
search.outkey = NULL; |
|
824 |
18 |
search.argmode = ARG_NAME; |
|
825 |
18 |
search.firstmatch = 1; |
|
826 |
✓✓ | 18 |
if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz)) |
827 |
continue; |
||
828 |
✓✗ | 9 |
if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz)) |
829 |
continue; |
||
830 |
✓✗ | 9 |
if (xr->count == 1) |
831 |
9 |
mandoc_asprintf(&cp, "Xr %s %s", xr->name, xr->sec); |
|
832 |
else |
||
833 |
mandoc_asprintf(&cp, "Xr %s %s (%d times)", |
||
834 |
xr->name, xr->sec, xr->count); |
||
835 |
9 |
mmsg(MANDOCERR_XR_BAD, MANDOCLEVEL_STYLE, |
|
836 |
9 |
file, xr->line, xr->pos + 1, cp); |
|
837 |
9 |
free(cp); |
|
838 |
9 |
} |
|
839 |
2061 |
} |
|
840 |
|||
841 |
static void |
||
842 |
outdata_alloc(struct curparse *curp) |
||
843 |
{ |
||
844 |
✓✓✓✓ ✗✗✓ |
31731 |
switch (curp->outtype) { |
845 |
case OUTT_HTML: |
||
846 |
216 |
curp->outdata = html_alloc(curp->outopts); |
|
847 |
216 |
break; |
|
848 |
case OUTT_UTF8: |
||
849 |
198 |
curp->outdata = utf8_alloc(curp->outopts); |
|
850 |
198 |
break; |
|
851 |
case OUTT_LOCALE: |
||
852 |
5 |
curp->outdata = locale_alloc(curp->outopts); |
|
853 |
5 |
break; |
|
854 |
case OUTT_ASCII: |
||
855 |
6210 |
curp->outdata = ascii_alloc(curp->outopts); |
|
856 |
6210 |
break; |
|
857 |
case OUTT_PDF: |
||
858 |
curp->outdata = pdf_alloc(curp->outopts); |
||
859 |
break; |
||
860 |
case OUTT_PS: |
||
861 |
curp->outdata = ps_alloc(curp->outopts); |
||
862 |
break; |
||
863 |
default: |
||
864 |
break; |
||
865 |
} |
||
866 |
19180 |
} |
|
867 |
|||
868 |
static void |
||
869 |
passthrough(const char *file, int fd, int synopsis_only) |
||
870 |
{ |
||
871 |
const char synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"; |
||
872 |
const char synr[] = "SYNOPSIS"; |
||
873 |
|||
874 |
FILE *stream; |
||
875 |
const char *syscall; |
||
876 |
char *line, *cp; |
||
877 |
size_t linesz; |
||
878 |
ssize_t len, written; |
||
879 |
int print; |
||
880 |
|||
881 |
line = NULL; |
||
882 |
linesz = 0; |
||
883 |
|||
884 |
if (fflush(stdout) == EOF) { |
||
885 |
syscall = "fflush"; |
||
886 |
goto fail; |
||
887 |
} |
||
888 |
|||
889 |
if ((stream = fdopen(fd, "r")) == NULL) { |
||
890 |
close(fd); |
||
891 |
syscall = "fdopen"; |
||
892 |
goto fail; |
||
893 |
} |
||
894 |
|||
895 |
print = 0; |
||
896 |
while ((len = getline(&line, &linesz, stream)) != -1) { |
||
897 |
cp = line; |
||
898 |
if (synopsis_only) { |
||
899 |
if (print) { |
||
900 |
if ( ! isspace((unsigned char)*cp)) |
||
901 |
goto done; |
||
902 |
while (isspace((unsigned char)*cp)) { |
||
903 |
cp++; |
||
904 |
len--; |
||
905 |
} |
||
906 |
} else { |
||
907 |
if (strcmp(cp, synb) == 0 || |
||
908 |
strcmp(cp, synr) == 0) |
||
909 |
print = 1; |
||
910 |
continue; |
||
911 |
} |
||
912 |
} |
||
913 |
for (; len > 0; len -= written) { |
||
914 |
if ((written = write(STDOUT_FILENO, cp, len)) != -1) |
||
915 |
continue; |
||
916 |
fclose(stream); |
||
917 |
syscall = "write"; |
||
918 |
goto fail; |
||
919 |
} |
||
920 |
} |
||
921 |
|||
922 |
if (ferror(stream)) { |
||
923 |
fclose(stream); |
||
924 |
syscall = "getline"; |
||
925 |
goto fail; |
||
926 |
} |
||
927 |
|||
928 |
done: |
||
929 |
free(line); |
||
930 |
fclose(stream); |
||
931 |
return; |
||
932 |
|||
933 |
fail: |
||
934 |
free(line); |
||
935 |
warn("%s: SYSERR: %s", file, syscall); |
||
936 |
if (rc < MANDOCLEVEL_SYSERR) |
||
937 |
rc = MANDOCLEVEL_SYSERR; |
||
938 |
} |
||
939 |
|||
940 |
static int |
||
941 |
koptions(int *options, char *arg) |
||
942 |
{ |
||
943 |
|||
944 |
if ( ! strcmp(arg, "utf-8")) { |
||
945 |
*options |= MPARSE_UTF8; |
||
946 |
*options &= ~MPARSE_LATIN1; |
||
947 |
} else if ( ! strcmp(arg, "iso-8859-1")) { |
||
948 |
*options |= MPARSE_LATIN1; |
||
949 |
*options &= ~MPARSE_UTF8; |
||
950 |
} else if ( ! strcmp(arg, "us-ascii")) { |
||
951 |
*options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); |
||
952 |
} else { |
||
953 |
warnx("-K %s: Bad argument", arg); |
||
954 |
return 0; |
||
955 |
} |
||
956 |
return 1; |
||
957 |
} |
||
958 |
|||
959 |
static void |
||
960 |
moptions(int *options, char *arg) |
||
961 |
{ |
||
962 |
|||
963 |
✓✓ | 25092 |
if (arg == NULL) |
964 |
return; |
||
965 |
✓✓ | 2142 |
if (strcmp(arg, "doc") == 0) |
966 |
234 |
*options |= MPARSE_MDOC; |
|
967 |
✓✗ | 1908 |
else if (strcmp(arg, "an") == 0) |
968 |
1908 |
*options |= MPARSE_MAN; |
|
969 |
12546 |
} |
|
970 |
|||
971 |
static int |
||
972 |
toptions(struct curparse *curp, char *arg) |
||
973 |
{ |
||
974 |
|||
975 |
✓✓ | 25092 |
if (0 == strcmp(arg, "ascii")) |
976 |
6210 |
curp->outtype = OUTT_ASCII; |
|
977 |
✓✓ | 6336 |
else if (0 == strcmp(arg, "lint")) { |
978 |
2061 |
curp->outtype = OUTT_LINT; |
|
979 |
2061 |
curp->mmin = MANDOCERR_BASE; |
|
980 |
2061 |
mmsg_stream = stdout; |
|
981 |
✗✓ | 6336 |
} else if (0 == strcmp(arg, "tree")) |
982 |
curp->outtype = OUTT_TREE; |
||
983 |
✓✓ | 4275 |
else if (0 == strcmp(arg, "man")) |
984 |
1899 |
curp->outtype = OUTT_MAN; |
|
985 |
✓✓ | 2376 |
else if (0 == strcmp(arg, "html")) |
986 |
216 |
curp->outtype = OUTT_HTML; |
|
987 |
✓✓ | 2160 |
else if (0 == strcmp(arg, "markdown")) |
988 |
1962 |
curp->outtype = OUTT_MARKDOWN; |
|
989 |
✓✗ | 198 |
else if (0 == strcmp(arg, "utf8")) |
990 |
198 |
curp->outtype = OUTT_UTF8; |
|
991 |
else if (0 == strcmp(arg, "locale")) |
||
992 |
curp->outtype = OUTT_LOCALE; |
||
993 |
else if (0 == strcmp(arg, "ps")) |
||
994 |
curp->outtype = OUTT_PS; |
||
995 |
else if (0 == strcmp(arg, "pdf")) |
||
996 |
curp->outtype = OUTT_PDF; |
||
997 |
else { |
||
998 |
warnx("-T %s: Bad argument", arg); |
||
999 |
return 0; |
||
1000 |
} |
||
1001 |
|||
1002 |
12546 |
return 1; |
|
1003 |
12546 |
} |
|
1004 |
|||
1005 |
static int |
||
1006 |
woptions(struct curparse *curp, char *arg) |
||
1007 |
{ |
||
1008 |
2061 |
char *v, *o; |
|
1009 |
2061 |
const char *toks[11]; |
|
1010 |
|||
1011 |
2061 |
toks[0] = "stop"; |
|
1012 |
2061 |
toks[1] = "all"; |
|
1013 |
2061 |
toks[2] = "base"; |
|
1014 |
2061 |
toks[3] = "style"; |
|
1015 |
2061 |
toks[4] = "warning"; |
|
1016 |
2061 |
toks[5] = "error"; |
|
1017 |
2061 |
toks[6] = "unsupp"; |
|
1018 |
2061 |
toks[7] = "fatal"; |
|
1019 |
2061 |
toks[8] = "openbsd"; |
|
1020 |
2061 |
toks[9] = "netbsd"; |
|
1021 |
2061 |
toks[10] = NULL; |
|
1022 |
|||
1023 |
✓✓ | 6183 |
while (*arg) { |
1024 |
o = arg; |
||
1025 |
✗✗✓✗ ✗✗✗✗ ✗✗✗ |
2061 |
switch (getsubopt(&arg, (char * const *)toks, &v)) { |
1026 |
case 0: |
||
1027 |
curp->wstop = 1; |
||
1028 |
break; |
||
1029 |
case 1: |
||
1030 |
case 2: |
||
1031 |
2061 |
curp->mmin = MANDOCERR_BASE; |
|
1032 |
2061 |
break; |
|
1033 |
case 3: |
||
1034 |
curp->mmin = MANDOCERR_STYLE; |
||
1035 |
break; |
||
1036 |
case 4: |
||
1037 |
curp->mmin = MANDOCERR_WARNING; |
||
1038 |
break; |
||
1039 |
case 5: |
||
1040 |
curp->mmin = MANDOCERR_ERROR; |
||
1041 |
break; |
||
1042 |
case 6: |
||
1043 |
curp->mmin = MANDOCERR_UNSUPP; |
||
1044 |
break; |
||
1045 |
case 7: |
||
1046 |
curp->mmin = MANDOCERR_MAX; |
||
1047 |
break; |
||
1048 |
case 8: |
||
1049 |
curp->mmin = MANDOCERR_BASE; |
||
1050 |
curp->os_e = MANDOC_OS_OPENBSD; |
||
1051 |
break; |
||
1052 |
case 9: |
||
1053 |
curp->mmin = MANDOCERR_BASE; |
||
1054 |
curp->os_e = MANDOC_OS_NETBSD; |
||
1055 |
break; |
||
1056 |
default: |
||
1057 |
warnx("-W %s: Bad argument", o); |
||
1058 |
return 0; |
||
1059 |
} |
||
1060 |
} |
||
1061 |
2061 |
return 1; |
|
1062 |
2061 |
} |
|
1063 |
|||
1064 |
static void |
||
1065 |
mmsg(enum mandocerr t, enum mandoclevel lvl, |
||
1066 |
const char *file, int line, int col, const char *msg) |
||
1067 |
{ |
||
1068 |
const char *mparse_msg; |
||
1069 |
|||
1070 |
26757 |
fprintf(mmsg_stream, "%s: %s:", getprogname(), |
|
1071 |
8919 |
file == NULL ? "<stdin>" : file); |
|
1072 |
|||
1073 |
✓✓ | 8919 |
if (line) |
1074 |
8847 |
fprintf(mmsg_stream, "%d:%d:", line, col + 1); |
|
1075 |
|||
1076 |
8919 |
fprintf(mmsg_stream, " %s", mparse_strlevel(lvl)); |
|
1077 |
|||
1078 |
✓✗ | 8919 |
if ((mparse_msg = mparse_strerror(t)) != NULL) |
1079 |
8919 |
fprintf(mmsg_stream, ": %s", mparse_msg); |
|
1080 |
|||
1081 |
✓✓ | 8919 |
if (msg) |
1082 |
7344 |
fprintf(mmsg_stream, ": %s", msg); |
|
1083 |
|||
1084 |
8919 |
fputc('\n', mmsg_stream); |
|
1085 |
8919 |
} |
|
1086 |
|||
1087 |
static pid_t |
||
1088 |
spawn_pager(struct tag_files *tag_files) |
||
1089 |
{ |
||
1090 |
const struct timespec timeout = { 0, 100000000 }; /* 0.1s */ |
||
1091 |
#define MAX_PAGER_ARGS 16 |
||
1092 |
10 |
char *argv[MAX_PAGER_ARGS]; |
|
1093 |
const char *pager; |
||
1094 |
char *cp; |
||
1095 |
size_t cmdlen; |
||
1096 |
int argc; |
||
1097 |
pid_t pager_pid; |
||
1098 |
|||
1099 |
5 |
pager = getenv("MANPAGER"); |
|
1100 |
✗✓✗✗ |
5 |
if (pager == NULL || *pager == '\0') |
1101 |
5 |
pager = getenv("PAGER"); |
|
1102 |
✗✓✗✗ |
5 |
if (pager == NULL || *pager == '\0') |
1103 |
5 |
pager = "more -s"; |
|
1104 |
5 |
cp = mandoc_strdup(pager); |
|
1105 |
|||
1106 |
/* |
||
1107 |
* Parse the pager command into words. |
||
1108 |
* Intentionally do not do anything fancy here. |
||
1109 |
*/ |
||
1110 |
|||
1111 |
argc = 0; |
||
1112 |
✓✗ | 15 |
while (argc + 4 < MAX_PAGER_ARGS) { |
1113 |
10 |
argv[argc++] = cp; |
|
1114 |
10 |
cp = strchr(cp, ' '); |
|
1115 |
✓✓ | 10 |
if (cp == NULL) |
1116 |
break; |
||
1117 |
5 |
*cp++ = '\0'; |
|
1118 |
✗✓ | 10 |
while (*cp == ' ') |
1119 |
cp++; |
||
1120 |
✓✗ | 5 |
if (*cp == '\0') |
1121 |
break; |
||
1122 |
} |
||
1123 |
|||
1124 |
/* For more(1) and less(1), use the tag file. */ |
||
1125 |
|||
1126 |
✓✗ | 5 |
if ((cmdlen = strlen(argv[0])) >= 4) { |
1127 |
5 |
cp = argv[0] + cmdlen - 4; |
|
1128 |
✓✗✓✗ |
10 |
if (strcmp(cp, "less") == 0 || strcmp(cp, "more") == 0) { |
1129 |
5 |
argv[argc++] = mandoc_strdup("-T"); |
|
1130 |
5 |
argv[argc++] = tag_files->tfn; |
|
1131 |
5 |
} |
|
1132 |
} |
||
1133 |
5 |
argv[argc++] = tag_files->ofn; |
|
1134 |
5 |
argv[argc] = NULL; |
|
1135 |
|||
1136 |
✗✓✗ | 5 |
switch (pager_pid = fork()) { |
1137 |
case -1: |
||
1138 |
err((int)MANDOCLEVEL_SYSERR, "fork"); |
||
1139 |
case 0: |
||
1140 |
break; |
||
1141 |
default: |
||
1142 |
5 |
(void)setpgid(pager_pid, 0); |
|
1143 |
5 |
(void)tcsetpgrp(tag_files->ofd, pager_pid); |
|
1144 |
✗✓ | 5 |
if (pledge("stdio rpath tmppath tty proc flock cpath wpath", NULL) == -1) |
1145 |
err((int)MANDOCLEVEL_SYSERR, "pledge"); |
||
1146 |
5 |
tag_files->pager_pid = pager_pid; |
|
1147 |
5 |
return pager_pid; |
|
1148 |
} |
||
1149 |
|||
1150 |
/* The child process becomes the pager. */ |
||
1151 |
|||
1152 |
if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) |
||
1153 |
err((int)MANDOCLEVEL_SYSERR, "pager stdout"); |
||
1154 |
close(tag_files->ofd); |
||
1155 |
close(tag_files->tfd); |
||
1156 |
|||
1157 |
/* Do not start the pager before controlling the terminal. */ |
||
1158 |
|||
1159 |
while (tcgetpgrp(STDOUT_FILENO) != getpid()) |
||
1160 |
nanosleep(&timeout, NULL); |
||
1161 |
|||
1162 |
execvp(argv[0], argv); |
||
1163 |
err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]); |
||
1164 |
5 |
} |
Generated by: GCOVR (Version 3.3) |