1 /* $OpenBSD: man.c,v 1.28 2004/02/23 14:14:14 jmc Exp $ */
2 /* $NetBSD: man.c,v 1.7 1995/09/28 06:05:34 tls Exp $ */
3
4 /*
5 * Copyright (c) 1987, 1993, 1994, 1995
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33 #ifndef lint
34 static char copyright[] =
35 "@(#) Copyright (c) 1987, 1993, 1994, 1995\n\
36 The Regents of the University of California. All rights reserved.\n";
37 #endif /* not lint */
38
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)man.c 8.17 (Berkeley) 1/31/95";
42 #else
43 static char rcsid[] = "$OpenBSD: man.c,v 1.28 2004/02/23 14:14:14 jmc Exp $";
44 #endif
45 #endif /* not lint */
46
47 #include <sys/param.h>
48 #include <sys/queue.h>
49
50 #include <ctype.h>
51 #include <err.h>
52 #include <errno.h>
53 #include <fcntl.h>
54 #include <fnmatch.h>
55 #include <glob.h>
56 #include <signal.h>
57 #include <stdio.h>
58 #include <libgen.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62
63 #include "config.h"
64 #include "pathnames.h"
65
66 int f_all, f_where;
67 static TAG *section; /* could be passed to cleanup() instead */
68
69 extern char *__progname;
70
71 static void build_page(char *, char **);
72 static void cat(char *);
73 static char *check_pager(char *);
74 static int cleanup(void);
75 static void how(char *);
76 static void jump(char **, char *, char *);
77 static int manual(char *, TAG *, glob_t *);
78 static void onsig(int);
79 static void usage(void);
80
81 sigset_t blocksigs;
82
83 int
main(int argc,char * argv[])84 main(int argc, char *argv[])
85 {
86 extern char *optarg;
87 extern int optind;
88 TAG *defp, *defnewp, *sectnewp, *subp;
89 ENTRY *e_defp, *e_sectp, *e_subp, *ep;
90 glob_t pg;
91 size_t len;
92 int ch, f_cat, f_how, found;
93 char **ap, *cmd, *machine, *p, *p_add, *p_path, *pager, *sflag, *slashp;
94 char *conffile, buf[MAXPATHLEN * 2];
95
96 if (argv[1] == NULL && strcmp(basename(__progname), "help") == 0) {
97 static char *nargv[3];
98 nargv[0] = "man";
99 nargv[1] = "help";
100 nargv[2] = NULL;
101 argv = nargv;
102 argc = 2;
103 }
104
105 machine = sflag = NULL;
106 f_cat = f_how = 0;
107 conffile = p_add = p_path = NULL;
108 while ((ch = getopt(argc, argv, "aC:cfhkM:m:P:s:S:w-")) != -1)
109 switch (ch) {
110 case 'a':
111 f_all = 1;
112 break;
113 case 'C':
114 conffile = optarg;
115 break;
116 case 'c':
117 case '-': /* Deprecated. */
118 f_cat = 1;
119 break;
120 case 'h':
121 f_how = 1;
122 break;
123 case 'm':
124 p_add = optarg;
125 break;
126 case 'M':
127 case 'P': /* Backward compatibility. */
128 p_path = optarg;
129 break;
130 case 's': /* SVR4 compatibility. */
131 sflag = optarg;
132 break;
133 case 'S':
134 machine = optarg;
135 break;
136 /*
137 * The -f and -k options are backward compatible
138 * ways of calling whatis(1) and apropos(1).
139 */
140 case 'f':
141 jump(argv, "-f", "whatis");
142 /* NOTREACHED */
143 case 'k':
144 jump(argv, "-k", "apropos");
145 /* NOTREACHED */
146 case 'w':
147 f_all = f_where = 1;
148 break;
149 case '?':
150 default:
151 usage();
152 }
153 argc -= optind;
154 argv += optind;
155
156 if (!*argv)
157 usage();
158
159 if (!f_cat && !f_how && !f_where) {
160 if (!isatty(1))
161 f_cat = 1;
162 else if ((pager = getenv("MANPAGER")) != NULL &&
163 (*pager != '\0'))
164 pager = check_pager(pager);
165 else if ((pager = getenv("PAGER")) != NULL && (*pager != '\0'))
166 pager = check_pager(pager);
167 else
168 pager = _PATH_PAGER;
169 }
170
171 /* Read the configuration file. */
172 config(conffile);
173
174 /* Get the machine type unless specified by -S. */
175 if (machine == NULL && (machine = getenv("MACHINE")) == NULL)
176 machine = MACHINE;
177
178 /* If there's no _default list, create an empty one. */
179 if ((defp = getlist("_default")) == NULL)
180 defp = addlist("_default");
181
182 /*
183 * 1: If the user specified a MANPATH variable, or set the -M
184 * option, we replace the _default list with the user's list,
185 * appending the entries in the _subdir list and the machine.
186 */
187 if (p_path == NULL)
188 p_path = getenv("MANPATH");
189 if (p_path != NULL) {
190 while ((e_defp = defp->list.tqh_first) != NULL) {
191 free(e_defp->s);
192 TAILQ_REMOVE(&defp->list, e_defp, q);
193 }
194 for (p = strtok(p_path, ":");
195 p != NULL; p = strtok(NULL, ":")) {
196 slashp = p[strlen(p) - 1] == '/' ? "" : "/";
197 e_subp = (subp = getlist("_subdir")) == NULL ?
198 NULL : subp->list.tqh_first;
199 for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) {
200 (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
201 p, slashp, e_subp->s, machine);
202 if ((ep = malloc(sizeof(ENTRY))) == NULL ||
203 (ep->s = strdup(buf)) == NULL)
204 err(1, NULL);
205 TAILQ_INSERT_TAIL(&defp->list, ep, q);
206 }
207 }
208 }
209
210 /*
211 * 2: If the user did not specify MANPATH, -M or a section, rewrite
212 * the _default list to include the _subdir list and the machine.
213 */
214 if (sflag == NULL && argv[1] == NULL)
215 section = NULL;
216 else {
217 if (sflag != NULL && (section = getlist(sflag)) == NULL)
218 errx(1, "unknown manual section `%s'", sflag);
219 else if (sflag == NULL && (section = getlist(*argv)) != NULL)
220 ++argv;
221 }
222 if (p_path == NULL && section == NULL) {
223 defnewp = addlist("_default_new");
224 e_defp =
225 defp->list.tqh_first == NULL ? NULL : defp->list.tqh_first;
226 for (; e_defp != NULL; e_defp = e_defp->q.tqe_next) {
227 slashp =
228 e_defp->s[strlen(e_defp->s) - 1] == '/' ? "" : "/";
229 e_subp = (subp = getlist("_subdir")) == NULL ?
230 NULL : subp->list.tqh_first;
231 for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) {
232 (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
233 e_defp->s, slashp, e_subp->s, machine);
234 if ((ep = malloc(sizeof(ENTRY))) == NULL ||
235 (ep->s = strdup(buf)) == NULL)
236 err(1, NULL);
237 TAILQ_INSERT_TAIL(&defnewp->list, ep, q);
238 }
239 }
240 defp = getlist("_default");
241 while ((e_defp = defp->list.tqh_first) != NULL) {
242 free(e_defp->s);
243 TAILQ_REMOVE(&defp->list, e_defp, q);
244 }
245 free(defp->s);
246 TAILQ_REMOVE(&head, defp, q);
247 defnewp = getlist("_default_new");
248 free(defnewp->s);
249 defnewp->s = "_default";
250 defp = defnewp;
251 }
252
253 /*
254 * 3: If the user set the -m option, insert the user's list before
255 * whatever list we have, again appending the _subdir list and
256 * the machine.
257 */
258 if (p_add != NULL) {
259 e_sectp = NULL;
260 for (p = strtok(p_add, ":"); p != NULL; p = strtok(NULL, ":")) {
261 slashp = p[strlen(p) - 1] == '/' ? "" : "/";
262 e_subp = (subp = getlist("_subdir")) == NULL ?
263 NULL : subp->list.tqh_first;
264 for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) {
265 (void)snprintf(buf, sizeof(buf), "%s%s%s{/%s,}",
266 p, slashp, e_subp->s, machine);
267 if ((ep = malloc(sizeof(ENTRY))) == NULL ||
268 (ep->s = strdup(buf)) == NULL)
269 err(1, NULL);
270 /*
271 * puts it at the end, should be at the top,
272 * but then the added entries would be in
273 * reverse order, fix later when all are added
274 */
275 TAILQ_INSERT_TAIL(&defp->list, ep, q);
276 if (e_sectp == NULL)
277 /* save first added, to-be the new top */
278 e_sectp = ep;
279 }
280 }
281 if (e_sectp != NULL) { /* entries added, fix order */
282 /* save original head */
283 ep->q.tqe_next = defp->list.tqh_first;
284 /* first added entry, new top */
285 defp->list.tqh_first = e_sectp;
286 /* terminate list */
287 *e_sectp->q.tqe_prev = NULL;
288 }
289 }
290 /*
291 * 4: If no -m was specified, and a section was, rewrite the section's
292 * paths (if they have a trailing slash) to append the _subdir list
293 * and the machine. This then becomes the _default list.
294 */
295 if (p_add == NULL && section != NULL) {
296 sectnewp = addlist("_section_new");
297 for (e_sectp = section->list.tqh_first;
298 e_sectp != NULL; e_sectp = e_sectp->q.tqe_next) {
299 if (e_sectp->s[strlen(e_sectp->s) - 1] != '/') {
300 (void)snprintf(buf, sizeof(buf),
301 "%s{/%s,}", e_sectp->s, machine);
302 if ((ep = malloc(sizeof(ENTRY))) == NULL ||
303 (ep->s = strdup(buf)) == NULL)
304 err(1, NULL);
305 TAILQ_INSERT_TAIL(§newp->list, ep, q);
306 continue;
307 }
308 e_subp = (subp = getlist("_subdir")) == NULL ?
309 NULL : subp->list.tqh_first;
310 for (; e_subp != NULL; e_subp = e_subp->q.tqe_next) {
311 (void)snprintf(buf, sizeof(buf), "%s%s{/%s,}",
312 e_sectp->s, e_subp->s, machine);
313 if ((ep = malloc(sizeof(ENTRY))) == NULL ||
314 (ep->s = strdup(buf)) == NULL)
315 err(1, NULL);
316 TAILQ_INSERT_TAIL(§newp->list, ep, q);
317 }
318 }
319 sectnewp->s = section->s;
320 defp = sectnewp;
321 TAILQ_REMOVE(&head, section, q);
322 }
323
324 /*
325 * 5: Search for the files. Set up an interrupt handler, so the
326 * temporary files go away.
327 */
328 (void)signal(SIGINT, onsig);
329 (void)signal(SIGHUP, onsig);
330
331 sigemptyset(&blocksigs);
332 sigaddset(&blocksigs, SIGINT);
333 sigaddset(&blocksigs, SIGHUP);
334
335 memset(&pg, 0, sizeof(pg));
336 for (found = 0; *argv; ++argv)
337 if (manual(*argv, defp, &pg))
338 found = 1;
339
340 /* 6: If nothing found, we're done. */
341 if (!found) {
342 (void)cleanup();
343 exit (1);
344 }
345
346 /* 7: If it's simple, display it fast. */
347 if (f_cat) {
348 for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
349 if (**ap == '\0')
350 continue;
351 cat(*ap);
352 }
353 exit (cleanup());
354 }
355 if (f_how) {
356 for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
357 if (**ap == '\0')
358 continue;
359 how(*ap);
360 }
361 exit(cleanup());
362 }
363 if (f_where) {
364 for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
365 if (**ap == '\0')
366 continue;
367 (void)puts(*ap);
368 }
369 exit(cleanup());
370 }
371
372 /*
373 * 8: We display things in a single command; build a list of things
374 * to display.
375 */
376 for (ap = pg.gl_pathv, len = strlen(pager) + 1; *ap != NULL; ++ap) {
377 if (**ap == '\0')
378 continue;
379 len += strlen(*ap) + 1;
380 }
381 if ((cmd = malloc(len)) == NULL) {
382 warn(NULL);
383 (void)cleanup();
384 exit(1);
385 }
386 p = cmd;
387 len = strlen(pager);
388 memcpy(p, pager, len);
389 p += len;
390 *p++ = ' ';
391 for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
392 if (**ap == '\0')
393 continue;
394 len = strlen(*ap);
395 memcpy(p, *ap, len);
396 p += len;
397 *p++ = ' ';
398 }
399 *--p = '\0';
400
401 /* Use system(3) in case someone's pager is "pager arg1 arg2". */
402 (void)system(cmd);
403
404 exit(cleanup());
405 }
406
407 /*
408 * manual --
409 * Search the manuals for the pages.
410 */
411 static int
manual(char * page,TAG * tag,glob_t * pg)412 manual(char *page, TAG *tag, glob_t *pg)
413 {
414 ENTRY *ep, *e_sufp, *e_tag;
415 TAG *missp, *sufp;
416 int anyfound, cnt, found;
417 char *p, buf[MAXPATHLEN];
418
419 anyfound = 0;
420 buf[0] = '*';
421
422 /* For each element in the list... */
423 e_tag = tag == NULL ? NULL : tag->list.tqh_first;
424 for (; e_tag != NULL; e_tag = e_tag->q.tqe_next) {
425 (void)snprintf(buf, sizeof(buf), "%s/%s.*", e_tag->s, page);
426 if (glob(buf,
427 GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT | GLOB_QUOTE,
428 NULL, pg)) {
429 warn("globbing");
430 (void)cleanup();
431 exit(1);
432 }
433 if (pg->gl_matchc == 0)
434 continue;
435
436 /* Find out if it's really a man page. */
437 for (cnt = pg->gl_pathc - pg->gl_matchc;
438 cnt < pg->gl_pathc; ++cnt) {
439
440 /*
441 * Try the _suffix key words first.
442 *
443 * XXX
444 * Older versions of man.conf didn't have the suffix
445 * key words, it was assumed that everything was a .0.
446 * We just test for .0 first, it's fast and probably
447 * going to hit.
448 */
449 (void)snprintf(buf, sizeof(buf), "*/%s.0", page);
450 if (!fnmatch(buf, pg->gl_pathv[cnt], 0))
451 goto next;
452
453 e_sufp = (sufp = getlist("_suffix")) == NULL ?
454 NULL : sufp->list.tqh_first;
455 for (found = 0;
456 e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) {
457 (void)snprintf(buf,
458 sizeof(buf), "*/%s%s", page, e_sufp->s);
459 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
460 found = 1;
461 break;
462 }
463 }
464 if (found)
465 goto next;
466
467 /* Try the _build key words next. */
468 e_sufp = (sufp = getlist("_build")) == NULL ?
469 NULL : sufp->list.tqh_first;
470 for (found = 0;
471 e_sufp != NULL; e_sufp = e_sufp->q.tqe_next) {
472 for (p = e_sufp->s;
473 *p != '\0' && !isspace(*p); ++p);
474 if (*p == '\0')
475 continue;
476 *p = '\0';
477 (void)snprintf(buf,
478 sizeof(buf), "*/%s%s", page, e_sufp->s);
479 if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
480 if (!f_where)
481 build_page(p + 1,
482 &pg->gl_pathv[cnt]);
483 *p = ' ';
484 found = 1;
485 break;
486 }
487 *p = ' ';
488 }
489 if (found) {
490 next: anyfound = 1;
491 if (!f_all) {
492 /* Delete any other matches. */
493 while (++cnt< pg->gl_pathc)
494 pg->gl_pathv[cnt] = "";
495 break;
496 }
497 continue;
498 }
499
500 /* It's not a man page, forget about it. */
501 pg->gl_pathv[cnt] = "";
502 }
503
504 if (anyfound && !f_all)
505 break;
506 }
507
508 /* If not found, enter onto the missing list. */
509 if (!anyfound) {
510 sigset_t osigs;
511
512 sigprocmask(SIG_BLOCK, &blocksigs, &osigs);
513
514 if ((missp = getlist("_missing")) == NULL)
515 missp = addlist("_missing");
516 if ((ep = malloc(sizeof(ENTRY))) == NULL ||
517 (ep->s = strdup(page)) == NULL) {
518 warn(NULL);
519 (void)cleanup();
520 exit(1);
521 }
522 TAILQ_INSERT_TAIL(&missp->list, ep, q);
523 sigprocmask(SIG_SETMASK, &osigs, NULL);
524 }
525 return (anyfound);
526 }
527
528 /*
529 * build_page --
530 * Build a man page for display.
531 */
532 static void
build_page(char * fmt,char ** pathp)533 build_page(char *fmt, char **pathp)
534 {
535 static int warned;
536 ENTRY *ep;
537 TAG *intmpp;
538 int fd, n;
539 char *p, *b;
540 char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[MAXPATHLEN];
541 sigset_t osigs;
542
543 /* Let the user know this may take awhile. */
544 if (!warned) {
545 warned = 1;
546 warnx("Formatting manual page...");
547 }
548
549 /*
550 * Historically man chdir'd to the root of the man tree.
551 * This was used in man pages that contained relative ".so"
552 * directives (including other man pages for command aliases etc.)
553 * It even went one step farther, by examining the first line
554 * of the man page and parsing the .so filename so it would
555 * make hard(?) links to the cat'ted man pages for space savings.
556 * (We don't do that here, but we could).
557 */
558
559 /* copy and find the end */
560 for (b = buf, p = *pathp; (*b++ = *p++) != '\0';)
561 continue;
562
563 /* skip the last two path components, page name and man[n] */
564 for (--b, n = 2; b != buf; b--)
565 if (*b == '/')
566 if (--n == 0) {
567 *b = '\0';
568 (void) chdir(buf);
569 }
570
571
572 /* Add a remove-when-done list. */
573 sigprocmask(SIG_BLOCK, &blocksigs, &osigs);
574 if ((intmpp = getlist("_intmp")) == NULL)
575 intmpp = addlist("_intmp");
576 sigprocmask(SIG_SETMASK, &osigs, NULL);
577
578 /* Move to the printf(3) format string. */
579 for (; *fmt && isspace(*fmt); ++fmt)
580 ;
581
582 /*
583 * Get a temporary file and build a version of the file
584 * to display. Replace the old file name with the new one.
585 */
586 (void)strlcpy(tpath, _PATH_TMPFILE, sizeof(tpath));
587 if ((fd = mkstemp(tpath)) == -1) {
588 warn("%s", tpath);
589 (void)cleanup();
590 exit(1);
591 }
592 (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath);
593 (void)snprintf(cmd, sizeof(cmd), buf, *pathp);
594 (void)system(cmd);
595 (void)close(fd);
596 if ((*pathp = strdup(tpath)) == NULL) {
597 warn(NULL);
598 (void)cleanup();
599 exit(1);
600 }
601
602 /* Link the built file into the remove-when-done list. */
603 if ((ep = malloc(sizeof(ENTRY))) == NULL) {
604 warn(NULL);
605 (void)cleanup();
606 exit(1);
607 }
608 ep->s = *pathp;
609 TAILQ_INSERT_TAIL(&intmpp->list, ep, q);
610 }
611
612 /*
613 * how --
614 * display how information
615 */
616 static void
how(char * fname)617 how(char *fname)
618 {
619 FILE *fp;
620
621 int lcnt, print;
622 char *p, buf[256];
623
624 if (!(fp = fopen(fname, "r"))) {
625 warn("%s", fname);
626 (void)cleanup();
627 exit (1);
628 }
629 #define S1 "SYNOPSIS"
630 #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
631 #define D1 "DESCRIPTION"
632 #define D2 "D\bDE\bES\bSC\bCR\bRI\bIP\bPT\bTI\bIO\bON\bN"
633 for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) {
634 if (!strncmp(buf, S1, sizeof(S1) - 1) ||
635 !strncmp(buf, S2, sizeof(S2) - 1)) {
636 print = 1;
637 continue;
638 } else if (!strncmp(buf, D1, sizeof(D1) - 1) ||
639 !strncmp(buf, D2, sizeof(D2) - 1))
640 return;
641 if (!print)
642 continue;
643 if (*buf == '\n')
644 ++lcnt;
645 else {
646 while (lcnt) {
647 --lcnt;
648 (void)putchar('\n');
649 }
650 for (p = buf; isspace(*p); ++p)
651 ;
652 (void)fputs(p, stdout);
653 }
654 }
655 (void)fclose(fp);
656 }
657
658 /*
659 * cat --
660 * cat out the file
661 */
662 static void
cat(char * fname)663 cat(char *fname)
664 {
665 int fd, n;
666 char buf[2048];
667
668 if ((fd = open(fname, O_RDONLY, 0)) < 0) {
669 warn("%s", fname);
670 (void)cleanup();
671 exit(1);
672 }
673 while ((n = read(fd, buf, sizeof(buf))) > 0)
674 if (write(STDOUT_FILENO, buf, n) != n) {
675 warn("write");
676 (void)cleanup();
677 exit (1);
678 }
679 if (n == -1) {
680 warn("read");
681 (void)cleanup();
682 exit(1);
683 }
684 (void)close(fd);
685 }
686
687 /*
688 * check_pager --
689 * check the user supplied page information
690 */
691 static char *
check_pager(char * name)692 check_pager(char *name)
693 {
694 char *p, *save;
695 int len;
696
697 /*
698 * if the user uses "more", we make it "more -s"; watch out for
699 * PAGER = "mypager /usr/bin/more"
700 */
701 for (p = name; *p && !isspace(*p); ++p)
702 ;
703 for (; p > name && *p != '/'; --p)
704 ;
705 if (p != name)
706 ++p;
707
708 /* make sure it's "more", not "morex" */
709 if (!strncmp(p, "more", 4) && (p[4] == '\0' || isspace(p[4]))){
710 save = name;
711 /* allocate space to add the "-s" */
712 len = strlen(save) + 1 + sizeof("-s");
713 if ((name = malloc(len)) == NULL)
714 err(1, NULL);
715 (void)snprintf(name, len, "%s %s", save, "-s");
716 }
717 return(name);
718 }
719
720 /*
721 * jump --
722 * strip out flag argument and jump
723 */
724 static void
jump(char ** argv,char * flag,char * name)725 jump(char **argv, char *flag, char *name)
726 {
727 char **arg;
728
729 argv[0] = name;
730 for (arg = argv + 1; *arg; ++arg)
731 if (!strcmp(*arg, flag))
732 break;
733 for (; *arg; ++arg)
734 arg[0] = arg[1];
735 execvp(name, argv);
736 (void)fprintf(stderr, "%s: Command not found.\n", name);
737 exit(1);
738 }
739
740 /*
741 * onsig --
742 * If signaled, delete the temporary files.
743 */
744 static void
onsig(int signo)745 onsig(int signo)
746 {
747 (void)cleanup(); /* XXX signal race */
748
749 (void)signal(signo, SIG_DFL);
750 (void)kill(getpid(), signo);
751
752 /* NOTREACHED */
753 _exit(1);
754 }
755
756 /*
757 * cleanup --
758 * Clean up temporary files, show any error messages.
759 */
760 static int
cleanup(void)761 cleanup(void)
762 {
763 TAG *intmpp, *missp;
764 ENTRY *ep;
765 int rval;
766
767 rval = 0;
768 ep = (missp = getlist("_missing")) == NULL ?
769 NULL : missp->list.tqh_first;
770 if (ep != NULL)
771 for (; ep != NULL; ep = ep->q.tqe_next) {
772 if (section)
773 warnx("no entry for %s in section %s of the manual.",
774 ep->s, section->s);
775 else
776 warnx("no entry for %s in the manual.", ep->s);
777 rval = 1;
778 }
779
780 ep = (intmpp = getlist("_intmp")) == NULL ?
781 NULL : intmpp->list.tqh_first;
782 for (; ep != NULL; ep = ep->q.tqe_next)
783 (void)unlink(ep->s);
784 return (rval);
785 }
786
787 /*
788 * usage --
789 * print usage message and die
790 */
791 static void
usage(void)792 usage(void)
793 {
794 (void)fprintf(stderr, "usage: %s [-achw] [-C file] [-M path] [-m path] "
795 "[-S subsection] [-s section]\n\t [section] name [...]\n",
796 __progname);
797 (void)fprintf(stderr, "usage: %s -f command\n", __progname);
798 (void)fprintf(stderr, "usage: %s -k keyword\n", __progname);
799 exit(1);
800 }
801