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