Annotation of src/usr.bin/man/man.c, Revision 1.43
1.43 ! schwarze 1: /* $OpenBSD: man.c,v 1.42 2011/07/05 05:47:20 schwarze Exp $ */
1.1 deraadt 2: /* $NetBSD: man.c,v 1.7 1995/09/28 06:05:34 tls Exp $ */
3:
4: /*
1.42 schwarze 5: * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
1.39 schwarze 6: *
7: * Permission to use, copy, modify, and distribute this software for any
8: * purpose with or without fee is hereby granted, provided that the above
9: * copyright notice and this permission notice appear in all copies.
10: *
11: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18: */
19:
20: /*
1.1 deraadt 21: * Copyright (c) 1987, 1993, 1994, 1995
22: * The Regents of the University of California. All rights reserved.
23: *
24: * Redistribution and use in source and binary forms, with or without
25: * modification, are permitted provided that the following conditions
26: * are met:
27: * 1. Redistributions of source code must retain the above copyright
28: * notice, this list of conditions and the following disclaimer.
29: * 2. Redistributions in binary form must reproduce the above copyright
30: * notice, this list of conditions and the following disclaimer in the
31: * documentation and/or other materials provided with the distribution.
1.25 millert 32: * 3. Neither the name of the University nor the names of its contributors
1.1 deraadt 33: * may be used to endorse or promote products derived from this software
34: * without specific prior written permission.
35: *
36: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
37: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
38: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
39: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
40: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
41: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
42: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
43: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
44: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
45: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
46: * SUCH DAMAGE.
47: */
48:
1.42 schwarze 49: #include <sys/types.h>
1.1 deraadt 50: #include <sys/param.h>
51: #include <sys/queue.h>
1.42 schwarze 52: #include <sys/stat.h>
1.1 deraadt 53:
54: #include <ctype.h>
55: #include <err.h>
56: #include <errno.h>
57: #include <fcntl.h>
58: #include <fnmatch.h>
59: #include <glob.h>
60: #include <signal.h>
61: #include <stdio.h>
1.11 deraadt 62: #include <libgen.h>
1.1 deraadt 63: #include <stdlib.h>
64: #include <string.h>
65: #include <unistd.h>
66:
67: #include "config.h"
68: #include "pathnames.h"
69:
70: int f_all, f_where;
1.39 schwarze 71: static char gbuf[MAXPATHLEN * 2];
72: static TAG *section;
1.1 deraadt 73:
1.8 millert 74: extern char *__progname;
75:
1.39 schwarze 76: static void clearlist(TAG *);
77: static void parse_path(TAG *, char *);
78: static void append_subdirs(TAG *, const char *);
1.20 millert 79: static void build_page(char *, char **);
80: static void cat(char *);
81: static char *check_pager(char *);
1.41 deraadt 82: static int cleanup(int);
1.20 millert 83: static void how(char *);
84: static void jump(char **, char *, char *);
85: static int manual(char *, TAG *, glob_t *);
1.43 ! schwarze 86: static void check_companion(char **, TAG *);
1.20 millert 87: static void onsig(int);
88: static void usage(void);
1.1 deraadt 89:
1.19 deraadt 90: sigset_t blocksigs;
91:
1.1 deraadt 92: int
1.21 deraadt 93: main(int argc, char *argv[])
1.1 deraadt 94: {
95: extern char *optarg;
96: extern int optind;
1.39 schwarze 97: TAG *searchlist;
98: ENTRY *ep;
1.1 deraadt 99: glob_t pg;
100: size_t len;
101: int ch, f_cat, f_how, found;
1.39 schwarze 102: char **ap, *cmd, *machine, *p, *p_add, *p_path, *pager, *sflag;
103: char *conffile;
1.11 deraadt 104:
105: if (argv[1] == NULL && strcmp(basename(__progname), "help") == 0) {
106: static char *nargv[3];
107: nargv[0] = "man";
1.15 aaron 108: nargv[1] = "help";
1.11 deraadt 109: nargv[2] = NULL;
110: argv = nargv;
111: argc = 2;
112: }
1.1 deraadt 113:
1.7 millert 114: machine = sflag = NULL;
1.1 deraadt 115: f_cat = f_how = 0;
116: conffile = p_add = p_path = NULL;
1.23 millert 117: while ((ch = getopt(argc, argv, "aC:cfhkM:m:P:s:S:w-")) != -1)
1.1 deraadt 118: switch (ch) {
119: case 'a':
120: f_all = 1;
121: break;
122: case 'C':
123: conffile = optarg;
124: break;
125: case 'c':
126: case '-': /* Deprecated. */
127: f_cat = 1;
128: break;
129: case 'h':
130: f_how = 1;
131: break;
132: case 'm':
133: p_add = optarg;
134: break;
135: case 'M':
136: case 'P': /* Backward compatibility. */
137: p_path = optarg;
138: break;
1.7 millert 139: case 's': /* SVR4 compatibility. */
140: sflag = optarg;
141: break;
142: case 'S':
143: machine = optarg;
144: break;
1.1 deraadt 145: /*
1.22 espie 146: * The -f and -k options are backward compatible
147: * ways of calling whatis(1) and apropos(1).
1.1 deraadt 148: */
149: case 'f':
150: jump(argv, "-f", "whatis");
151: /* NOTREACHED */
152: case 'k':
153: jump(argv, "-k", "apropos");
154: /* NOTREACHED */
155: case 'w':
1.43 ! schwarze 156: f_where = 1;
1.1 deraadt 157: break;
158: case '?':
159: default:
160: usage();
161: }
162: argc -= optind;
163: argv += optind;
164:
165: if (!*argv)
166: usage();
167:
1.17 millert 168: if (!f_cat && !f_how && !f_where) {
1.1 deraadt 169: if (!isatty(1))
170: f_cat = 1;
1.27 millert 171: else if ((pager = getenv("MANPAGER")) != NULL &&
172: (*pager != '\0'))
173: pager = check_pager(pager);
1.13 pjanzen 174: else if ((pager = getenv("PAGER")) != NULL && (*pager != '\0'))
1.1 deraadt 175: pager = check_pager(pager);
176: else
177: pager = _PATH_PAGER;
1.17 millert 178: }
1.1 deraadt 179:
180: /* Read the configuration file. */
181: config(conffile);
182:
1.39 schwarze 183: /*
184: * 1: If the user specified a section,
185: * use the section list from the configuration file.
186: * Otherwise, fall back to the default list or to an empty list.
187: */
188: if (sflag && (section = getlist(sflag)) == NULL)
189: errx(1, "unknown manual section `%s'", sflag);
190: else if (argv[1] && (section = getlist(*argv)) != NULL)
191: ++argv;
192:
193: searchlist = section;
194: if (searchlist == NULL)
195: searchlist = getlist("_default");
196: if (searchlist == NULL)
197: searchlist = addlist("_default");
1.1 deraadt 198:
199: /*
1.39 schwarze 200: * 2: If the user set the -M option or defined the MANPATH variable,
201: * clear what we have and take the user's list instead.
1.1 deraadt 202: */
203: if (p_path == NULL)
204: p_path = getenv("MANPATH");
205:
1.39 schwarze 206: if (p_path) {
207: clearlist(searchlist);
208: parse_path(searchlist, p_path);
1.1 deraadt 209: }
210:
211: /*
212: * 3: If the user set the -m option, insert the user's list before
1.39 schwarze 213: * whatever list we have.
1.1 deraadt 214: */
1.39 schwarze 215: if (p_add)
216: parse_path(searchlist, p_add);
217:
1.1 deraadt 218: /*
1.39 schwarze 219: * 4: Append the _subdir list where appropriate,
220: * and always append the machine type.
1.1 deraadt 221: */
1.39 schwarze 222: if (machine || (machine = getenv("MACHINE")))
223: for (p = machine; *p; ++p)
224: *p = tolower(*p);
225: else
226: machine = MACHINE;
227:
228: append_subdirs(searchlist, machine);
1.1 deraadt 229:
230: /*
231: * 5: Search for the files. Set up an interrupt handler, so the
232: * temporary files go away.
233: */
234: (void)signal(SIGINT, onsig);
235: (void)signal(SIGHUP, onsig);
236:
1.19 deraadt 237: sigemptyset(&blocksigs);
238: sigaddset(&blocksigs, SIGINT);
239: sigaddset(&blocksigs, SIGHUP);
240:
1.1 deraadt 241: memset(&pg, 0, sizeof(pg));
242: for (found = 0; *argv; ++argv)
1.39 schwarze 243: if (manual(*argv, searchlist, &pg))
1.1 deraadt 244: found = 1;
245:
246: /* 6: If nothing found, we're done. */
247: if (!found) {
1.41 deraadt 248: (void)cleanup(0);
1.1 deraadt 249: exit (1);
250: }
251:
252: /* 7: If it's simple, display it fast. */
253: if (f_cat) {
254: for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
255: if (**ap == '\0')
256: continue;
257: cat(*ap);
258: }
1.41 deraadt 259: exit (cleanup(0));
1.1 deraadt 260: }
261: if (f_how) {
262: for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
263: if (**ap == '\0')
264: continue;
265: how(*ap);
266: }
1.41 deraadt 267: exit(cleanup(0));
1.1 deraadt 268: }
269: if (f_where) {
270: for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
271: if (**ap == '\0')
272: continue;
1.17 millert 273: (void)puts(*ap);
1.1 deraadt 274: }
1.41 deraadt 275: exit(cleanup(0));
1.1 deraadt 276: }
1.17 millert 277:
1.1 deraadt 278: /*
279: * 8: We display things in a single command; build a list of things
280: * to display.
281: */
282: for (ap = pg.gl_pathv, len = strlen(pager) + 1; *ap != NULL; ++ap) {
283: if (**ap == '\0')
284: continue;
285: len += strlen(*ap) + 1;
286: }
287: if ((cmd = malloc(len)) == NULL) {
288: warn(NULL);
1.41 deraadt 289: (void)cleanup(0);
1.1 deraadt 290: exit(1);
291: }
292: p = cmd;
293: len = strlen(pager);
1.17 millert 294: memcpy(p, pager, len);
1.1 deraadt 295: p += len;
296: *p++ = ' ';
297: for (ap = pg.gl_pathv; *ap != NULL; ++ap) {
298: if (**ap == '\0')
299: continue;
300: len = strlen(*ap);
1.17 millert 301: memcpy(p, *ap, len);
1.1 deraadt 302: p += len;
303: *p++ = ' ';
304: }
1.12 deraadt 305: *--p = '\0';
1.1 deraadt 306:
307: /* Use system(3) in case someone's pager is "pager arg1 arg2". */
308: (void)system(cmd);
309:
1.41 deraadt 310: exit(cleanup(0));
1.39 schwarze 311: }
312:
313: /*
314: * clearlist --
315: * Remove all entries from a list,
316: * but leave the list header intact.
317: */
318: static void
319: clearlist(TAG *t)
320: {
321: ENTRY *e;
322:
323: while ((e = TAILQ_FIRST(&t->list)) != NULL) {
324: free(e->s);
325: TAILQ_REMOVE(&t->list, e, q);
326: free(e);
327: }
328: }
329:
330: /*
331: * parse_path --
332: * Split the -M or -m argument or the MANPATH variable at colons,
333: * and insert the parts into the searchlist.
334: */
335: static void
336: parse_path(TAG *t, char *path)
337: {
338: ENTRY *eplast = NULL, *ep;
339: char *p, *slashp;
340:
341: while ((p = strsep(&path, ":")) != NULL) {
342: if ((ep = malloc(sizeof(ENTRY))) == NULL)
343: err(1, NULL);
344:
345: /*
346: * Bring section specific paths to the same format as
347: * used in the configuration file, ending in /{cat,man}N.
348: */
349: if (section) {
350: slashp = p[strlen(p) - 1] == '/' ? "" : "/";
351: (void)snprintf(gbuf, sizeof(gbuf),
352: "%s%s{cat,man}%s", p, slashp, t->s);
353: if ((ep->s = strdup(gbuf)) == NULL)
354: err(1, NULL);
355: }
356:
357: /* Without a section, subdirs will be appended later. */
358: else
359: if ((ep->s = strdup(p)) == NULL)
360: err(1, NULL);
361:
362: /*
363: * Even in case of -M, inserting in front is fine:
364: * We have just cleared the list.
365: */
366: if (eplast)
367: TAILQ_INSERT_AFTER(&t->list, eplast, ep, q);
368: else
369: TAILQ_INSERT_HEAD(&t->list, ep, q);
370: eplast = ep;
371: }
372: }
373:
374: /*
375: * append_subdirs --
376: * Iterate the searchlist and append section and machine
377: * subdirectories as needed.
378: */
379: static void
380: append_subdirs(TAG *t, const char *machine)
381: {
382: TAG *tsub;
383: ENTRY *eold, *elast, *enew, *esub;
384: char *slashp;
385:
386: eold = elast = TAILQ_FIRST(&t->list);
387: while (eold) {
388:
389: /*
390: * Section subdirectories *not* ending in a slash
391: * only get the machine suffix: They already had
392: * the {cat,man}N part in the configuration file
393: * or got it in parse_path().
394: */
395: if (section && eold->s[strlen(eold->s)-1] != '/') {
396: (void)snprintf(gbuf, sizeof(gbuf), "%s{/%s,}",
397: eold->s, machine);
398: free(eold->s);
399: if ((eold->s = strdup(gbuf)) == NULL)
400: err(1, NULL);
401: eold = elast = TAILQ_NEXT(eold, q);
402: continue;
403: }
404:
405: /*
406: * Without a section, expand each entry using the
407: * subdir list, then drop the original entry.
408: */
409: esub = (tsub = getlist("_subdir")) == NULL ?
410: NULL : TAILQ_FIRST(&tsub->list);
411: while (esub) {
412: slashp = eold->s[strlen(eold->s)-1] == '/' ? "" : "/";
413: (void)snprintf(gbuf, sizeof(gbuf), "%s%s%s{/%s,}",
414: eold->s, slashp, esub->s, machine);
415: if ((enew = malloc(sizeof(ENTRY))) == NULL ||
416: (enew->s = strdup(gbuf)) == NULL)
417: err(1, NULL);
418: TAILQ_INSERT_AFTER(&t->list, elast, enew, q);
419: elast = enew;
420: esub = TAILQ_NEXT(esub, q);
421: }
422: elast = TAILQ_NEXT(elast, q);
423: TAILQ_REMOVE(&t->list, eold, q);
424: eold = elast;
425: }
1.1 deraadt 426: }
427:
428: /*
429: * manual --
430: * Search the manuals for the pages.
431: */
432: static int
1.26 deraadt 433: manual(char *page, TAG *tag, glob_t *pg)
1.1 deraadt 434: {
435: ENTRY *ep, *e_sufp, *e_tag;
436: TAG *missp, *sufp;
1.43 ! schwarze 437: int anyfound, cnt, found, globres;
1.6 deraadt 438: char *p, buf[MAXPATHLEN];
1.1 deraadt 439:
440: anyfound = 0;
441: buf[0] = '*';
442:
1.43 ! schwarze 443: /* Expand the search path. */
! 444: if (f_all != f_where) {
! 445: e_tag = tag == NULL ? NULL : TAILQ_FIRST(&tag->list);
! 446: for (; e_tag != NULL; e_tag = TAILQ_NEXT(e_tag, q)) {
! 447: if (glob(e_tag->s, GLOB_BRACE | GLOB_NOSORT,
! 448: NULL, pg)) {
! 449: /* No GLOB_NOMATCH here due to {arch,}. */
! 450: warn("globbing directories");
! 451: (void)cleanup(0);
! 452: exit(1);
! 453: }
! 454: ep = e_tag;
! 455: for (cnt = 0; cnt < pg->gl_pathc; cnt++) {
! 456: if ((e_tag = malloc(sizeof(ENTRY))) == NULL ||
! 457: (e_tag->s = strdup(pg->gl_pathv[cnt])) ==
! 458: NULL) {
! 459: warn(NULL);
! 460: (void)cleanup(0);
! 461: exit(1);
! 462: }
! 463: TAILQ_INSERT_BEFORE(ep, e_tag, q);
! 464: }
! 465: free(ep->s);
! 466: TAILQ_REMOVE(&tag->list, ep, q);
! 467: free(ep);
! 468: globfree(pg);
! 469: pg->gl_pathc = 0;
! 470: }
! 471: }
! 472:
1.1 deraadt 473: /* For each element in the list... */
1.29 otto 474: e_tag = tag == NULL ? NULL : TAILQ_FIRST(&tag->list);
475: for (; e_tag != NULL; e_tag = TAILQ_NEXT(e_tag, q)) {
1.1 deraadt 476: (void)snprintf(buf, sizeof(buf), "%s/%s.*", e_tag->s, page);
1.43 ! schwarze 477: switch (glob(buf, GLOB_APPEND | GLOB_BRACE | GLOB_NOSORT,
1.1 deraadt 478: NULL, pg)) {
1.43 ! schwarze 479: case (0):
! 480: break;
! 481: case (GLOB_NOMATCH):
! 482: continue;
! 483: default:
! 484: warn("globbing files");
1.41 deraadt 485: (void)cleanup(0);
1.1 deraadt 486: exit(1);
487: }
488: if (pg->gl_matchc == 0)
489: continue;
490:
491: /* Find out if it's really a man page. */
492: for (cnt = pg->gl_pathc - pg->gl_matchc;
493: cnt < pg->gl_pathc; ++cnt) {
494:
1.43 ! schwarze 495: if (!f_all || !f_where) {
! 496: check_companion(pg->gl_pathv + cnt, tag);
! 497: if (*pg->gl_pathv[cnt] == '\0')
! 498: continue;
! 499: }
1.42 schwarze 500:
1.1 deraadt 501: /*
502: * Try the _suffix key words first.
503: *
504: * XXX
505: * Older versions of man.conf didn't have the suffix
506: * key words, it was assumed that everything was a .0.
507: * We just test for .0 first, it's fast and probably
508: * going to hit.
509: */
510: (void)snprintf(buf, sizeof(buf), "*/%s.0", page);
511: if (!fnmatch(buf, pg->gl_pathv[cnt], 0))
512: goto next;
513:
514: e_sufp = (sufp = getlist("_suffix")) == NULL ?
1.29 otto 515: NULL : TAILQ_FIRST(&sufp->list);
1.1 deraadt 516: for (found = 0;
1.29 otto 517: e_sufp != NULL; e_sufp = TAILQ_NEXT(e_sufp, q)) {
1.1 deraadt 518: (void)snprintf(buf,
519: sizeof(buf), "*/%s%s", page, e_sufp->s);
520: if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
521: found = 1;
522: break;
523: }
524: }
525: if (found)
526: goto next;
527:
528: /* Try the _build key words next. */
529: e_sufp = (sufp = getlist("_build")) == NULL ?
1.29 otto 530: NULL : TAILQ_FIRST(&sufp->list);
1.1 deraadt 531: for (found = 0;
1.29 otto 532: e_sufp != NULL; e_sufp = TAILQ_NEXT(e_sufp, q)) {
1.1 deraadt 533: for (p = e_sufp->s;
534: *p != '\0' && !isspace(*p); ++p);
535: if (*p == '\0')
536: continue;
537: *p = '\0';
538: (void)snprintf(buf,
539: sizeof(buf), "*/%s%s", page, e_sufp->s);
540: if (!fnmatch(buf, pg->gl_pathv[cnt], 0)) {
541: if (!f_where)
542: build_page(p + 1,
543: &pg->gl_pathv[cnt]);
544: *p = ' ';
545: found = 1;
546: break;
547: }
548: *p = ' ';
549: }
550: if (found) {
551: next: anyfound = 1;
1.43 ! schwarze 552: if (!f_all && !f_where) {
1.1 deraadt 553: /* Delete any other matches. */
554: while (++cnt< pg->gl_pathc)
555: pg->gl_pathv[cnt] = "";
556: break;
557: }
558: continue;
559: }
560:
561: /* It's not a man page, forget about it. */
562: pg->gl_pathv[cnt] = "";
563: }
564:
1.43 ! schwarze 565: if (anyfound && !f_all && !f_where)
1.1 deraadt 566: break;
567: }
568:
569: /* If not found, enter onto the missing list. */
570: if (!anyfound) {
1.19 deraadt 571: sigset_t osigs;
572:
573: sigprocmask(SIG_BLOCK, &blocksigs, &osigs);
574:
1.1 deraadt 575: if ((missp = getlist("_missing")) == NULL)
576: missp = addlist("_missing");
577: if ((ep = malloc(sizeof(ENTRY))) == NULL ||
578: (ep->s = strdup(page)) == NULL) {
579: warn(NULL);
1.41 deraadt 580: (void)cleanup(0);
1.1 deraadt 581: exit(1);
582: }
583: TAILQ_INSERT_TAIL(&missp->list, ep, q);
1.19 deraadt 584: sigprocmask(SIG_SETMASK, &osigs, NULL);
1.1 deraadt 585: }
586: return (anyfound);
1.42 schwarze 587: }
588:
589: /*
590: * check_companion --
1.43 ! schwarze 591: * Check for a companion [un]formatted page.
! 592: * If one is found, skip this page.
! 593: * Use the companion instead, unless it will be found anyway.
1.42 schwarze 594: */
595: static void
1.43 ! schwarze 596: check_companion(char **orig, TAG *tag) {
1.42 schwarze 597: struct stat sb_orig, sb_comp;
598: char *p, *pext, comp[MAXPATHLEN];
1.43 ! schwarze 599: ENTRY *entry;
1.42 schwarze 600: size_t len;
601: int found;
602:
603: len = strlcpy(comp, *orig, sizeof(comp));
604: /* The minus 2 avoids a buffer overrun in case of a trailing dot. */
605: p = comp + len - 2;
606:
607: /* Locate the file name extension. */
608: while (p > comp && *p != '.' && *p != '/')
609: p--;
610: if (*p != '.')
611: return;
612: pext = p + 1;
613:
614: /* Search for slashes. */
615: for (found = 0; 1; p--) {
1.43 ! schwarze 616: if (*p != '/')
! 617: continue;
1.42 schwarze 618:
619: /* Did not find /{cat,man}. */
620: if (p == comp)
621: return;
622:
623: /* Pass over one slash, the one before "page". */
1.43 ! schwarze 624: if (!found++) {
! 625: len = p - comp;
1.42 schwarze 626: continue;
1.43 ! schwarze 627: }
1.42 schwarze 628:
629: /* Rewrite manN/page.N <-> catN/page.0. */
630: if (!strncmp(p+1, "man", 3)) {
631: memcpy(++p, "cat", 3);
632: *pext++ = '0';
633: break;
634: } else if (!strncmp(p+1, "cat", 3)) {
635: memcpy(++p, "man", 3);
636: p += 3;
637: while (*p != '/' && pext < comp + sizeof(comp) - 1)
638: *pext++ = *p++;
639: break;
640:
641: /* Accept one architecture subdir, but not more. */
642: } else if (found > 2)
643: return;
644: }
645: *pext = '\0';
646:
647: /* Check whether both files exist. */
648: if (stat(*orig, &sb_orig) || stat(comp, &sb_comp))
649: return;
650:
651: /* No action if the companion file is older. */
652: if (sb_orig.st_mtim.tv_sec > sb_comp.st_mtim.tv_sec || (
653: sb_orig.st_mtim.tv_sec == sb_comp.st_mtim.tv_sec &&
654: sb_orig.st_mtim.tv_nsec > sb_comp.st_mtim.tv_nsec))
655: return;
1.43 ! schwarze 656:
! 657: /* Drop the companion if it is in the path, too. */
! 658: if (f_all || f_where)
! 659: for(entry = TAILQ_FIRST(&tag->list); entry != NULL;
! 660: entry = TAILQ_NEXT(entry, q))
! 661: if (!strncmp(entry->s, comp, len)) {
! 662: **orig = '\0';
! 663: return;
! 664: }
1.42 schwarze 665:
666: /* The companion file is newer, use it. */
667: free(*orig);
668: if ((p = strdup(comp)) == NULL) {
669: warn(NULL);
670: (void)cleanup(0);
671: exit(1);
672: }
673: *orig = p;
1.1 deraadt 674: }
675:
1.17 millert 676: /*
1.1 deraadt 677: * build_page --
678: * Build a man page for display.
679: */
680: static void
1.21 deraadt 681: build_page(char *fmt, char **pathp)
1.1 deraadt 682: {
683: ENTRY *ep;
684: TAG *intmpp;
685: int fd, n;
686: char *p, *b;
1.4 deraadt 687: char buf[MAXPATHLEN], cmd[MAXPATHLEN], tpath[MAXPATHLEN];
1.19 deraadt 688: sigset_t osigs;
1.1 deraadt 689:
690: /*
1.17 millert 691: * Historically man chdir'd to the root of the man tree.
1.1 deraadt 692: * This was used in man pages that contained relative ".so"
693: * directives (including other man pages for command aliases etc.)
694: * It even went one step farther, by examining the first line
695: * of the man page and parsing the .so filename so it would
696: * make hard(?) links to the cat'ted man pages for space savings.
697: * (We don't do that here, but we could).
698: */
1.17 millert 699:
1.1 deraadt 700: /* copy and find the end */
701: for (b = buf, p = *pathp; (*b++ = *p++) != '\0';)
702: continue;
1.17 millert 703:
1.1 deraadt 704: /* skip the last two path components, page name and man[n] */
705: for (--b, n = 2; b != buf; b--)
706: if (*b == '/')
707: if (--n == 0) {
708: *b = '\0';
709: (void) chdir(buf);
710: }
711:
712:
713: /* Add a remove-when-done list. */
1.19 deraadt 714: sigprocmask(SIG_BLOCK, &blocksigs, &osigs);
1.1 deraadt 715: if ((intmpp = getlist("_intmp")) == NULL)
716: intmpp = addlist("_intmp");
1.19 deraadt 717: sigprocmask(SIG_SETMASK, &osigs, NULL);
1.1 deraadt 718:
719: /* Move to the printf(3) format string. */
1.16 deraadt 720: for (; *fmt && isspace(*fmt); ++fmt)
721: ;
1.1 deraadt 722:
723: /*
724: * Get a temporary file and build a version of the file
725: * to display. Replace the old file name with the new one.
726: */
1.18 deraadt 727: (void)strlcpy(tpath, _PATH_TMPFILE, sizeof(tpath));
1.1 deraadt 728: if ((fd = mkstemp(tpath)) == -1) {
729: warn("%s", tpath);
1.41 deraadt 730: (void)cleanup(0);
1.1 deraadt 731: exit(1);
732: }
733: (void)snprintf(buf, sizeof(buf), "%s > %s", fmt, tpath);
734: (void)snprintf(cmd, sizeof(cmd), buf, *pathp);
735: (void)system(cmd);
736: (void)close(fd);
737: if ((*pathp = strdup(tpath)) == NULL) {
738: warn(NULL);
1.41 deraadt 739: (void)cleanup(0);
1.1 deraadt 740: exit(1);
741: }
742:
743: /* Link the built file into the remove-when-done list. */
744: if ((ep = malloc(sizeof(ENTRY))) == NULL) {
745: warn(NULL);
1.41 deraadt 746: (void)cleanup(0);
1.1 deraadt 747: exit(1);
748: }
749: ep->s = *pathp;
1.41 deraadt 750:
751: sigprocmask(SIG_BLOCK, &blocksigs, &osigs);
1.1 deraadt 752: TAILQ_INSERT_TAIL(&intmpp->list, ep, q);
1.41 deraadt 753: sigprocmask(SIG_SETMASK, &osigs, NULL);
1.1 deraadt 754: }
755:
756: /*
757: * how --
758: * display how information
759: */
760: static void
1.21 deraadt 761: how(char *fname)
1.1 deraadt 762: {
763: FILE *fp;
764:
765: int lcnt, print;
766: char *p, buf[256];
767:
768: if (!(fp = fopen(fname, "r"))) {
769: warn("%s", fname);
1.41 deraadt 770: (void)cleanup(0);
1.1 deraadt 771: exit (1);
772: }
773: #define S1 "SYNOPSIS"
774: #define S2 "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS"
775: for (lcnt = print = 0; fgets(buf, sizeof(buf), fp);) {
776: if (!strncmp(buf, S1, sizeof(S1) - 1) ||
777: !strncmp(buf, S2, sizeof(S2) - 1)) {
778: print = 1;
779: continue;
1.34 otto 780: } else if (print) {
781: char *p = buf;
782: int allcaps = 0;
783:
784: while (*p) {
785: if (!allcaps && isalpha(*p))
786: allcaps = 1;
787: if (isalpha(*p) && !isupper(*p)) {
788: allcaps = 0;
789: break;
790: }
791: p++;
792: }
793: if (allcaps) {
794: (void)fclose(fp);
795: return;
796: }
1.32 jasper 797: }
1.1 deraadt 798: if (!print)
799: continue;
800: if (*buf == '\n')
801: ++lcnt;
802: else {
1.17 millert 803: while (lcnt) {
804: --lcnt;
1.1 deraadt 805: (void)putchar('\n');
1.17 millert 806: }
807: for (p = buf; isspace(*p); ++p)
808: ;
1.1 deraadt 809: (void)fputs(p, stdout);
810: }
811: }
812: (void)fclose(fp);
813: }
814:
815: /*
816: * cat --
817: * cat out the file
818: */
819: static void
1.21 deraadt 820: cat(char *fname)
1.1 deraadt 821: {
822: int fd, n;
823: char buf[2048];
824:
825: if ((fd = open(fname, O_RDONLY, 0)) < 0) {
826: warn("%s", fname);
1.41 deraadt 827: (void)cleanup(0);
1.1 deraadt 828: exit(1);
829: }
830: while ((n = read(fd, buf, sizeof(buf))) > 0)
831: if (write(STDOUT_FILENO, buf, n) != n) {
832: warn("write");
1.41 deraadt 833: (void)cleanup(0);
1.1 deraadt 834: exit (1);
835: }
836: if (n == -1) {
837: warn("read");
1.41 deraadt 838: (void)cleanup(0);
1.1 deraadt 839: exit(1);
840: }
841: (void)close(fd);
842: }
843:
844: /*
845: * check_pager --
846: * check the user supplied page information
847: */
848: static char *
1.21 deraadt 849: check_pager(char *name)
1.1 deraadt 850: {
851: char *p, *save;
852:
853: /*
854: * if the user uses "more", we make it "more -s"; watch out for
1.17 millert 855: * PAGER = "mypager /usr/bin/more"
1.1 deraadt 856: */
1.16 deraadt 857: for (p = name; *p && !isspace(*p); ++p)
858: ;
859: for (; p > name && *p != '/'; --p)
860: ;
1.1 deraadt 861: if (p != name)
862: ++p;
863:
864: /* make sure it's "more", not "morex" */
1.27 millert 865: if (!strncmp(p, "more", 4) && (p[4] == '\0' || isspace(p[4]))){
1.1 deraadt 866: save = name;
867: /* allocate space to add the "-s" */
1.31 deraadt 868: if (asprintf(&name, "%s -s", save) == -1)
869: err(1, "asprintf");
1.1 deraadt 870: }
871: return(name);
872: }
873:
874: /*
875: * jump --
876: * strip out flag argument and jump
877: */
878: static void
1.21 deraadt 879: jump(char **argv, char *flag, char *name)
1.1 deraadt 880: {
881: char **arg;
882:
883: argv[0] = name;
884: for (arg = argv + 1; *arg; ++arg)
885: if (!strcmp(*arg, flag))
886: break;
887: for (; *arg; ++arg)
888: arg[0] = arg[1];
889: execvp(name, argv);
890: (void)fprintf(stderr, "%s: Command not found.\n", name);
891: exit(1);
892: }
893:
1.17 millert 894: /*
1.1 deraadt 895: * onsig --
896: * If signaled, delete the temporary files.
897: */
898: static void
1.21 deraadt 899: onsig(int signo)
1.1 deraadt 900: {
1.41 deraadt 901: (void)cleanup(1);
1.1 deraadt 902:
903: (void)signal(signo, SIG_DFL);
904: (void)kill(getpid(), signo);
905:
906: /* NOTREACHED */
1.19 deraadt 907: _exit(1);
1.1 deraadt 908: }
909:
910: /*
911: * cleanup --
912: * Clean up temporary files, show any error messages.
913: */
914: static int
1.41 deraadt 915: cleanup(int insig)
1.1 deraadt 916: {
917: TAG *intmpp, *missp;
918: ENTRY *ep;
1.41 deraadt 919: int rval = 0;
1.1 deraadt 920:
1.41 deraadt 921: if (insig == 0) {
922: ep = (missp = getlist("_missing")) == NULL ?
923: NULL : TAILQ_FIRST(&missp->list);
924: if (ep != NULL)
925: for (; ep != NULL; ep = TAILQ_NEXT(ep, q)) {
926: if (section)
927: warnx("no entry for %s in "
928: "section %s of the manual.",
929: ep->s, section->s);
930: else
931: warnx("no entry for %s in the manual.",
932: ep->s);
933: rval = 1;
934: }
935: }
1.1 deraadt 936:
937: ep = (intmpp = getlist("_intmp")) == NULL ?
1.29 otto 938: NULL : TAILQ_FIRST(&intmpp->list);
939: for (; ep != NULL; ep = TAILQ_NEXT(ep, q))
1.1 deraadt 940: (void)unlink(ep->s);
941: return (rval);
942: }
943:
944: /*
945: * usage --
946: * print usage message and die
947: */
948: static void
1.21 deraadt 949: usage(void)
1.1 deraadt 950: {
1.8 millert 951: (void)fprintf(stderr, "usage: %s [-achw] [-C file] [-M path] [-m path] "
1.35 sobrado 952: "[-S subsection] [-s section]\n\t [section] name ...\n",
1.28 jmc 953: __progname);
1.36 jmc 954: (void)fprintf(stderr, " %s -f command ...\n", __progname);
955: (void)fprintf(stderr, " %s -k keyword ...\n", __progname);
1.1 deraadt 956: exit(1);
957: }