6356 Update mdocml to 1.13.3
[unleashed.git] / usr / src / cmd / man / man.c
blobd181c6d374d7cb14d796f99746a61a82a8d5db91
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
25 * Copyright 2014 Garrett D'Amore <garrett@damore.org>
26 * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
29 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. */
30 /* All rights reserved. */
33 * University Copyright- Copyright (c) 1982, 1986, 1988
34 * The Regents of the University of California
35 * All Rights Reserved
37 * University Acknowledgment- Portions of this document are derived from
38 * software developed by the University of California, Berkeley, and its
39 * contributors.
43 * Find and display reference manual pages. This version includes makewhatis
44 * functionality as well.
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/termios.h>
50 #include <sys/types.h>
52 #include <ctype.h>
53 #include <dirent.h>
54 #include <err.h>
55 #include <errno.h>
56 #include <fcntl.h>
57 #include <fnmatch.h>
58 #include <limits.h>
59 #include <locale.h>
60 #include <malloc.h>
61 #include <memory.h>
62 #include <regex.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
68 #include "man.h"
71 /* Mapping of old directories to new directories */
72 static const struct map_entry {
73 char *old_name;
74 char *new_name;
75 } map[] = {
76 { "3b", "3ucb" },
77 { "3e", "3elf" },
78 { "3g", "3gen" },
79 { "3k", "3kstat" },
80 { "3n", "3socket" },
81 { "3r", "3rt" },
82 { "3s", "3c" },
83 { "3t", "3thr" },
84 { "3x", "3curses" },
85 { "3xc", "3xcurses" },
86 { "3xn", "3xnet" },
87 { NULL, NULL }
90 struct suffix {
91 char *ds;
92 char *fs;
96 * Flags that control behavior of build_manpath()
98 * BMP_ISPATH pathv is a vector constructed from PATH.
99 * Perform appropriate path translations for
100 * manpath.
101 * BMP_APPEND_DEFMANDIR Add DEFMANDIR to the end if it hasn't
102 * already appeared earlier.
103 * BMP_FALLBACK_DEFMANDIR Append /usr/share/man only if no other
104 * manpath (including derived from PATH)
105 * elements are valid.
107 #define BMP_ISPATH 1
108 #define BMP_APPEND_DEFMANDIR 2
109 #define BMP_FALLBACK_DEFMANDIR 4
112 * When doing equality comparisons of directories, device and inode
113 * comparisons are done. The secnode and dupnode structures are used
114 * to form a list of lists for this processing.
116 struct secnode {
117 char *secp;
118 struct secnode *next;
120 struct dupnode {
121 dev_t dev; /* from struct stat st_dev */
122 ino_t ino; /* from struct stat st_ino */
123 struct secnode *secl; /* sections already considered */
124 struct dupnode *next;
128 * Map directories that may appear in PATH to the corresponding
129 * man directory.
131 static struct pathmap {
132 char *bindir;
133 char *mandir;
134 dev_t dev;
135 ino_t ino;
136 } bintoman[] = {
137 { "/sbin", "/usr/share/man,1m", 0, 0 },
138 { "/usr/sbin", "/usr/share/man,1m", 0, 0 },
139 { "/usr/ucb", "/usr/share/man,1b", 0, 0 },
140 { "/usr/bin", "/usr/share/man,1,1m,1s,1t,1c", 0, 0 },
141 { "/usr/xpg4/bin", "/usr/share/man,1", 0, 0 },
142 { "/usr/xpg6/bin", "/usr/share/man,1", 0, 0 },
143 { NULL, NULL, 0, 0 }
146 struct man_node {
147 char *path; /* mandir path */
148 char **secv; /* submandir suffices */
149 int defsrch; /* hint for man -p */
150 int frompath; /* hint for man -d */
151 struct man_node *next;
154 static int all = 0;
155 static int apropos = 0;
156 static int debug = 0;
157 static int found = 0;
158 static int list = 0;
159 static int makewhatis = 0;
160 static int printmp = 0;
161 static int sargs = 0;
162 static int psoutput = 0;
163 static int lintout = 0;
164 static int whatis = 0;
165 static int makewhatishere = 0;
167 static char *mansec;
168 static char *pager = NULL;
170 static char *addlocale(char *);
171 static struct man_node *build_manpath(char **, int);
172 static void do_makewhatis(struct man_node *);
173 static char *check_config(char *);
174 static int cmp(const void *, const void *);
175 static int dupcheck(struct man_node *, struct dupnode **);
176 static int format(char *, char *, char *, char *);
177 static void free_dupnode(struct dupnode *);
178 static void free_manp(struct man_node *manp);
179 static void freev(char **);
180 static void fullpaths(struct man_node **);
181 static void get_all_sect(struct man_node *);
182 static int getdirs(char *, char ***, int);
183 static void getpath(struct man_node *, char **);
184 static void getsect(struct man_node *, char **);
185 static void init_bintoman(void);
186 static void lower(char *);
187 static void mandir(char **, char *, char *, int);
188 static int manual(struct man_node *, char *);
189 static char *map_section(char *, char *);
190 static char *path_to_manpath(char *);
191 static void print_manpath(struct man_node *);
192 static void search_whatis(char *, char *);
193 static int searchdir(char *, char *, char *);
194 static void sortdir(DIR *, char ***);
195 static char **split(char *, char);
196 static void usage_man(void);
197 static void usage_whatapro(void);
198 static void usage_catman(void);
199 static void usage_makewhatis(void);
200 static void whatapro(struct man_node *, char *);
202 static char language[MAXPATHLEN]; /* LC_MESSAGES */
203 static char localedir[MAXPATHLEN]; /* locale specific path component */
205 static char *newsection = NULL;
207 static int manwidth = 0;
209 extern const char *__progname;
212 main(int argc, char **argv)
214 int c, i;
215 char **pathv;
216 char *manpath = NULL;
217 static struct man_node *mandirs = NULL;
218 int bmp_flags = 0;
219 int ret = 0;
220 char *opts;
221 char *mwstr;
222 int catman = 0;
224 (void) setlocale(LC_ALL, "");
225 (void) strcpy(language, setlocale(LC_MESSAGES, (char *)NULL));
226 if (strcmp("C", language) != 0)
227 (void) strlcpy(localedir, language, MAXPATHLEN);
229 #if !defined(TEXT_DOMAIN)
230 #define TEXT_DOMAIN "SYS_TEST"
231 #endif
232 (void) textdomain(TEXT_DOMAIN);
234 if (strcmp(__progname, "apropos") == 0) {
235 apropos++;
236 opts = "M:ds:";
237 } else if (strcmp(__progname, "whatis") == 0) {
238 apropos++;
239 whatis++;
240 opts = "M:ds:";
241 } else if (strcmp(__progname, "catman") == 0) {
242 catman++;
243 makewhatis++;
244 opts = "P:M:w";
245 } else if (strcmp(__progname, "makewhatis") == 0) {
246 makewhatis++;
247 makewhatishere++;
248 manpath = ".";
249 opts = "";
250 } else {
251 opts = "FM:P:T:adfklprs:tw";
252 if (argc > 1 && strcmp(argv[1], "-") == 0) {
253 pager = "cat";
254 optind++;
258 opterr = 0;
259 while ((c = getopt(argc, argv, opts)) != -1) {
260 switch (c) {
261 case 'M': /* Respecify path for man pages */
262 manpath = optarg;
263 break;
264 case 'a':
265 all++;
266 break;
267 case 'd':
268 debug++;
269 break;
270 case 'f':
271 whatis++;
272 /*FALLTHROUGH*/
273 case 'k':
274 apropos++;
275 break;
276 case 'l':
277 list++;
278 all++;
279 break;
280 case 'p':
281 printmp++;
282 break;
283 case 's':
284 mansec = optarg;
285 sargs++;
286 break;
287 case 'r':
288 lintout++;
289 break;
290 case 't':
291 psoutput++;
292 break;
293 case 'T':
294 case 'P':
295 case 'F':
296 /* legacy options, compatibility only and ignored */
297 break;
298 case 'w':
299 makewhatis++;
300 break;
301 case '?':
302 default:
303 if (apropos)
304 usage_whatapro();
305 else if (catman)
306 usage_catman();
307 else if (makewhatishere)
308 usage_makewhatis();
309 else
310 usage_man();
313 argc -= optind;
314 argv += optind;
316 if (argc == 0) {
317 if (apropos) {
318 (void) fprintf(stderr, gettext("%s what?\n"),
319 __progname);
320 exit(1);
321 } else if (!printmp && !makewhatis) {
322 (void) fprintf(stderr,
323 gettext("What manual page do you want?\n"));
324 exit(1);
328 init_bintoman();
329 if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) {
330 if ((manpath = getenv("PATH")) != NULL)
331 bmp_flags = BMP_ISPATH | BMP_APPEND_DEFMANDIR;
332 else
333 manpath = DEFMANDIR;
335 pathv = split(manpath, ':');
336 mandirs = build_manpath(pathv, bmp_flags);
337 freev(pathv);
338 fullpaths(&mandirs);
340 if (makewhatis) {
341 do_makewhatis(mandirs);
342 exit(0);
345 if (printmp) {
346 print_manpath(mandirs);
347 exit(0);
350 /* Collect environment information */
351 if (isatty(STDOUT_FILENO) && (mwstr = getenv("MANWIDTH")) != NULL &&
352 *mwstr != '\0') {
353 if (strcasecmp(mwstr, "tty") == 0) {
354 struct winsize ws;
356 if (ioctl(0, TIOCGWINSZ, &ws) != 0)
357 warn("TIOCGWINSZ");
358 else
359 manwidth = ws.ws_col;
360 } else {
361 manwidth = (int)strtol(mwstr, (char **)NULL, 10);
362 if (manwidth < 0)
363 manwidth = 0;
366 if (manwidth != 0) {
367 DPRINTF("-- Using non-standard page width: %d\n", manwidth);
370 if (pager == NULL) {
371 if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
372 pager = PAGER;
374 DPRINTF("-- Using pager: %s\n", pager);
376 for (i = 0; i < argc; i++) {
377 char *cmd;
378 static struct man_node *mp;
379 char *pv[2];
382 * If full path to command specified, customize
383 * the manpath accordingly.
385 if ((cmd = strrchr(argv[i], '/')) != NULL) {
386 *cmd = '\0';
387 if ((pv[0] = strdup(argv[i])) == NULL)
388 err(1, "strdup");
389 pv[1] = NULL;
390 *cmd = '/';
391 mp = build_manpath(pv,
392 BMP_ISPATH | BMP_FALLBACK_DEFMANDIR);
393 } else {
394 mp = mandirs;
397 if (apropos)
398 whatapro(mp, argv[i]);
399 else
400 ret += manual(mp, argv[i]);
402 if (mp != NULL && mp != mandirs) {
403 free(pv[0]);
404 free_manp(mp);
408 return (ret == 0 ? 0 : 1);
412 * This routine builds the manpage structure from MANPATH or PATH,
413 * depending on flags. See BMP_* definitions above for valid
414 * flags.
416 static struct man_node *
417 build_manpath(char **pathv, int flags)
419 struct man_node *manpage = NULL;
420 struct man_node *currp = NULL;
421 struct man_node *lastp = NULL;
422 char **p;
423 char **q;
424 char *mand = NULL;
425 char *mandir = DEFMANDIR;
426 int s;
427 struct dupnode *didup = NULL;
428 struct stat sb;
430 s = sizeof (struct man_node);
431 for (p = pathv; *p != NULL; ) {
432 if (flags & BMP_ISPATH) {
433 if ((mand = path_to_manpath(*p)) == NULL)
434 goto next;
435 free(*p);
436 *p = mand;
438 q = split(*p, ',');
439 if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) {
440 freev(q);
441 goto next;
444 if (access(q[0], R_OK | X_OK) == 0) {
446 * Some element exists. Do not append DEFMANDIR as a
447 * fallback.
449 flags &= ~BMP_FALLBACK_DEFMANDIR;
451 if ((currp = (struct man_node *)calloc(1, s)) == NULL)
452 err(1, "calloc");
454 currp->frompath = (flags & BMP_ISPATH);
456 if (manpage == NULL)
457 lastp = manpage = currp;
459 getpath(currp, p);
460 getsect(currp, p);
463 * If there are no new elements in this path,
464 * do not add it to the manpage list.
466 if (dupcheck(currp, &didup) != 0) {
467 freev(currp->secv);
468 free(currp);
469 } else {
470 currp->next = NULL;
471 if (currp != manpage)
472 lastp->next = currp;
473 lastp = currp;
476 freev(q);
477 next:
479 * Special handling of appending DEFMANDIR. After all pathv
480 * elements have been processed, append DEFMANDIR if needed.
482 if (p == &mandir)
483 break;
484 p++;
485 if (*p != NULL)
486 continue;
487 if (flags & (BMP_APPEND_DEFMANDIR | BMP_FALLBACK_DEFMANDIR)) {
488 p = &mandir;
489 flags &= ~BMP_ISPATH;
493 free_dupnode(didup);
495 return (manpage);
499 * Store the mandir path into the manp structure.
501 static void
502 getpath(struct man_node *manp, char **pv)
504 char *s = *pv;
505 int i = 0;
507 while (*s != '\0' && *s != ',')
508 i++, s++;
510 if ((manp->path = (char *)malloc(i + 1)) == NULL)
511 err(1, "malloc");
512 (void) strlcpy(manp->path, *pv, i + 1);
516 * Store the mandir's corresponding sections (submandir
517 * directories) into the manp structure.
519 static void
520 getsect(struct man_node *manp, char **pv)
522 char *sections;
523 char **sectp;
525 /* Just store all sections when doing makewhatis or apropos/whatis */
526 if (makewhatis || apropos) {
527 manp->defsrch = 1;
528 DPRINTF("-- Adding %s\n", manp->path);
529 manp->secv = NULL;
530 get_all_sect(manp);
531 } else if (sargs) {
532 manp->secv = split(mansec, ',');
533 for (sectp = manp->secv; *sectp; sectp++)
534 lower(*sectp);
535 } else if ((sections = strchr(*pv, ',')) != NULL) {
536 DPRINTF("-- Adding %s: MANSECTS=%s\n", manp->path, sections);
537 manp->secv = split(++sections, ',');
538 for (sectp = manp->secv; *sectp; sectp++)
539 lower(*sectp);
540 if (*manp->secv == NULL)
541 get_all_sect(manp);
542 } else if ((sections = check_config(*pv)) != NULL) {
543 manp->defsrch = 1;
544 DPRINTF("-- Adding %s: from %s, MANSECTS=%s\n", manp->path,
545 CONFIG, sections);
546 manp->secv = split(sections, ',');
547 for (sectp = manp->secv; *sectp; sectp++)
548 lower(*sectp);
549 if (*manp->secv == NULL)
550 get_all_sect(manp);
551 } else {
552 manp->defsrch = 1;
553 DPRINTF("-- Adding %s: default sort order\n", manp->path);
554 manp->secv = NULL;
555 get_all_sect(manp);
560 * Get suffices of all sub-mandir directories in a mandir.
562 static void
563 get_all_sect(struct man_node *manp)
565 DIR *dp;
566 char **dirv;
567 char **dv;
568 char **p;
569 char *prev = NULL;
570 char *tmp = NULL;
571 int maxentries = MAXTOKENS;
572 int entries = 0;
574 if ((dp = opendir(manp->path)) == 0)
575 return;
577 sortdir(dp, &dirv);
579 (void) closedir(dp);
581 if (manp->secv == NULL) {
582 if ((manp->secv = malloc(maxentries * sizeof (char *))) == NULL)
583 err(1, "malloc");
586 for (dv = dirv, p = manp->secv; *dv; dv++) {
587 if (strcmp(*dv, CONFIG) == 0) {
588 free(*dv);
589 continue;
592 free(tmp);
593 if ((tmp = strdup(*dv + 3)) == NULL)
594 err(1, "strdup");
596 if (prev != NULL && strcmp(prev, tmp) == 0) {
597 free(*dv);
598 continue;
601 free(prev);
602 if ((prev = strdup(*dv + 3)) == NULL)
603 err(1, "strdup");
605 if ((*p = strdup(*dv + 3)) == NULL)
606 err(1, "strdup");
608 p++; entries++;
610 if (entries == maxentries) {
611 maxentries += MAXTOKENS;
612 if ((manp->secv = realloc(manp->secv,
613 sizeof (char *) * maxentries)) == NULL)
614 err(1, "realloc");
615 p = manp->secv + entries;
617 free(*dv);
619 free(tmp);
620 free(prev);
621 *p = NULL;
622 free(dirv);
626 * Build whatis databases.
628 static void
629 do_makewhatis(struct man_node *manp)
631 struct man_node *p;
632 char *ldir;
634 for (p = manp; p != NULL; p = p->next) {
635 ldir = addlocale(p->path);
636 if (*localedir != '\0' && getdirs(ldir, NULL, 0) > 0)
637 mwpath(ldir);
638 free(ldir);
639 mwpath(p->path);
644 * Count mandirs under the given manpath
646 static int
647 getdirs(char *path, char ***dirv, int flag)
649 DIR *dp;
650 struct dirent *d;
651 int n = 0;
652 int maxentries = MAXDIRS;
653 char **dv = NULL;
655 if ((dp = opendir(path)) == NULL)
656 return (0);
658 if (flag) {
659 if ((*dirv = malloc(sizeof (char *) *
660 maxentries)) == NULL)
661 err(1, "malloc");
662 dv = *dirv;
664 while ((d = readdir(dp))) {
665 if (strncmp(d->d_name, "man", 3) != 0)
666 continue;
667 n++;
669 if (flag) {
670 if ((*dv = strdup(d->d_name + 3)) == NULL)
671 err(1, "strdup");
672 dv++;
673 if ((dv - *dirv) == maxentries) {
674 int entries = maxentries;
676 maxentries += MAXTOKENS;
677 if ((*dirv = realloc(*dirv,
678 sizeof (char *) * maxentries)) == NULL)
679 err(1, "realloc");
680 dv = *dirv + entries;
685 (void) closedir(dp);
686 return (n);
691 * Find matching whatis or apropos entries.
693 static void
694 whatapro(struct man_node *manp, char *word)
696 char whatpath[MAXPATHLEN];
697 struct man_node *b;
698 char *ldir;
700 for (b = manp; b != NULL; b = b->next) {
701 if (*localedir != '\0') {
702 ldir = addlocale(b->path);
703 if (getdirs(ldir, NULL, 0) != 0) {
704 (void) snprintf(whatpath, sizeof (whatpath),
705 "%s/%s", ldir, WHATIS);
706 search_whatis(whatpath, word);
708 free(ldir);
710 (void) snprintf(whatpath, sizeof (whatpath), "%s/%s", b->path,
711 WHATIS);
712 search_whatis(whatpath, word);
716 static void
717 search_whatis(char *whatpath, char *word)
719 FILE *fp;
720 char *line = NULL;
721 size_t linecap = 0;
722 char *pkwd;
723 regex_t preg;
724 char **ss = NULL;
725 char s[MAXNAMELEN];
726 int i;
728 if ((fp = fopen(whatpath, "r")) == NULL) {
729 perror(whatpath);
730 return;
733 DPRINTF("-- Found %s: %s\n", WHATIS, whatpath);
735 /* Build keyword regex */
736 if (asprintf(&pkwd, "%s%s%s", (whatis) ? "\\<" : "",
737 word, (whatis) ? "\\>" : "") == -1)
738 err(1, "asprintf");
740 if (regcomp(&preg, pkwd, REG_BASIC | REG_ICASE | REG_NOSUB) != 0)
741 err(1, "regcomp");
743 if (sargs)
744 ss = split(mansec, ',');
746 while (getline(&line, &linecap, fp) > 0) {
747 if (regexec(&preg, line, 0, NULL, 0) == 0) {
748 if (sargs) {
749 /* Section-restricted search */
750 for (i = 0; ss[i] != NULL; i++) {
751 (void) snprintf(s, sizeof (s), "(%s)",
752 ss[i]);
753 if (strstr(line, s) != NULL) {
754 (void) printf("%s", line);
755 break;
758 } else {
759 (void) printf("%s", line);
764 if (ss != NULL)
765 freev(ss);
766 free(pkwd);
767 (void) fclose(fp);
772 * Split a string by specified separator.
774 static char **
775 split(char *s1, char sep)
777 char **tokv, **vp;
778 char *mp = s1, *tp;
779 int maxentries = MAXTOKENS;
780 int entries = 0;
782 if ((tokv = vp = malloc(maxentries * sizeof (char *))) == NULL)
783 err(1, "malloc");
785 for (; mp && *mp; mp = tp) {
786 tp = strchr(mp, sep);
787 if (mp == tp) {
788 tp++;
789 continue;
791 if (tp) {
792 size_t len;
794 len = tp - mp;
795 if ((*vp = (char *)malloc(sizeof (char) *
796 len + 1)) == NULL)
797 err(1, "malloc");
798 (void) strncpy(*vp, mp, len);
799 *(*vp + len) = '\0';
800 tp++;
801 vp++;
802 } else {
803 if ((*vp = strdup(mp)) == NULL)
804 err(1, "strdup");
805 vp++;
807 entries++;
808 if (entries == maxentries) {
809 maxentries += MAXTOKENS;
810 if ((tokv = realloc(tokv,
811 maxentries * sizeof (char *))) == NULL)
812 err(1, "realloc");
813 vp = tokv + entries;
816 *vp = 0;
818 return (tokv);
822 * Free a vector allocated by split()
824 static void
825 freev(char **v)
827 int i;
828 if (v != NULL) {
829 for (i = 0; v[i] != NULL; i++) {
830 free(v[i]);
832 free(v);
837 * Convert paths to full paths if necessary
839 static void
840 fullpaths(struct man_node **manp_head)
842 char *cwd = NULL;
843 char *p;
844 int cwd_gotten = 0;
845 struct man_node *manp = *manp_head;
846 struct man_node *b;
847 struct man_node *prev = NULL;
849 for (b = manp; b != NULL; b = b->next) {
850 if (*(b->path) == '/') {
851 prev = b;
852 continue;
855 if (!cwd_gotten) {
856 cwd = getcwd(NULL, MAXPATHLEN);
857 cwd_gotten = 1;
860 if (cwd) {
861 /* Relative manpath with cwd: make absolute */
862 if (asprintf(&p, "%s/%s", cwd, b->path) == -1)
863 err(1, "asprintf");
864 free(b->path);
865 b->path = p;
866 } else {
867 /* Relative manpath but no cwd: omit path entry */
868 if (prev)
869 prev->next = b->next;
870 else
871 *manp_head = b->next;
873 free_manp(b);
876 free(cwd);
880 * Free a man_node structure and its contents
882 static void
883 free_manp(struct man_node *manp)
885 char **p;
887 free(manp->path);
888 p = manp->secv;
889 while ((p != NULL) && (*p != NULL)) {
890 free(*p);
891 p++;
893 free(manp->secv);
894 free(manp);
899 * Map (in place) to lower case.
901 static void
902 lower(char *s)
905 if (s == 0)
906 return;
907 while (*s) {
908 if (isupper(*s))
909 *s = tolower(*s);
910 s++;
916 * Compare function for qsort().
917 * Sort first by section, then by prefix.
919 static int
920 cmp(const void *arg1, const void *arg2)
922 int n;
923 char **p1 = (char **)arg1;
924 char **p2 = (char **)arg2;
926 /* By section */
927 if ((n = strcmp(*p1 + 3, *p2 + 3)) != 0)
928 return (n);
930 /* By prefix reversed */
931 return (strncmp(*p2, *p1, 3));
936 * Find a manpage.
938 static int
939 manual(struct man_node *manp, char *name)
941 struct man_node *p;
942 struct man_node *local;
943 int ndirs = 0;
944 char *ldir;
945 char *ldirs[2];
946 char *fullname = name;
947 char *slash;
949 if ((slash = strrchr(name, '/')) != NULL)
950 name = slash + 1;
952 /* For each path in MANPATH */
953 found = 0;
955 for (p = manp; p != NULL; p = p->next) {
956 DPRINTF("-- Searching mandir: %s\n", p->path);
958 if (*localedir != '\0') {
959 ldir = addlocale(p->path);
960 ndirs = getdirs(ldir, NULL, 0);
961 if (ndirs != 0) {
962 ldirs[0] = ldir;
963 ldirs[1] = NULL;
964 local = build_manpath(ldirs, 0);
965 DPRINTF("-- Locale specific subdir: %s\n",
966 ldir);
967 mandir(local->secv, ldir, name, 1);
968 free_manp(local);
970 free(ldir);
974 * Locale mandir not valid, man page in locale
975 * mandir not found, or -a option present
977 if (ndirs == 0 || !found || all)
978 mandir(p->secv, p->path, name, 0);
980 if (found && !all)
981 break;
984 if (!found) {
985 if (sargs) {
986 (void) fprintf(stderr, gettext(
987 "No manual entry for %s in section(s) %s\n"),
988 fullname, mansec);
989 } else {
990 (void) fprintf(stderr,
991 gettext("No manual entry for %s\n"), fullname);
996 return (!found);
1001 * For a specified manual directory, read, store and sort section subdirs.
1002 * For each section specified, find and search matching subdirs.
1004 static void
1005 mandir(char **secv, char *path, char *name, int lspec)
1007 DIR *dp;
1008 char **dirv;
1009 char **dv, **pdv;
1010 int len, dslen;
1012 if ((dp = opendir(path)) == NULL)
1013 return;
1015 if (lspec)
1016 DPRINTF("-- Searching mandir: %s\n", path);
1018 sortdir(dp, &dirv);
1020 /* Search in the order specified by MANSECTS */
1021 for (; *secv; secv++) {
1022 len = strlen(*secv);
1023 for (dv = dirv; *dv; dv++) {
1024 dslen = strlen(*dv + 3);
1025 if (dslen > len)
1026 len = dslen;
1027 if (**secv == '\\') {
1028 if (strcmp(*secv + 1, *dv + 3) != 0)
1029 continue;
1030 } else if (strncasecmp(*secv, *dv + 3, len) != 0) {
1031 if (!all &&
1032 (newsection = map_section(*secv, path))
1033 == NULL) {
1034 continue;
1036 if (newsection == NULL)
1037 newsection = "";
1038 if (strncmp(newsection, *dv + 3, len) != 0) {
1039 continue;
1043 if (searchdir(path, *dv, name) == 0)
1044 continue;
1046 if (!all) {
1047 pdv = dirv;
1048 while (*pdv) {
1049 free(*pdv);
1050 pdv++;
1052 (void) closedir(dp);
1053 free(dirv);
1054 return;
1057 if (all && **dv == 'm' && *(dv + 1) &&
1058 strcmp(*(dv + 1) + 3, *dv + 3) == 0)
1059 dv++;
1062 pdv = dirv;
1063 while (*pdv != NULL) {
1064 free(*pdv);
1065 pdv++;
1067 free(dirv);
1068 (void) closedir(dp);
1072 * Sort directories.
1074 static void
1075 sortdir(DIR *dp, char ***dirv)
1077 struct dirent *d;
1078 char **dv;
1079 int maxentries = MAXDIRS;
1080 int entries = 0;
1082 if ((dv = *dirv = malloc(sizeof (char *) *
1083 maxentries)) == NULL)
1084 err(1, "malloc");
1085 dv = *dirv;
1087 while ((d = readdir(dp))) {
1088 if (strcmp(d->d_name, ".") == 0 ||
1089 strcmp(d->d_name, "..") == 0)
1090 continue;
1092 if (strncmp(d->d_name, "man", 3) == 0 ||
1093 strncmp(d->d_name, "cat", 3) == 0) {
1094 if ((*dv = strdup(d->d_name)) == NULL)
1095 err(1, "strdup");
1096 dv++;
1097 entries++;
1098 if (entries == maxentries) {
1099 maxentries += MAXDIRS;
1100 if ((*dirv = realloc(*dirv,
1101 sizeof (char *) * maxentries)) == NULL)
1102 err(1, "realloc");
1103 dv = *dirv + entries;
1107 *dv = 0;
1109 qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
1115 * Search a section subdir for a given manpage.
1117 static int
1118 searchdir(char *path, char *dir, char *name)
1120 DIR *sdp;
1121 struct dirent *sd;
1122 char sectpath[MAXPATHLEN];
1123 char file[MAXNAMLEN];
1124 char dname[MAXPATHLEN];
1125 char *last;
1126 int nlen;
1128 (void) snprintf(sectpath, sizeof (sectpath), "%s/%s", path, dir);
1129 (void) snprintf(file, sizeof (file), "%s.", name);
1131 if ((sdp = opendir(sectpath)) == NULL)
1132 return (0);
1134 while ((sd = readdir(sdp))) {
1135 char *pname;
1137 if ((pname = strdup(sd->d_name)) == NULL)
1138 err(1, "strdup");
1139 if ((last = strrchr(pname, '.')) != NULL &&
1140 (strcmp(last, ".gz") == 0 || strcmp(last, ".bz2") == 0))
1141 *last = '\0';
1142 last = strrchr(pname, '.');
1143 nlen = last - pname;
1144 (void) snprintf(dname, sizeof (dname), "%.*s.", nlen, pname);
1145 if (strcmp(dname, file) == 0 ||
1146 strcmp(pname, name) == 0) {
1147 (void) format(path, dir, name, sd->d_name);
1148 (void) closedir(sdp);
1149 free(pname);
1150 return (1);
1152 free(pname);
1154 (void) closedir(sdp);
1156 return (0);
1160 * Check the hash table of old directory names to see if there is a
1161 * new directory name.
1163 static char *
1164 map_section(char *section, char *path)
1166 int i;
1167 char fullpath[MAXPATHLEN];
1169 if (list) /* -l option fall through */
1170 return (NULL);
1172 for (i = 0; map[i].new_name != NULL; i++) {
1173 if (strcmp(section, map[i].old_name) == 0) {
1174 (void) snprintf(fullpath, sizeof (fullpath),
1175 "%s/man%s", path, map[i].new_name);
1176 if (!access(fullpath, R_OK | X_OK)) {
1177 return (map[i].new_name);
1178 } else {
1179 return (NULL);
1184 return (NULL);
1188 * Format the manpage.
1190 static int
1191 format(char *path, char *dir, char *name, char *pg)
1193 char manpname[MAXPATHLEN], catpname[MAXPATHLEN];
1194 char cmdbuf[BUFSIZ], tmpbuf[BUFSIZ];
1195 char *cattool;
1196 struct stat sbman, sbcat;
1198 found++;
1200 if (list) {
1201 (void) printf(gettext("%s(%s)\t-M %s\n"), name, dir + 3, path);
1202 return (-1);
1205 (void) snprintf(manpname, sizeof (manpname), "%s/man%s/%s", path,
1206 dir + 3, pg);
1207 (void) snprintf(catpname, sizeof (catpname), "%s/cat%s/%s", path,
1208 dir + 3, pg);
1210 /* Can't do PS output if manpage doesn't exist */
1211 if (stat(manpname, &sbman) != 0 && (psoutput|lintout))
1212 return (-1);
1215 * If both manpage and catpage do not exist, manpname is
1216 * broken symlink, most likely.
1218 if (stat(catpname, &sbcat) != 0 && stat(manpname, &sbman) != 0)
1219 err(1, "%s", manpname);
1221 /* Setup cattool */
1222 if (fnmatch("*.gz", manpname, 0) == 0)
1223 cattool = "gzcat";
1224 else if (fnmatch("*.bz2", manpname, 0) == 0)
1225 cattool = "bzcat";
1226 else
1227 cattool = "cat";
1229 if (psoutput) {
1230 (void) snprintf(cmdbuf, BUFSIZ,
1231 "cd %s; %s %s | mandoc -Tps | lp -Tpostscript",
1232 path, cattool, manpname);
1233 DPRINTF("-- Using manpage: %s\n", manpname);
1234 goto cmd;
1235 } else if (lintout) {
1236 (void) snprintf(cmdbuf, BUFSIZ,
1237 "cd %s; %s %s | mandoc -Tlint",
1238 path, cattool, manpname);
1239 DPRINTF("-- Linting manpage: %s\n", manpname);
1240 goto cmd;
1244 * Output catpage if:
1245 * - manpage doesn't exist
1246 * - output width is standard and catpage is recent enough
1248 if (stat(manpname, &sbman) != 0 || (manwidth == 0 &&
1249 stat(catpname, &sbcat) == 0 && sbcat.st_mtime >= sbman.st_mtime)) {
1250 DPRINTF("-- Using catpage: %s\n", catpname);
1251 (void) snprintf(cmdbuf, BUFSIZ, "%s %s", pager, catpname);
1252 goto cmd;
1255 DPRINTF("-- Using manpage: %s\n", manpname);
1256 if (manwidth > 0)
1257 (void) snprintf(tmpbuf, BUFSIZ, "-Owidth=%d ", manwidth);
1258 (void) snprintf(cmdbuf, BUFSIZ, "cd %s; %s %s | mandoc %s| %s",
1259 path, cattool, manpname, (manwidth > 0) ? tmpbuf : "", pager);
1261 cmd:
1262 DPRINTF("-- Command: %s\n", cmdbuf);
1264 if (!debug)
1265 return (system(cmdbuf) == 0);
1266 else
1267 return (0);
1271 * Add <localedir> to the path.
1273 static char *
1274 addlocale(char *path)
1276 char *tmp;
1278 if (asprintf(&tmp, "%s/%s", path, localedir) == -1)
1279 err(1, "asprintf");
1281 return (tmp);
1285 * Get the order of sections from man.cf.
1287 static char *
1288 check_config(char *path)
1290 FILE *fp;
1291 char *rc = NULL;
1292 char *sect;
1293 char fname[MAXPATHLEN];
1294 char *line = NULL;
1295 size_t linecap = 0;
1297 (void) snprintf(fname, MAXPATHLEN, "%s/%s", path, CONFIG);
1299 if ((fp = fopen(fname, "r")) == NULL)
1300 return (NULL);
1302 while (getline(&line, &linecap, fp) > 0) {
1303 if ((rc = strstr(line, "MANSECTS")) != NULL)
1304 break;
1307 (void) fclose(fp);
1309 if (rc == NULL || (sect = strchr(line, '=')) == NULL)
1310 return (NULL);
1311 else
1312 return (++sect);
1317 * Initialize the bintoman array with appropriate device and inode info.
1319 static void
1320 init_bintoman(void)
1322 int i;
1323 struct stat sb;
1325 for (i = 0; bintoman[i].bindir != NULL; i++) {
1326 if (stat(bintoman[i].bindir, &sb) == 0) {
1327 bintoman[i].dev = sb.st_dev;
1328 bintoman[i].ino = sb.st_ino;
1329 } else {
1330 bintoman[i].dev = NODEV;
1336 * If a duplicate is found, return 1.
1337 * If a duplicate is not found, add it to the dupnode list and return 0.
1339 static int
1340 dupcheck(struct man_node *mnp, struct dupnode **dnp)
1342 struct dupnode *curdnp;
1343 struct secnode *cursnp;
1344 struct stat sb;
1345 int i;
1346 int rv = 1;
1347 int dupfound;
1349 /* If the path doesn't exist, treat it as a duplicate */
1350 if (stat(mnp->path, &sb) != 0)
1351 return (1);
1353 /* If no sections were found in the man dir, treat it as duplicate */
1354 if (mnp->secv == NULL)
1355 return (1);
1358 * Find the dupnode structure for the previous time this directory
1359 * was looked at. Device and inode numbers are compared so that
1360 * directories that are reached via different paths (e.g. /usr/man and
1361 * /usr/share/man) are treated as equivalent.
1363 for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) {
1364 if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino)
1365 break;
1369 * First time this directory has been seen. Add a new node to the
1370 * head of the list. Since all entries are guaranteed to be unique
1371 * copy all sections to new node.
1373 if (curdnp == NULL) {
1374 if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL)
1375 err(1, "calloc");
1376 for (i = 0; mnp->secv[i] != NULL; i++) {
1377 if ((cursnp = calloc(1, sizeof (struct secnode)))
1378 == NULL)
1379 err(1, "calloc");
1380 cursnp->next = curdnp->secl;
1381 curdnp->secl = cursnp;
1382 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1383 err(1, "strdup");
1385 curdnp->dev = sb.st_dev;
1386 curdnp->ino = sb.st_ino;
1387 curdnp->next = *dnp;
1388 *dnp = curdnp;
1389 return (0);
1393 * Traverse the section vector in the man_node and the section list
1394 * in dupnode cache to eliminate all duplicates from man_node.
1396 for (i = 0; mnp->secv[i] != NULL; i++) {
1397 dupfound = 0;
1398 for (cursnp = curdnp->secl; cursnp != NULL;
1399 cursnp = cursnp->next) {
1400 if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
1401 dupfound = 1;
1402 break;
1405 if (dupfound) {
1406 mnp->secv[i][0] = '\0';
1407 continue;
1412 * Update curdnp and set return value to indicate that this
1413 * was not all duplicates.
1415 if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL)
1416 err(1, "calloc");
1417 cursnp->next = curdnp->secl;
1418 curdnp->secl = cursnp;
1419 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1420 err(1, "strdup");
1421 rv = 0;
1424 return (rv);
1428 * Given a bindir, return corresponding mandir.
1430 static char *
1431 path_to_manpath(char *bindir)
1433 char *mand, *p;
1434 int i;
1435 struct stat sb;
1437 /* First look for known translations for specific bin paths */
1438 if (stat(bindir, &sb) != 0) {
1439 return (NULL);
1441 for (i = 0; bintoman[i].bindir != NULL; i++) {
1442 if (sb.st_dev == bintoman[i].dev &&
1443 sb.st_ino == bintoman[i].ino) {
1444 if ((mand = strdup(bintoman[i].mandir)) == NULL)
1445 err(1, "strdup");
1446 if ((p = strchr(mand, ',')) != NULL)
1447 *p = '\0';
1448 if (stat(mand, &sb) != 0) {
1449 free(mand);
1450 return (NULL);
1452 if (p != NULL)
1453 *p = ',';
1454 return (mand);
1459 * No specific translation found. Try `dirname $bindir`/share/man
1460 * and `dirname $bindir`/man
1462 if ((mand = malloc(MAXPATHLEN)) == NULL)
1463 err(1, "malloc");
1464 if (strlcpy(mand, bindir, MAXPATHLEN) >= MAXPATHLEN) {
1465 free(mand);
1466 return (NULL);
1470 * Advance to end of buffer, strip trailing /'s then remove last
1471 * directory component.
1473 for (p = mand; *p != '\0'; p++)
1475 for (; p > mand && *p == '/'; p--)
1477 for (; p > mand && *p != '/'; p--)
1479 if (p == mand && *p == '.') {
1480 if (realpath("..", mand) == NULL) {
1481 free(mand);
1482 return (NULL);
1484 for (; *p != '\0'; p++)
1486 } else {
1487 *p = '\0';
1490 if (strlcat(mand, "/share/man", MAXPATHLEN) >= MAXPATHLEN) {
1491 free(mand);
1492 return (NULL);
1495 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1496 return (mand);
1500 * Strip the /share/man off and try /man
1502 *p = '\0';
1503 if (strlcat(mand, "/man", MAXPATHLEN) >= MAXPATHLEN) {
1504 free(mand);
1505 return (NULL);
1507 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1508 return (mand);
1512 * No man or share/man directory found
1514 free(mand);
1515 return (NULL);
1519 * Free a linked list of dupnode structs.
1521 void
1522 free_dupnode(struct dupnode *dnp) {
1523 struct dupnode *dnp2;
1524 struct secnode *snp;
1526 while (dnp != NULL) {
1527 dnp2 = dnp;
1528 dnp = dnp->next;
1529 while (dnp2->secl != NULL) {
1530 snp = dnp2->secl;
1531 dnp2->secl = dnp2->secl->next;
1532 free(snp->secp);
1533 free(snp);
1535 free(dnp2);
1540 * Print manp linked list to stdout.
1542 void
1543 print_manpath(struct man_node *manp)
1545 char colon[2] = "\0\0";
1546 char **secp;
1548 for (; manp != NULL; manp = manp->next) {
1549 (void) printf("%s%s", colon, manp->path);
1550 colon[0] = ':';
1553 * If man.cf or a directory scan was used to create section
1554 * list, do not print section list again. If the output of
1555 * man -p is used to set MANPATH, subsequent runs of man
1556 * will re-read man.cf and/or scan man directories as
1557 * required.
1559 if (manp->defsrch != 0)
1560 continue;
1562 for (secp = manp->secv; *secp != NULL; secp++) {
1564 * Section deduplication may have eliminated some
1565 * sections from the vector. Avoid displaying this
1566 * detail which would appear as ",," in output
1568 if ((*secp)[0] != '\0')
1569 (void) printf(",%s", *secp);
1572 (void) printf("\n");
1575 static void
1576 usage_man(void)
1579 (void) fprintf(stderr, gettext(
1580 "usage: man [-alptw] [-M path] [-s section] name ...\n"
1581 " man [-M path] [-s section] -k keyword ...\n"
1582 " man [-M path] [-s section] -f keyword ...\n"));
1584 exit(1);
1587 static void
1588 usage_whatapro(void)
1591 (void) fprintf(stderr, gettext(
1592 "usage: %s [-M path] [-s section] keyword ...\n"),
1593 whatis ? "whatis" : "apropos");
1595 exit(1);
1598 static void
1599 usage_catman(void)
1601 (void) fprintf(stderr, gettext(
1602 "usage: catman [-M path] [-w]\n"));
1604 exit(1);
1607 static void
1608 usage_makewhatis(void)
1610 (void) fprintf(stderr, gettext("usage: makewhatis\n"));
1612 exit(1);