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