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]
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
37 * University Acknowledgment- Portions of this document are derived from
38 * software developed by the University of California, Berkeley, and its
43 * Find and display reference manual pages. This version includes makewhatis
44 * functionality as well.
47 #include <sys/param.h>
49 #include <sys/termios.h>
50 #include <sys/types.h>
71 /* Mapping of old directories to new directories */
72 static const struct map_entry
{
85 { "3xc", "3xcurses" },
96 * Flags that control behavior of build_manpath()
98 * BMP_ISPATH pathv is a vector constructed from PATH.
99 * Perform appropriate path translations for
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.
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.
118 struct secnode
*next
;
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
131 static struct pathmap
{
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 },
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
;
155 static int apropos
= 0;
156 static int debug
= 0;
157 static int found
= 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;
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
)
216 char *manpath
= NULL
;
217 static struct man_node
*mandirs
= NULL
;
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"
232 (void) textdomain(TEXT_DOMAIN
);
234 if (strcmp(__progname
, "apropos") == 0) {
237 } else if (strcmp(__progname
, "whatis") == 0) {
241 } else if (strcmp(__progname
, "catman") == 0) {
245 } else if (strcmp(__progname
, "makewhatis") == 0) {
251 opts
= "FM:P:T:adfklprs:tw";
252 if (argc
> 1 && strcmp(argv
[1], "-") == 0) {
259 while ((c
= getopt(argc
, argv
, opts
)) != -1) {
261 case 'M': /* Respecify path for man pages */
296 /* legacy options, compatibility only and ignored */
307 else if (makewhatishere
)
318 (void) fprintf(stderr
, gettext("%s what?\n"),
321 } else if (!printmp
&& !makewhatis
) {
322 (void) fprintf(stderr
,
323 gettext("What manual page do you want?\n"));
329 if (manpath
== NULL
&& (manpath
= getenv("MANPATH")) == NULL
) {
330 if ((manpath
= getenv("PATH")) != NULL
)
331 bmp_flags
= BMP_ISPATH
| BMP_APPEND_DEFMANDIR
;
335 pathv
= split(manpath
, ':');
336 mandirs
= build_manpath(pathv
, bmp_flags
);
341 do_makewhatis(mandirs
);
346 print_manpath(mandirs
);
350 /* Collect environment information */
351 if (isatty(STDOUT_FILENO
) && (mwstr
= getenv("MANWIDTH")) != NULL
&&
353 if (strcasecmp(mwstr
, "tty") == 0) {
356 if (ioctl(0, TIOCGWINSZ
, &ws
) != 0)
359 manwidth
= ws
.ws_col
;
361 manwidth
= (int)strtol(mwstr
, (char **)NULL
, 10);
367 DPRINTF("-- Using non-standard page width: %d\n", manwidth
);
371 if ((pager
= getenv("PAGER")) == NULL
|| *pager
== '\0')
374 DPRINTF("-- Using pager: %s\n", pager
);
376 for (i
= 0; i
< argc
; i
++) {
378 static struct man_node
*mp
;
382 * If full path to command specified, customize
383 * the manpath accordingly.
385 if ((cmd
= strrchr(argv
[i
], '/')) != NULL
) {
387 if ((pv
[0] = strdup(argv
[i
])) == NULL
)
391 mp
= build_manpath(pv
,
392 BMP_ISPATH
| BMP_FALLBACK_DEFMANDIR
);
398 whatapro(mp
, argv
[i
]);
400 ret
+= manual(mp
, argv
[i
]);
402 if (mp
!= NULL
&& mp
!= mandirs
) {
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
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
;
425 char *mandir
= DEFMANDIR
;
427 struct dupnode
*didup
= NULL
;
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
)
439 if (stat(q
[0], &sb
) != 0 || (sb
.st_mode
& S_IFDIR
) == 0) {
444 if (access(q
[0], R_OK
| X_OK
) == 0) {
446 * Some element exists. Do not append DEFMANDIR as a
449 flags
&= ~BMP_FALLBACK_DEFMANDIR
;
451 if ((currp
= (struct man_node
*)calloc(1, s
)) == NULL
)
454 currp
->frompath
= (flags
& BMP_ISPATH
);
457 lastp
= manpage
= currp
;
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) {
471 if (currp
!= manpage
)
479 * Special handling of appending DEFMANDIR. After all pathv
480 * elements have been processed, append DEFMANDIR if needed.
487 if (flags
& (BMP_APPEND_DEFMANDIR
| BMP_FALLBACK_DEFMANDIR
)) {
489 flags
&= ~BMP_ISPATH
;
499 * Store the mandir path into the manp structure.
502 getpath(struct man_node
*manp
, char **pv
)
507 while (*s
!= '\0' && *s
!= ',')
510 if ((manp
->path
= (char *)malloc(i
+ 1)) == NULL
)
512 (void) strlcpy(manp
->path
, *pv
, i
+ 1);
516 * Store the mandir's corresponding sections (submandir
517 * directories) into the manp structure.
520 getsect(struct man_node
*manp
, char **pv
)
525 /* Just store all sections when doing makewhatis or apropos/whatis */
526 if (makewhatis
|| apropos
) {
528 DPRINTF("-- Adding %s\n", manp
->path
);
532 manp
->secv
= split(mansec
, ',');
533 for (sectp
= manp
->secv
; *sectp
; 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
++)
540 if (*manp
->secv
== NULL
)
542 } else if ((sections
= check_config(*pv
)) != NULL
) {
544 DPRINTF("-- Adding %s: from %s, MANSECTS=%s\n", manp
->path
,
546 manp
->secv
= split(sections
, ',');
547 for (sectp
= manp
->secv
; *sectp
; sectp
++)
549 if (*manp
->secv
== NULL
)
553 DPRINTF("-- Adding %s: default sort order\n", manp
->path
);
560 * Get suffices of all sub-mandir directories in a mandir.
563 get_all_sect(struct man_node
*manp
)
571 int maxentries
= MAXTOKENS
;
574 if ((dp
= opendir(manp
->path
)) == 0)
581 if (manp
->secv
== NULL
) {
582 if ((manp
->secv
= malloc(maxentries
* sizeof (char *))) == NULL
)
586 for (dv
= dirv
, p
= manp
->secv
; *dv
; dv
++) {
587 if (strcmp(*dv
, CONFIG
) == 0) {
593 if ((tmp
= strdup(*dv
+ 3)) == NULL
)
596 if (prev
!= NULL
&& strcmp(prev
, tmp
) == 0) {
602 if ((prev
= strdup(*dv
+ 3)) == NULL
)
605 if ((*p
= strdup(*dv
+ 3)) == NULL
)
610 if (entries
== maxentries
) {
611 maxentries
+= MAXTOKENS
;
612 if ((manp
->secv
= realloc(manp
->secv
,
613 sizeof (char *) * maxentries
)) == NULL
)
615 p
= manp
->secv
+ entries
;
626 * Build whatis databases.
629 do_makewhatis(struct man_node
*manp
)
634 for (p
= manp
; p
!= NULL
; p
= p
->next
) {
635 ldir
= addlocale(p
->path
);
636 if (*localedir
!= '\0' && getdirs(ldir
, NULL
, 0) > 0)
644 * Count mandirs under the given manpath
647 getdirs(char *path
, char ***dirv
, int flag
)
652 int maxentries
= MAXDIRS
;
655 if ((dp
= opendir(path
)) == NULL
)
659 if ((*dirv
= malloc(sizeof (char *) *
660 maxentries
)) == NULL
)
664 while ((d
= readdir(dp
))) {
665 if (strncmp(d
->d_name
, "man", 3) != 0)
670 if ((*dv
= strdup(d
->d_name
+ 3)) == NULL
)
673 if ((dv
- *dirv
) == maxentries
) {
674 int entries
= maxentries
;
676 maxentries
+= MAXTOKENS
;
677 if ((*dirv
= realloc(*dirv
,
678 sizeof (char *) * maxentries
)) == NULL
)
680 dv
= *dirv
+ entries
;
691 * Find matching whatis or apropos entries.
694 whatapro(struct man_node
*manp
, char *word
)
696 char whatpath
[MAXPATHLEN
];
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
);
710 (void) snprintf(whatpath
, sizeof (whatpath
), "%s/%s", b
->path
,
712 search_whatis(whatpath
, word
);
717 search_whatis(char *whatpath
, char *word
)
728 if ((fp
= fopen(whatpath
, "r")) == NULL
) {
733 DPRINTF("-- Found %s: %s\n", WHATIS
, whatpath
);
735 /* Build keyword regex */
736 if (asprintf(&pkwd
, "%s%s%s", (whatis
) ? "\\<" : "",
737 word
, (whatis
) ? "\\>" : "") == -1)
740 if (regcomp(&preg
, pkwd
, REG_BASIC
| REG_ICASE
| REG_NOSUB
) != 0)
744 ss
= split(mansec
, ',');
746 while (getline(&line
, &linecap
, fp
) > 0) {
747 if (regexec(&preg
, line
, 0, NULL
, 0) == 0) {
749 /* Section-restricted search */
750 for (i
= 0; ss
[i
] != NULL
; i
++) {
751 (void) snprintf(s
, sizeof (s
), "(%s)",
753 if (strstr(line
, s
) != NULL
) {
754 (void) printf("%s", line
);
759 (void) printf("%s", line
);
772 * Split a string by specified separator.
775 split(char *s1
, char sep
)
779 int maxentries
= MAXTOKENS
;
782 if ((tokv
= vp
= malloc(maxentries
* sizeof (char *))) == NULL
)
785 for (; mp
&& *mp
; mp
= tp
) {
786 tp
= strchr(mp
, sep
);
795 if ((*vp
= (char *)malloc(sizeof (char) *
798 (void) strncpy(*vp
, mp
, len
);
803 if ((*vp
= strdup(mp
)) == NULL
)
808 if (entries
== maxentries
) {
809 maxentries
+= MAXTOKENS
;
810 if ((tokv
= realloc(tokv
,
811 maxentries
* sizeof (char *))) == NULL
)
822 * Free a vector allocated by split()
829 for (i
= 0; v
[i
] != NULL
; i
++) {
837 * Convert paths to full paths if necessary
840 fullpaths(struct man_node
**manp_head
)
845 struct man_node
*manp
= *manp_head
;
847 struct man_node
*prev
= NULL
;
849 for (b
= manp
; b
!= NULL
; b
= b
->next
) {
850 if (*(b
->path
) == '/') {
856 cwd
= getcwd(NULL
, MAXPATHLEN
);
861 /* Relative manpath with cwd: make absolute */
862 if (asprintf(&p
, "%s/%s", cwd
, b
->path
) == -1)
867 /* Relative manpath but no cwd: omit path entry */
869 prev
->next
= b
->next
;
871 *manp_head
= b
->next
;
880 * Free a man_node structure and its contents
883 free_manp(struct man_node
*manp
)
889 while ((p
!= NULL
) && (*p
!= NULL
)) {
899 * Map (in place) to lower case.
916 * Compare function for qsort().
917 * Sort first by section, then by prefix.
920 cmp(const void *arg1
, const void *arg2
)
923 char **p1
= (char **)arg1
;
924 char **p2
= (char **)arg2
;
927 if ((n
= strcmp(*p1
+ 3, *p2
+ 3)) != 0)
930 /* By prefix reversed */
931 return (strncmp(*p2
, *p1
, 3));
939 manual(struct man_node
*manp
, char *name
)
942 struct man_node
*local
;
946 char *fullname
= name
;
949 if ((slash
= strrchr(name
, '/')) != NULL
)
952 /* For each path in MANPATH */
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);
964 local
= build_manpath(ldirs
, 0);
965 DPRINTF("-- Locale specific subdir: %s\n",
967 mandir(local
->secv
, ldir
, name
, 1);
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);
986 (void) fprintf(stderr
, gettext(
987 "No manual entry for %s in section(s) %s\n"),
990 (void) fprintf(stderr
,
991 gettext("No manual entry for %s\n"), fullname
);
1001 * For a specified manual directory, read, store and sort section subdirs.
1002 * For each section specified, find and search matching subdirs.
1005 mandir(char **secv
, char *path
, char *name
, int lspec
)
1012 if ((dp
= opendir(path
)) == NULL
)
1016 DPRINTF("-- Searching mandir: %s\n", path
);
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);
1027 if (**secv
== '\\') {
1028 if (strcmp(*secv
+ 1, *dv
+ 3) != 0)
1030 } else if (strncasecmp(*secv
, *dv
+ 3, len
) != 0) {
1032 (newsection
= map_section(*secv
, path
))
1036 if (newsection
== NULL
)
1038 if (strncmp(newsection
, *dv
+ 3, len
) != 0) {
1043 if (searchdir(path
, *dv
, name
) == 0)
1052 (void) closedir(dp
);
1057 if (all
&& **dv
== 'm' && *(dv
+ 1) &&
1058 strcmp(*(dv
+ 1) + 3, *dv
+ 3) == 0)
1063 while (*pdv
!= NULL
) {
1068 (void) closedir(dp
);
1075 sortdir(DIR *dp
, char ***dirv
)
1079 int maxentries
= MAXDIRS
;
1082 if ((dv
= *dirv
= malloc(sizeof (char *) *
1083 maxentries
)) == NULL
)
1087 while ((d
= readdir(dp
))) {
1088 if (strcmp(d
->d_name
, ".") == 0 ||
1089 strcmp(d
->d_name
, "..") == 0)
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
)
1098 if (entries
== maxentries
) {
1099 maxentries
+= MAXDIRS
;
1100 if ((*dirv
= realloc(*dirv
,
1101 sizeof (char *) * maxentries
)) == NULL
)
1103 dv
= *dirv
+ entries
;
1109 qsort((void *)*dirv
, dv
- *dirv
, sizeof (char *), cmp
);
1115 * Search a section subdir for a given manpage.
1118 searchdir(char *path
, char *dir
, char *name
)
1122 char sectpath
[MAXPATHLEN
];
1123 char file
[MAXNAMLEN
];
1124 char dname
[MAXPATHLEN
];
1128 (void) snprintf(sectpath
, sizeof (sectpath
), "%s/%s", path
, dir
);
1129 (void) snprintf(file
, sizeof (file
), "%s.", name
);
1131 if ((sdp
= opendir(sectpath
)) == NULL
)
1134 while ((sd
= readdir(sdp
))) {
1137 if ((pname
= strdup(sd
->d_name
)) == NULL
)
1139 if ((last
= strrchr(pname
, '.')) != NULL
&&
1140 (strcmp(last
, ".gz") == 0 || strcmp(last
, ".bz2") == 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
);
1154 (void) closedir(sdp
);
1160 * Check the hash table of old directory names to see if there is a
1161 * new directory name.
1164 map_section(char *section
, char *path
)
1167 char fullpath
[MAXPATHLEN
];
1169 if (list
) /* -l option fall through */
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
);
1188 * Format the manpage.
1191 format(char *path
, char *dir
, char *name
, char *pg
)
1193 char manpname
[MAXPATHLEN
], catpname
[MAXPATHLEN
];
1194 char cmdbuf
[BUFSIZ
], tmpbuf
[BUFSIZ
];
1196 struct stat sbman
, sbcat
;
1201 (void) printf(gettext("%s(%s)\t-M %s\n"), name
, dir
+ 3, path
);
1205 (void) snprintf(manpname
, sizeof (manpname
), "%s/man%s/%s", path
,
1207 (void) snprintf(catpname
, sizeof (catpname
), "%s/cat%s/%s", path
,
1210 /* Can't do PS output if manpage doesn't exist */
1211 if (stat(manpname
, &sbman
) != 0 && (psoutput
|lintout
))
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
);
1222 if (fnmatch("*.gz", manpname
, 0) == 0)
1224 else if (fnmatch("*.bz2", manpname
, 0) == 0)
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
);
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
);
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
);
1255 DPRINTF("-- Using manpage: %s\n", manpname
);
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
);
1262 DPRINTF("-- Command: %s\n", cmdbuf
);
1265 return (system(cmdbuf
) == 0);
1271 * Add <localedir> to the path.
1274 addlocale(char *path
)
1278 if (asprintf(&tmp
, "%s/%s", path
, localedir
) == -1)
1285 * Get the order of sections from man.cf.
1288 check_config(char *path
)
1293 char fname
[MAXPATHLEN
];
1297 (void) snprintf(fname
, MAXPATHLEN
, "%s/%s", path
, CONFIG
);
1299 if ((fp
= fopen(fname
, "r")) == NULL
)
1302 while (getline(&line
, &linecap
, fp
) > 0) {
1303 if ((rc
= strstr(line
, "MANSECTS")) != NULL
)
1309 if (rc
== NULL
|| (sect
= strchr(line
, '=')) == NULL
)
1317 * Initialize the bintoman array with appropriate device and inode info.
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
;
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.
1340 dupcheck(struct man_node
*mnp
, struct dupnode
**dnp
)
1342 struct dupnode
*curdnp
;
1343 struct secnode
*cursnp
;
1349 /* If the path doesn't exist, treat it as a duplicate */
1350 if (stat(mnp
->path
, &sb
) != 0)
1353 /* If no sections were found in the man dir, treat it as duplicate */
1354 if (mnp
->secv
== NULL
)
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
)
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
)
1376 for (i
= 0; mnp
->secv
[i
] != NULL
; i
++) {
1377 if ((cursnp
= calloc(1, sizeof (struct secnode
)))
1380 cursnp
->next
= curdnp
->secl
;
1381 curdnp
->secl
= cursnp
;
1382 if ((cursnp
->secp
= strdup(mnp
->secv
[i
])) == NULL
)
1385 curdnp
->dev
= sb
.st_dev
;
1386 curdnp
->ino
= sb
.st_ino
;
1387 curdnp
->next
= *dnp
;
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
++) {
1398 for (cursnp
= curdnp
->secl
; cursnp
!= NULL
;
1399 cursnp
= cursnp
->next
) {
1400 if (strcmp(mnp
->secv
[i
], cursnp
->secp
) == 0) {
1406 mnp
->secv
[i
][0] = '\0';
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
)
1417 cursnp
->next
= curdnp
->secl
;
1418 curdnp
->secl
= cursnp
;
1419 if ((cursnp
->secp
= strdup(mnp
->secv
[i
])) == NULL
)
1428 * Given a bindir, return corresponding mandir.
1431 path_to_manpath(char *bindir
)
1437 /* First look for known translations for specific bin paths */
1438 if (stat(bindir
, &sb
) != 0) {
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
)
1446 if ((p
= strchr(mand
, ',')) != NULL
)
1448 if (stat(mand
, &sb
) != 0) {
1459 * No specific translation found. Try `dirname $bindir`/share/man
1460 * and `dirname $bindir`/man
1462 if ((mand
= malloc(MAXPATHLEN
)) == NULL
)
1464 if (strlcpy(mand
, bindir
, MAXPATHLEN
) >= MAXPATHLEN
) {
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
) {
1484 for (; *p
!= '\0'; p
++)
1490 if (strlcat(mand
, "/share/man", MAXPATHLEN
) >= MAXPATHLEN
) {
1495 if ((stat(mand
, &sb
) == 0) && S_ISDIR(sb
.st_mode
)) {
1500 * Strip the /share/man off and try /man
1503 if (strlcat(mand
, "/man", MAXPATHLEN
) >= MAXPATHLEN
) {
1507 if ((stat(mand
, &sb
) == 0) && S_ISDIR(sb
.st_mode
)) {
1512 * No man or share/man directory found
1519 * Free a linked list of dupnode structs.
1522 free_dupnode(struct dupnode
*dnp
) {
1523 struct dupnode
*dnp2
;
1524 struct secnode
*snp
;
1526 while (dnp
!= NULL
) {
1529 while (dnp2
->secl
!= NULL
) {
1531 dnp2
->secl
= dnp2
->secl
->next
;
1540 * Print manp linked list to stdout.
1543 print_manpath(struct man_node
*manp
)
1545 char colon
[2] = "\0\0";
1548 for (; manp
!= NULL
; manp
= manp
->next
) {
1549 (void) printf("%s%s", colon
, manp
->path
);
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
1559 if (manp
->defsrch
!= 0)
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");
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"));
1588 usage_whatapro(void)
1591 (void) fprintf(stderr
, gettext(
1592 "usage: %s [-M path] [-s section] keyword ...\n"),
1593 whatis
? "whatis" : "apropos");
1601 (void) fprintf(stderr
, gettext(
1602 "usage: catman [-M path] [-w]\n"));
1608 usage_makewhatis(void)
1610 (void) fprintf(stderr
, gettext("usage: makewhatis\n"));