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]
22 * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. */
27 /* All rights reserved. */
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
42 * links to apropos, whatis, and catman
43 * This version uses more for underlining and paging.
49 #include <sys/param.h>
50 #include <sys/types.h>
65 #define MACROF "tmac.an" /* name of <locale> macro file */
66 #define TMAC_AN "-man" /* default macro file */
69 * The default search path for man subtrees.
72 #define MANDIR "/usr/share/man" /* default mandir */
73 #define MAKEWHATIS "/usr/lib/makewhatis"
74 #define WHATIS "windex"
75 #define TEMPLATE "/tmp/mpXXXXXX"
76 #define CONFIG "man.cf"
79 * Names for formatting and display programs. The values given
80 * below are reasonable defaults, but sites with source may
81 * wish to modify them to match the local environment. The
82 * value for TCAT is particularly problematic as there's no
83 * accepted standard value available for it. (The definition
84 * below assumes C.A.T. troff output and prints it).
87 #define MORE "more -s" /* default paging filter */
88 #define CAT_S "/usr/bin/cat -s" /* for '-' opt (no more) */
89 #define CAT_ "/usr/bin/cat" /* for when output is not a tty */
90 #define TROFF "troff" /* local name for troff */
91 #define TCAT "lp -c -T troff" /* command to "display" troff output */
93 #define SOLIMIT 10 /* maximum allowed .so chain length */
94 #define MAXDIRS 128 /* max # of subdirs per manpath */
95 #define MAXPAGES 128 /* max # for multiple pages */
96 #define PLEN 3 /* prefix length {man, cat, fmt} */
97 #define TMPLEN 7 /* length of tmpfile prefix */
100 #define DOT_SO ".so "
101 #define PREPROC_SPEC "'\\\" "
103 #define DPRINTF if (debug && !catmando) \
106 #define sys(s) (debug ? ((void)puts(s), 0) : system(s))
107 #define eq(a, b) (strcmp(a, b) == 0)
108 #define match(a, b, c) (strncmp(a, b, c) == 0)
110 #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR)
112 #define SROFF_CMD "/usr/lib/sgml/sgml2roff" /* sgml converter */
113 #define MANDIRNAME "man" /* man directory */
114 #define SGMLDIR "sman" /* sman directory */
115 #define SGML_SYMBOL "<!DOCTYPE" /* a sgml file should contain this */
116 #define SGML_SYMBOL_LEN 9 /* length of SGML_SYMBOL */
119 * Directory mapping of old directories to new directories
127 static const map_entry map
[] = {
137 { "3xc", "3xcurses" },
142 * A list of known preprocessors to precede the formatter itself
143 * in the formatting pipeline. Preprocessors are specified by
144 * starting a manual page with a line of the form:
146 * where X is a string consisting of letters from the p_tag fields
149 static const struct preprocessor
{
154 } preprocessors
[] = {
155 {'c', "cw", "cw", "-"},
156 {'e', "neqn /usr/share/lib/pub/eqnchar",
157 "eqn /usr/share/lib/pub/eqnchar", "-"},
158 {'p', "gpic", "gpic", "-"},
159 {'r', "refer", "refer", "-"},
160 {'t', "tbl", "tbl", ""},
161 {'v', "vgrind -f", "vgrind -f", "-"},
171 * Flags that control behavior of build_manpath()
173 * BMP_ISPATH pathv is a vector constructed from PATH.
174 * Perform appropriate path translations for
176 * BMP_APPEND_MANDIR Add /usr/share/man to the end if it
177 * hasn't already appeared earlier.
178 * BMP_FALLBACK_MANDIR Append /usr/share/man only if no other
179 * manpath (including derived from PATH)
180 * elements are valid.
183 #define BMP_APPEND_MANDIR 2
184 #define BMP_FALLBACK_MANDIR 4
187 * When doing equality comparisons of directories, device and inode
188 * comparisons are done. The dupsec and dupnode structures are used
189 * to form a list of lists for this processing.
193 struct secnode
*next
;
196 dev_t dev
; /* from struct stat st_dev */
197 ino_t ino
; /* from struct stat st_ino */
198 struct secnode
*secl
; /* sections already considered */
199 struct dupnode
*next
;
203 * Map directories that may appear in PATH to the corresponding
206 static struct pathmap
{
212 {"/sbin", "/usr/share/man,1m", 0, 0},
213 {"/usr/sbin", "/usr/share/man,1m", 0, 0},
214 {"/usr/ucb", "/usr/share/man,1b", 0, 0},
215 {"/usr/bin/X11", "/usr/X11/share/man", 0, 0},
217 * Restrict to section 1 so that whatis /usr/{,xpg4,xpg6}/bin/ls
218 * does not confuse users with section 1 and 1b
220 {"/usr/bin", "/usr/share/man,1,1m,1s,1t,1c", 0, 0},
221 {"/usr/xpg4/bin", "/usr/share/man,1", 0, 0},
222 {"/usr/xpg6/bin", "/usr/share/man,1", 0, 0},
227 * Subdirectories to search for unformatted/formatted man page
228 * versions, in nroff and troff variations. The searching
229 * code in manual() is structured to expect there to be two
230 * subdirectories apiece, the first for unformatted files
231 * and the second for formatted ones.
233 static char *nroffdirs
[] = { "man", "cat", 0 };
234 static char *troffdirs
[] = { "man", "fmt", 0 };
237 usage:\tman [-] [-adFlprt] [-M path] [-T macro-package ] [ -s section ] \
239 \tman [-M path] -k keyword ...\n\tman [-M path] -f file ..."
240 #define CATMAN_USAGE "\
241 usage:\tcatman [-p] [-c|-ntw] [-M path] [-T macro-package ] [sections]"
243 static char *opts
[] = {
244 "FfkrpP:M:T:ts:lad", /* man */
245 "wpnP:M:T:tc" /* catman */
249 char *path
; /* mandir path */
250 char **secv
; /* submandir suffices */
251 int defsrch
; /* hint for man -p to avoid section list */
252 int frompath
; /* hint for man -d and catman -p */
253 struct man_node
*next
;
256 static char *pages
[MAXPAGES
];
257 static char **endp
= pages
;
277 static int compargs
; /* -c option for catman */
280 static char *CAT
= CAT_
;
281 static char macros
[MAXPATHLEN
];
284 static char *troffcmd
;
285 static char *troffcat
;
286 static char **subdirs
;
288 static char *check_config(char *);
289 static struct man_node
*build_manpath(char **, int);
290 static void getpath(struct man_node
*, char **);
291 static void getsect(struct man_node
*, char **);
292 static void get_all_sect(struct man_node
*);
293 static void catman(struct man_node
*, char **, int);
294 static int makecat(char *, char **, int);
295 static int getdirs(char *, char ***, short);
296 static void whatapro(struct man_node
*, char *, int);
297 static void lookup_windex(char *, char *, char **);
298 static int icmp(wchar_t *, wchar_t *);
299 static void more(char **, int);
300 static void cleanup(char **);
301 static void bye(int);
302 static char **split(char *, char);
303 static void freev(char **);
304 static void fullpaths(struct man_node
**);
305 static void lower(char *);
306 static int cmp(const void *, const void *);
307 static int manual(struct man_node
*, char *);
308 static void mandir(char **, char *, char *);
309 static void sortdir(DIR *, char ***);
310 static int searchdir(char *, char *, char *);
311 static int windex(char **, char *, char *);
312 static void section(struct suffix
*, char *);
313 static int bfsearch(FILE *, char **, char *, char **);
314 static int compare(char *, char *, char **);
315 static int format(char *, char *, char *, char *);
316 static char *addlocale(char *);
317 static int get_manconfig(FILE *, char *);
318 static void malloc_error(void);
319 static int sgmlcheck(const char *);
320 static char *map_section(char *, char *);
321 static void free_manp(struct man_node
*manp
);
322 static void init_bintoman(void);
323 static char *path_to_manpath(char *);
324 static int dupcheck(struct man_node
*, struct dupnode
**);
325 static void free_dupnode(struct dupnode
*);
326 static void print_manpath(struct man_node
*, char *);
329 * This flag is used when the SGML-to-troff converter
330 * is absent - all the SGML searches are bypassed.
332 static int no_sroff
= 0;
335 * This flag is used to describe the case where we've found
336 * an SGML formatted manpage in the sman directory, we haven't
337 * found a troff formatted manpage, and we don't have the SGML to troff
338 * conversion utility on the system.
340 static int sman_no_man_no_sroff
;
342 static char language
[PATH_MAX
+ 1]; /* LC_MESSAGES */
343 static char localedir
[PATH_MAX
+ 1]; /* locale specific path component */
345 static int defaultmandir
= 1; /* if processing default mandir, 1 */
347 static char *newsection
= NULL
;
350 main(int argc
, char *argv
[])
356 char *manpath
= NULL
;
357 static struct man_node
*manpage
= NULL
;
361 if (access(SROFF_CMD
, F_OK
| X_OK
) != 0)
364 (void) setlocale(LC_ALL
, "");
365 (void) strcpy(language
, setlocale(LC_MESSAGES
, (char *)0));
366 if (strcmp("C", language
) != 0)
367 (void) sprintf(localedir
, "%s", language
);
369 #if !defined(TEXT_DOMAIN)
370 #define TEXT_DOMAIN "SYS_TEST"
372 (void) textdomain(TEXT_DOMAIN
);
374 (void) strcpy(macros
, TMAC_AN
);
377 * get base part of command name
379 if ((cmdname
= strrchr(argv
[0], '/')) != NULL
)
384 if (eq(cmdname
, "apropos") || eq(cmdname
, "whatis")) {
386 apropos
= (*cmdname
== 'a');
387 if ((optind
= 1) == argc
) {
388 (void) fprintf(stderr
, gettext("%s what?\n"), cmdname
);
392 } else if (eq(cmdname
, "catman"))
396 while ((c
= getopt(argc
, argv
, opts
[catmando
])) != -1)
400 * man specific options
409 force
++; /* do lookups the hard way */
419 list
++; /* implies all */
428 * man and catman use -p differently. In catman it
429 * enables debug mode and in man it prints the (possibly
430 * derived from PATH or name operand) MANPATH.
445 case 'c': /* n|troff compatibility */
447 (void) fprintf(stderr
, gettext(
448 "catman: SGML conversion not "
449 "available -- -c flag ignored\n"));
457 case 'P': /* Backwards compatibility */
458 case 'M': /* Respecify path for man pages. */
462 case 'T': /* Respecify man macros */
463 (void) strcpy(macros
, optarg
);
474 * Bad options or no args?
475 * (man -p and catman don't need args)
477 if (badopts
|| (!catmando
&& !printmp
&& optind
== argc
)) {
478 (void) fprintf(stderr
, "%s\n", catmando
?
479 gettext(CATMAN_USAGE
) : gettext(MAN_USAGE
));
483 if (compargs
&& (nowhatis
|| whatonly
|| troffit
)) {
484 (void) fprintf(stderr
, "%s\n", gettext(CATMAN_USAGE
));
485 (void) fprintf(stderr
, gettext(
486 "-c option cannot be used with [-w][-n][-t]\n"));
490 if (sargs
&& margs
&& catmando
) {
491 (void) fprintf(stderr
, "%s\n", gettext(CATMAN_USAGE
));
495 if (troffit
== 0 && nomore
== 0 && !isatty(fileno(stdout
)))
499 * Collect environment information.
502 if ((troffcmd
= getenv("TROFF")) == NULL
)
504 if ((troffcat
= getenv("TCAT")) == NULL
)
507 if (((pager
= getenv("PAGER")) == NULL
) ||
513 subdirs
= troffit
? troffdirs
: nroffdirs
;
517 if (manpath
== NULL
&& (manpath
= getenv("MANPATH")) == NULL
) {
518 if ((manpath
= getenv("PATH")) != NULL
) {
519 bmp_flags
= BMP_ISPATH
| BMP_APPEND_MANDIR
;
525 pathv
= split(manpath
, ':');
527 manpage
= build_manpath(pathv
, bmp_flags
);
529 /* release pathv allocated by split() */
533 * Since we can't make use of GNU troff, set the path to ensure we
534 * find the one in /usr/bin first.
536 if (putenv("PATH=/usr/bin") != 0) {
544 catman(manpage
, argv
+optind
, argc
-optind
);
549 * The manual routine contains windows during which
550 * termination would leave a temp file behind. Thus
551 * we blanket the whole thing with a clean-up routine.
553 if (signal(SIGINT
, SIG_IGN
) == SIG_DFL
) {
554 (void) signal(SIGINT
, bye
);
555 (void) signal(SIGQUIT
, bye
);
556 (void) signal(SIGTERM
, bye
);
560 * "man -p" without operands
562 if ((printmp
!= 0) && (optind
== argc
)) {
563 print_manpath(manpage
, NULL
);
567 for (; optind
< argc
; optind
++) {
568 if (strcmp(argv
[optind
], "-") == 0) {
573 static struct man_node
*mp
;
577 * If full path to command specified, customize
578 * manpath accordingly
580 if ((cmd
= strrchr(argv
[optind
], '/')) != NULL
) {
582 if ((pv
[0] = strdup(argv
[optind
])) == NULL
) {
587 mp
= build_manpath(pv
,
588 BMP_ISPATH
|BMP_FALLBACK_MANDIR
);
594 whatapro(mp
, argv
[optind
], apropos
);
595 } else if (printmp
!= 0) {
596 print_manpath(mp
, argv
[optind
]);
598 err
+= manual(mp
, argv
[optind
]);
601 if (mp
!= NULL
&& mp
!= manpage
) {
607 return (err
== 0 ? 0 : 1);
612 * This routine builds the manpage structure from MANPATH or PATH,
613 * depending on flags. See BMP_* definitions above for valid
616 * Assumes pathv elements were malloc'd, as done by split().
617 * Elements may be freed and reallocated to have different contents.
620 static struct man_node
*
621 build_manpath(char **pathv
, int flags
)
623 struct man_node
*manpage
= NULL
;
624 struct man_node
*currp
= NULL
;
625 struct man_node
*lastp
= NULL
;
629 char *mandir
= MANDIR
;
631 struct dupnode
*didup
= NULL
;
634 s
= sizeof (struct man_node
);
635 for (p
= pathv
; *p
; ) {
637 if (flags
& BMP_ISPATH
) {
638 if ((mand
= path_to_manpath(*p
)) == NULL
) {
645 if (stat(q
[0], &sb
) != 0 || (sb
.st_mode
& S_IFDIR
) == 0) {
650 if (access(q
[0], R_OK
|X_OK
) != 0) {
652 (void) fprintf(stderr
,
653 gettext("%s is not accessible.\n"),
655 (void) fflush(stderr
);
660 * Some element exists. Do not append MANDIR as a
663 flags
&= ~BMP_FALLBACK_MANDIR
;
665 if ((currp
= (struct man_node
*)calloc(1, s
)) == NULL
) {
669 currp
->frompath
= (flags
& BMP_ISPATH
);
671 if (manpage
== NULL
) {
672 lastp
= manpage
= currp
;
679 * If there are no new elements in this path,
680 * do not add it to the manpage list
682 if (dupcheck(currp
, &didup
) != 0) {
687 if (currp
!= manpage
) {
696 * Special handling of appending MANDIR.
697 * After all pathv elements have been processed, append MANDIR
707 if (flags
& (BMP_APPEND_MANDIR
|BMP_FALLBACK_MANDIR
)) {
709 flags
&= ~BMP_ISPATH
;
719 * Stores the mandir path into the manp structure.
723 getpath(struct man_node
*manp
, char **pv
)
730 while (*s
!= NULL
&& *s
!= ',')
733 manp
->path
= (char *)malloc(i
+1);
734 if (manp
->path
== NULL
)
736 (void) strncpy(manp
->path
, *pv
, i
);
737 *(manp
->path
+ i
) = '\0';
741 * Stores the mandir's corresponding sections (submandir
742 * directories) into the manp structure.
746 getsect(struct man_node
*manp
, char **pv
)
752 manp
->secv
= split(mansec
, ',');
754 for (sectp
= manp
->secv
; *sectp
; sectp
++)
756 } else if ((sections
= strchr(*pv
, ',')) != NULL
) {
758 if (manp
->frompath
!= 0) {
760 * TRANSLATION_NOTE - message for man -d or catman -p
761 * ex. /usr/share/man: derived from PATH, MANSECTS=,1b
763 (void) printf(gettext(
764 "%s: derived from PATH, MANSECTS=%s\n"),
765 manp
->path
, sections
);
768 * TRANSLATION_NOTE - message for man -d or catman -p
769 * ex. /usr/share/man: from -M option, MANSECTS=,1,2,3c
771 (void) fprintf(stdout
, gettext(
772 "%s: from -M option, MANSECTS=%s\n"),
773 manp
->path
, sections
);
776 manp
->secv
= split(++sections
, ',');
777 for (sectp
= manp
->secv
; *sectp
; sectp
++)
780 if (*manp
->secv
== NULL
)
782 } else if ((sections
= check_config(*pv
)) != NULL
) {
785 * TRANSLATION_NOTE - message for man -d or catman -p
786 * ex. /usr/share/man: from man.cf, MANSECTS=1,1m,1c
789 (void) fprintf(stdout
, gettext(
790 "%s: from %s, MANSECTS=%s\n"),
791 manp
->path
, CONFIG
, sections
);
792 manp
->secv
= split(sections
, ',');
794 for (sectp
= manp
->secv
; *sectp
; sectp
++)
797 if (*manp
->secv
== NULL
)
802 * TRANSLATION_NOTE - message for man -d or catman -p
803 * if man.cf has not been found or sections has not been specified
804 * man/catman searches the sections lexicographically.
807 (void) fprintf(stdout
, gettext(
808 "%s: search the sections lexicographically\n"),
816 * Get suffices of all sub-mandir directories in a mandir.
820 get_all_sect(struct man_node
*manp
)
829 int maxentries
= MAXTOKENS
;
832 if ((dp
= opendir(manp
->path
)) == 0)
836 * sortdir() allocates memory for dirv and dirv[].
842 if (manp
->secv
== NULL
) {
844 * allocates memory for manp->secv only if it's NULL
846 manp
->secv
= (char **)malloc(maxentries
* sizeof (char *));
847 if (manp
->secv
== NULL
)
851 for (dv
= dirv
, p
= manp
->secv
; *dv
; dv
++) {
853 if (match(*dv
, SGMLDIR
, PLEN
+1))
856 if (strcmp(*dv
, CONFIG
) == 0) {
857 /* release memory allocated by sortdir */
864 tmp
= strdup(*dv
+ plen
);
867 (void) sprintf(tmp
, "%s", *dv
+ plen
);
870 if (strcmp(prev
, tmp
) == 0) {
871 /* release memory allocated by sortdir */
879 prev
= strdup(*dv
+ plen
);
882 (void) sprintf(prev
, "%s", *dv
+ plen
);
884 * copy the string in (*dv + plen) to *p
886 *p
= strdup(*dv
+ plen
);
891 if (entries
== maxentries
) {
892 maxentries
+= MAXTOKENS
;
893 manp
->secv
= (char **)realloc(manp
->secv
,
894 sizeof (char *) * maxentries
);
895 if (manp
->secv
== NULL
)
897 p
= manp
->secv
+ entries
;
899 /* release memory allocated by sortdir */
903 /* release memory allocated by sortdir */
908 * Format man pages (build cat pages); if no
909 * sections are specified, build all of them.
910 * When building cat pages:
911 * catman() tries to build cat pages for locale specific
912 * man dirs first. Then, catman() tries to build cat pages
913 * for the default man dir (for C locale like /usr/share/man)
914 * regardless of the locale.
915 * When building windex file:
916 * catman() tries to build windex file for locale specific
917 * man dirs first. Then, catman() tries to build windex file
918 * for the default man dir (for C locale like /usr/share/man)
919 * regardless of the locale.
923 catman(struct man_node
*manp
, char **argv
, int argc
)
932 struct dupnode
*dnp
= NULL
;
935 * May be overwritten in dupcheck() so must be kept out of .rodata.
937 char fakename
[] = " catman ";
940 fakesecv
[0] = fakename
;
943 for (p
= manp
; p
!= NULL
; p
= p
->next
) {
945 * prevent catman from doing very heavy lifting multiple
946 * times on some directory
950 if (dupcheck(p
, &dnp
) != 0) {
956 * TRANSLATION_NOTE - message for catman -p
957 * ex. mandir path = /usr/share/man
960 (void) fprintf(stdout
, gettext(
961 "\nmandir path = %s\n"), p
->path
);
966 * addlocale() allocates memory and returns it
968 ldir
= addlocale(p
->path
);
970 if (*localedir
!= '\0') {
973 /* getdirs allocate memory for dv */
974 ndirs
= getdirs(ldir
, &dv
, 1);
977 makecat(ldir
, argv
, argc
) :
978 makecat(ldir
, dv
, ndirs
);
979 /* release memory by getdirs */
980 for (i
= 0; i
< ndirs
; i
++) {
987 /* default man dir is always processed */
989 ndirs
= getdirs(p
->path
, &dv
, 1);
991 makecat(p
->path
, argv
, argc
) :
992 makecat(p
->path
, dv
, ndirs
);
993 /* release memory allocated by getdirs */
994 for (i
= 0; i
< ndirs
; i
++) {
1000 * Build whatis database
1001 * print error message if locale is set and man dir not found
1002 * won't build it at all if -c option is on
1004 if (!compargs
&& (whatonly
|| (!nowhatis
&& changed
))) {
1005 if (*localedir
!= '\0') {
1006 /* just count the number of ndirs */
1007 if ((ndirs
= getdirs(ldir
, NULL
, 0)) != 0) {
1008 (void) sprintf(cmdbuf
,
1009 "/usr/bin/sh %s %s",
1014 /* whatis database of the default man dir */
1015 /* will be always built in C locale. */
1016 (void) sprintf(cmdbuf
,
1017 "/usr/bin/sh %s %s",
1018 MAKEWHATIS
, p
->path
);
1021 /* release memory allocated by addlocale() */
1028 * Build cat pages for given sections
1032 makecat(char *path
, char **dv
, int ndirs
)
1037 char mandir
[MAXPATHLEN
+1];
1038 char smandir
[MAXPATHLEN
+1];
1039 char catdir
[MAXPATHLEN
+1];
1042 int manflag
, smanflag
;
1044 for (i
= fmt
= 0; i
< ndirs
; i
++) {
1045 (void) snprintf(mandir
, MAXPATHLEN
, "%s/%s%s",
1046 path
, MANDIRNAME
, dv
[i
]);
1047 (void) snprintf(smandir
, MAXPATHLEN
, "%s/%s%s",
1048 path
, SGMLDIR
, dv
[i
]);
1049 (void) snprintf(catdir
, MAXPATHLEN
, "%s/%s%s",
1050 path
, subdirs
[1], dv
[i
]);
1051 dirp
= strrchr(mandir
, '/') + 1;
1052 sdirp
= strrchr(smandir
, '/') + 1;
1054 manflag
= smanflag
= 0;
1056 if ((dp
= opendir(mandir
)) != NULL
)
1059 if (!no_sroff
&& (sdp
= opendir(smandir
)) != NULL
)
1062 if (dp
== 0 && sdp
== 0) {
1063 if (strcmp(mandir
, CONFIG
) == 0)
1068 * TRANSLATION_NOTE - message for catman -p
1069 * ex. Building cat pages for mandir = /usr/share/man/ja
1072 (void) fprintf(stdout
, gettext(
1073 "Building cat pages for mandir = %s\n"), path
);
1075 if (!compargs
&& stat(catdir
, &sbuf
) < 0) {
1078 * TRANSLATION_NOTE - message for catman -p
1079 * ex. mkdir /usr/share/man/ja/cat3c
1082 (void) fprintf(stdout
, gettext("mkdir %s\n"),
1085 if (mkdir(catdir
, 0755) < 0) {
1089 (void) chmod(catdir
, 0755);
1094 * if it is -c option of catman, if there is no
1095 * coresponding man dir for sman files to go to,
1099 if (compargs
&& !manflag
) {
1100 if (mkdir(mandir
, 0755) < 0) {
1104 (void) chmod(mandir
, 0755);
1108 while ((d
= readdir(sdp
))) {
1109 if (eq(".", d
->d_name
) || eq("..", d
->d_name
))
1112 if (format(path
, sdirp
, (char *)0, d
->d_name
)
1118 if (manflag
&& !compargs
) {
1119 while ((d
= readdir(dp
))) {
1120 if (eq(".", d
->d_name
) || eq("..", d
->d_name
))
1123 if (format(path
, dirp
, (char *)0, d
->d_name
)
1130 (void) closedir(dp
);
1133 (void) closedir(sdp
);
1141 * Get all "man" and "sman" dirs under a given manpath
1142 * and return the number found
1143 * If -c option is on, only count sman dirs
1147 getdirs(char *path
, char ***dirv
, short flag
)
1152 int plen
, sgml_flag
, man_flag
;
1154 int maxentries
= MAXDIRS
;
1157 if ((dp
= opendir(path
)) == 0) {
1159 if (*localedir
!= '\0')
1160 (void) printf(gettext("\
1161 locale is %s, search in %s\n"), localedir
, path
);
1168 /* allocate memory for dirv */
1169 *dirv
= (char **)malloc(sizeof (char *) *
1175 while ((d
= readdir(dp
))) {
1177 man_flag
= sgml_flag
= 0;
1178 if (match(d
->d_name
, SGMLDIR
, PLEN
+1)) {
1184 if (match(subdirs
[0], d
->d_name
, PLEN
))
1187 if (compargs
&& sgml_flag
) {
1189 *dv
= strdup(d
->d_name
+plen
);
1195 } else if (!compargs
&& (sgml_flag
|| man_flag
)) {
1197 *dv
= strdup(d
->d_name
+plen
);
1205 if ((dv
- *dirv
) == maxentries
) {
1206 int entries
= maxentries
;
1207 maxentries
+= MAXTOKENS
;
1208 *dirv
= (char **)realloc(*dirv
,
1209 sizeof (char *) * maxentries
);
1212 dv
= *dirv
+ entries
;
1217 (void) closedir(dp
);
1223 * Find matching whatis or apropos entries
1224 * whatapro() tries to handle the windex file of the locale specific
1225 * man dirs first, then tries to handle the windex file of the default
1226 * man dir (of C locale like /usr/share/man).
1230 whatapro(struct man_node
*manp
, char *word
, int apropos
)
1232 char whatpath
[MAXPATHLEN
+1];
1240 * TRANSLATION_NOTE - message for man -d
1241 * %s takes a parameter to -k option.
1243 DPRINTF(gettext("word = %s \n"), word
);
1246 * get base part of name
1249 if ((p
= strrchr(word
, '/')) == NULL
)
1257 for (b
= manp
; b
!= NULL
; b
= b
->next
) {
1259 if (*localedir
!= '\0') {
1260 /* addlocale() allocates memory and returns it */
1261 ldir
= addlocale(b
->path
);
1264 ndirs
= getdirs(ldir
, NULL
, 0);
1266 (void) sprintf(whatpath
, "%s/%s", ldir
, WHATIS
);
1268 * TRANSLATION_NOTE - message for man -d
1269 * ex. mandir path = /usr/share/man/ja
1271 DPRINTF(gettext("\nmandir path = %s\n"), ldir
);
1272 lookup_windex(whatpath
, p
, b
->secv
);
1274 /* release memory allocated by addlocale() */
1279 (void) sprintf(whatpath
, "%s/%s", b
->path
, WHATIS
);
1281 * TRANSLATION_NOTE - message for man -d
1282 * ex. mandir path = /usr/share/man
1284 DPRINTF(gettext("\nmandir path = %s\n"), b
->path
);
1286 lookup_windex(whatpath
, p
, b
->secv
);
1292 lookup_windex(char *whatpath
, char *word
, char **secv
)
1295 char *matches
[MAXPAGES
];
1297 wchar_t wbuf
[BUFSIZ
];
1298 wchar_t *word_wchar
= NULL
;
1300 size_t word_len
, ret
;
1302 if ((fp
= fopen(whatpath
, "r")) == NULL
) {
1308 word_len
= strlen(word
) + 1;
1309 if ((word_wchar
= (wchar_t *)malloc(sizeof (wchar_t) *
1310 word_len
)) == NULL
) {
1313 ret
= mbstowcs(word_wchar
, (const char *)word
, word_len
);
1314 if (ret
== (size_t)-1) {
1315 (void) fprintf(stderr
, gettext(
1316 "Invalid character in keyword\n"));
1319 while (fgetws(wbuf
, BUFSIZ
, fp
) != NULL
)
1320 for (ws
= wbuf
; *ws
; ws
++)
1321 if (icmp(word_wchar
, ws
) == 0) {
1322 (void) printf("%ws", wbuf
);
1326 if (bfsearch(fp
, matches
, word
, secv
))
1327 for (pp
= matches
; *pp
; pp
++) {
1328 (void) printf("%s", *pp
);
1330 * release memory allocated by
1331 * strdup() in bfsearch()
1344 * case-insensitive compare unless upper case is used
1345 * ie) "mount" matches mount, Mount, MOUNT
1346 * "Mount" matches Mount, MOUNT
1347 * "MOUNT" matches MOUNT only
1348 * If matched return 0. Otherwise, return 1.
1352 icmp(wchar_t *ws
, wchar_t *wt
)
1354 for (; (*ws
== 0) ||
1355 (*ws
== (iswupper(*ws
) ? *wt
: towlower(*wt
)));
1365 * Invoke PAGER with all matching man pages
1369 more(char **pages
, int plain
)
1371 char cmdbuf
[BUFSIZ
];
1377 if (list
|| (*pages
== 0))
1380 if (plain
&& troffit
) {
1384 (void) sprintf(cmdbuf
, "%s", troffit
? troffcat
:
1385 plain
? CAT
: pager
);
1390 for (vp
= pages
; vp
< endp
; vp
++) {
1391 (void) strcat(cmdbuf
, " ");
1392 (void) strcat(cmdbuf
, *vp
);
1404 cleanup(char **pages
)
1408 for (vp
= pages
; vp
< endp
; vp
++) {
1409 if (match(TEMPLATE
, *vp
, TMPLEN
))
1414 endp
= pages
; /* reset */
1419 * Clean things up after receiving a signal.
1433 * Split a string by specified separator.
1434 * ignore empty components/adjacent separators.
1435 * returns vector to all tokens
1439 split(char *s1
, char sep
)
1443 int maxentries
= MAXTOKENS
;
1446 tokv
= vp
= (char **)malloc(maxentries
* sizeof (char *));
1450 for (; mp
&& *mp
; mp
= tp
) {
1451 tp
= strchr(mp
, sep
);
1452 if (mp
== tp
) { /* empty component */
1457 /* a component found */
1461 *vp
= (char *)malloc(sizeof (char) * len
+ 1);
1464 (void) strncpy(*vp
, mp
, len
);
1465 *(*vp
+ len
) = '\0';
1469 /* the last component */
1476 if (entries
== maxentries
) {
1477 maxentries
+= MAXTOKENS
;
1478 tokv
= (char **)realloc(tokv
,
1479 maxentries
* sizeof (char *));
1482 vp
= tokv
+ entries
;
1490 * Free a vector allocated by split();
1497 for (i
= 0; v
[i
] != NULL
; i
++) {
1505 * Convert paths to full paths if necessary
1510 fullpaths(struct man_node
**manp_head
)
1514 char cwd_gotten
= 0;
1515 struct man_node
*manp
= *manp_head
;
1517 struct man_node
*prev
= NULL
;
1519 for (b
= manp
; b
!= NULL
; b
= b
->next
) {
1520 if (*(b
->path
) == '/') {
1525 /* try to get cwd if haven't already */
1527 cwd
= getcwd(NULL
, MAXPATHLEN
+1);
1532 /* case: relative manpath with cwd: make absolute */
1533 if ((p
= malloc(strlen(b
->path
)+strlen(cwd
)+2)) ==
1537 (void) sprintf(p
, "%s/%s", cwd
, b
->path
);
1544 /* case: relative manpath but no cwd: omit path entry */
1546 prev
->next
= b
->next
;
1548 *manp_head
= b
->next
;
1554 * release memory allocated by getcwd()
1560 * Free a man_node structure and its contents
1564 free_manp(struct man_node
*manp
)
1570 while ((p
!= NULL
) && (*p
!= NULL
)) {
1580 * Map (in place) to lower case
1597 * compare for sort()
1598 * sort first by section-spec, then by prefix {sman, man, cat, fmt}
1599 * note: prefix is reverse sorted so that "sman" and "man" always
1600 * comes before {cat, fmt}
1604 cmp(const void *arg1
, const void *arg2
)
1607 char **p1
= (char **)arg1
;
1608 char **p2
= (char **)arg2
;
1611 /* by section; sman always before man dirs */
1612 if ((n
= strcmp(*p1
+ PLEN
+ (**p1
== 's' ? 1 : 0),
1613 *p2
+ PLEN
+ (**p2
== 's' ? 1 : 0))))
1616 /* by prefix reversed */
1617 return (strncmp(*p2
, *p1
, PLEN
));
1622 * Find a man page ...
1623 * Loop through each path specified,
1624 * first try the lookup method (whatis database),
1625 * and if it doesn't exist, do the hard way.
1629 manual(struct man_node
*manp
, char *name
)
1632 struct man_node
*local
;
1636 char *fullname
= name
;
1639 if ((slash
= strrchr(name
, '/')) != NULL
) {
1644 * for each path in MANPATH
1648 for (p
= manp
; p
!= NULL
; p
= p
->next
) {
1650 * TRANSLATION_NOTE - message for man -d
1651 * ex. mandir path = /usr/share/man
1653 DPRINTF(gettext("\nmandir path = %s\n"), p
->path
);
1655 if (*localedir
!= '\0') {
1656 /* addlocale() allocates memory and returns it */
1657 ldir
= addlocale(p
->path
);
1661 * TRANSLATION_NOTE - message for man -d
1662 * ex. localedir = ja, ldir = /usr/share/man/ja
1665 (void) printf(gettext(
1666 "localedir = %s, ldir = %s\n"),
1668 ndirs
= getdirs(ldir
, NULL
, 0);
1672 local
= build_manpath(ldirs
, 0);
1674 windex(local
->secv
, ldir
, name
) < 0)
1675 mandir(local
->secv
, ldir
, name
);
1678 /* release memory allocated by addlocale() */
1684 * locale mandir not valid, man page in locale
1685 * mandir not found, or -a option present
1687 if (ndirs
== 0 || !found
|| all
) {
1688 if (force
|| windex(p
->secv
, p
->path
, name
) < 0)
1689 mandir(p
->secv
, p
->path
, name
);
1697 more(pages
, nomore
);
1700 (void) fprintf(stderr
, gettext("No entry for %s in "
1701 "section(s) %s of the manual.\n"),
1704 (void) fprintf(stderr
, gettext(
1705 "No manual entry for %s.\n"), fullname
, mansec
);
1708 if (sman_no_man_no_sroff
)
1709 (void) fprintf(stderr
, gettext("(An SGML manpage was "
1710 "found for '%s' but it cannot be displayed.)\n"),
1713 sman_no_man_no_sroff
= 0;
1719 * For a specified manual directory,
1720 * read, store, & sort section subdirs,
1721 * for each section specified
1722 * find and search matching subdirs
1726 mandir(char **secv
, char *path
, char *name
)
1731 int len
, dslen
, plen
= PLEN
;
1733 if ((dp
= opendir(path
)) == 0) {
1735 * TRANSLATION_NOTE - message for man -d or catman -p
1736 * opendir(%s) returned 0
1739 (void) fprintf(stdout
, gettext(
1740 " opendir on %s failed\n"), path
);
1745 * TRANSLATION_NOTE - message for man -d or catman -p
1746 * ex. mandir path = /usr/share/man/ja
1749 (void) printf(gettext("mandir path = %s\n"), path
);
1752 * sordir() allocates memory for dirv and dirv[].
1756 * Search in the order specified by MANSECTS
1758 for (; *secv
; secv
++) {
1760 * TRANSLATION_NOTE - message for man -d or catman -p
1763 DPRINTF(gettext(" section = %s\n"), *secv
);
1764 len
= strlen(*secv
);
1765 for (dv
= dirv
; *dv
; dv
++) {
1769 dslen
= strlen(*dv
+plen
);
1772 if (**secv
== '\\') {
1773 if (!eq(*secv
+ 1, *dv
+plen
))
1775 } else if (strncasecmp(*secv
, *dv
+plen
, len
) != 0) {
1776 /* check to see if directory name changed */
1778 (newsection
= map_section(*secv
, path
))
1782 if (newsection
== NULL
)
1784 if (!match(newsection
, *dv
+plen
, len
)) {
1789 if (searchdir(path
, *dv
, name
) == 0)
1793 /* release memory allocated by sortdir() */
1799 (void) closedir(dp
);
1800 /* release memory allocated by sortdir() */
1805 * if we found a match in the man dir skip
1806 * the corresponding cat dir if it exists
1808 if (all
&& **dv
== 'm' && *(dv
+1) &&
1809 eq(*(dv
+1)+plen
, *dv
+plen
))
1813 /* release memory allocated by sortdir() */
1820 (void) closedir(dp
);
1828 sortdir(DIR *dp
, char ***dirv
)
1832 int maxentries
= MAXDIRS
;
1835 *dirv
= (char **)malloc(sizeof (char *) * maxentries
);
1837 while ((d
= readdir(dp
))) { /* store dirs */
1838 if (eq(d
->d_name
, ".") || eq(d
->d_name
, "..")) /* ignore */
1841 /* check if it matches sman, man, cat format */
1842 if (match(d
->d_name
, SGMLDIR
, PLEN
+1) ||
1843 match(d
->d_name
, subdirs
[0], PLEN
) ||
1844 match(d
->d_name
, subdirs
[1], PLEN
)) {
1845 *dv
= malloc(strlen(d
->d_name
) + 1);
1848 (void) strcpy(*dv
, d
->d_name
);
1851 if (entries
== maxentries
) {
1852 maxentries
+= MAXDIRS
;
1853 *dirv
= (char **)realloc(*dirv
,
1854 sizeof (char *) * maxentries
);
1857 dv
= *dirv
+ entries
;
1863 qsort((void *)*dirv
, dv
- *dirv
, sizeof (char *), cmp
);
1869 * Search a section subdirectory for a
1870 * given man page, return 1 for success
1874 searchdir(char *path
, char *dir
, char *name
)
1878 char sectpath
[MAXPATHLEN
+1];
1879 char file
[MAXNAMLEN
+1];
1880 char dname
[MAXPATHLEN
+1];
1885 * TRANSLATION_NOTE - message for man -d or catman -p
1886 * ex. scanning = man3c
1888 DPRINTF(gettext(" scanning = %s\n"), dir
);
1889 (void) sprintf(sectpath
, "%s/%s", path
, dir
);
1890 (void) snprintf(file
, MAXPATHLEN
, "%s.", name
);
1892 if ((sdp
= opendir(sectpath
)) == 0) {
1893 if (errno
!= ENOTDIR
) /* ignore matching cruft */
1897 while ((sd
= readdir(sdp
))) {
1898 last
= strrchr(sd
->d_name
, '.');
1899 nlen
= last
- sd
->d_name
;
1900 (void) sprintf(dname
, "%.*s.", nlen
, sd
->d_name
);
1901 if (eq(dname
, file
) || eq(sd
->d_name
, name
)) {
1902 if (no_sroff
&& *dir
== 's') {
1903 sman_no_man_no_sroff
= 1;
1906 (void) format(path
, dir
, name
, sd
->d_name
);
1907 (void) closedir(sdp
);
1911 (void) closedir(sdp
);
1916 * Check the hash table of old directory names to see if there is a
1917 * new directory name.
1918 * Returns new directory name if a match; after checking to be sure
1920 * Otherwise returns NULL
1924 map_section(char *section
, char *path
)
1928 char fullpath
[MAXPATHLEN
];
1930 if (list
) /* -l option fall through */
1933 for (i
= 0; i
<= ((sizeof (map
)/sizeof (map
[0]) - 1)); i
++) {
1934 if (strlen(section
) > strlen(map
[i
].new_name
)) {
1935 len
= strlen(section
);
1937 len
= strlen(map
[i
].new_name
);
1939 if (match(section
, map
[i
].old_name
, len
)) {
1940 (void) sprintf(fullpath
,
1941 "%s/sman%s", path
, map
[i
].new_name
);
1942 if (!access(fullpath
, R_OK
| X_OK
)) {
1943 return (map
[i
].new_name
);
1955 * Use windex database for quick lookup of man pages
1956 * instead of mandir() (brute force search)
1960 windex(char **secv
, char *path
, char *name
)
1965 struct suffix psecs
[MAXPAGES
+1];
1966 char whatfile
[MAXPATHLEN
+1];
1967 char page
[MAXPATHLEN
+1];
1968 char *matches
[MAXPAGES
];
1971 int len
, dslen
, exist
, i
;
1972 int found_in_windex
= 0;
1973 char *tmp
[] = {0, 0, 0, 0};
1976 (void) sprintf(whatfile
, "%s/%s", path
, WHATIS
);
1977 if ((fp
= fopen(whatfile
, "r")) == NULL
) {
1978 if (errno
== ENOENT
)
1984 * TRANSLATION_NOTE - message for man -d or catman -p
1985 * ex. search in = /usr/share/man/ja/windex file
1988 (void) fprintf(stdout
, gettext(
1989 " search in = %s file\n"), whatfile
);
1991 if (bfsearch(fp
, matches
, name
, NULL
) == 0) {
1993 return (-1); /* force search in mandir */
1999 * Save and split sections
2000 * section() allocates memory for sp->ds
2002 for (sp
= psecs
, vp
= matches
; *vp
; vp
++, sp
++) {
2003 if ((sp
- psecs
) < MAXPAGES
) {
2007 (void) fprintf(stderr
, gettext(
2008 "too many sections in %s windex entry\n"),
2011 /* Setting sp->ds to NULL signifies end-of-data. */
2020 * Search in the order specified
2023 for (; *secv
; secv
++) {
2024 len
= strlen(*secv
);
2027 * TRANSLATION_NOTE - message for man -d or catman -p
2028 * ex. search an entry to match printf.3c
2031 (void) fprintf(stdout
, gettext(
2032 " search an entry to match %s.%s\n"), name
, *secv
);
2034 * For every whatis entry that
2037 for (sp
= psecs
; sp
->ds
; sp
++) {
2038 dslen
= strlen(sp
->ds
);
2041 if (**secv
== '\\') {
2042 if (!eq(*secv
+ 1, sp
->ds
))
2044 } else if (!match(*secv
, sp
->ds
, len
)) {
2045 /* check to see if directory name changed */
2047 (newsection
= map_section(*secv
, path
))
2051 if (newsection
== NULL
)
2053 if (!match(newsection
, sp
->ds
, len
)) {
2058 * here to form "sman", "man", "cat"|"fmt" in
2063 for (i
= 1; i
< 4; i
++)
2064 tmp
[i
] = subdirs
[i
-1];
2066 for (i
= 0; i
< 3; i
++)
2067 tmp
[i
] = subdirs
[i
];
2070 for (sv
= tmp
; *sv
; sv
++) {
2071 (void) sprintf(page
,
2072 "%s/%s%s/%s%s%s", path
, *sv
,
2073 sp
->ds
, name
, *sp
->fs
? "." : "",
2075 exist
= (stat(page
, &sbuf
) == 0);
2080 (void) fprintf(stderr
, gettext(
2081 "%s entry incorrect: %s(%s) not found.\n"),
2082 WHATIS
, name
, sp
->ds
);
2086 file
= strrchr(page
, '/'), *file
= 0;
2087 dir
= strrchr(page
, '/');
2090 * By now we have a match
2092 found_in_windex
= 1;
2093 (void) format(path
, ++dir
, name
, ++file
);
2101 * release memory allocated by section()
2111 * If we didn't find a match, return failure as if we didn't find
2112 * the windex at all. Why? Well, if you create a windex, then upgrade
2113 * to a later release that contains new man pages, and forget to
2114 * recreate the windex (since we don't do that automatically), you
2115 * won't see any new man pages since they aren't in the windex.
2116 * Pretending we didn't see a windex at all if there are no matches
2117 * forces a search of the underlying directory. After all, the
2118 * goal of the windex is to enable searches (man -k) and speed things
2119 * up, not to _prevent_ you from seeing new man pages, so this seems
2120 * ok. The only problem is when there are multiple entries (different
2121 * sections), and some are in and some are out. Say you do 'man ls',
2122 * and ls(1) isn't in the windex, but ls(1B) is. In that case, we
2123 * will find a match in ls(1B), and you'll see that man page.
2124 * That doesn't seem bad since if you specify the section the search
2125 * will be restricted too. So in the example above, if you do
2126 * 'man -s 1 ls' you'll get ls(1).
2128 if (found_in_windex
)
2136 * Return pointers to the section-spec
2137 * and file-suffix of a whatis entry
2141 section(struct suffix
*sp
, char *s
)
2145 lp
= strchr(s
, '(');
2148 if (++lp
== 0 || p
== 0 || lp
== p
) {
2149 (void) fprintf(stderr
,
2150 gettext("mangled windex entry:\n\t%s\n"), s
);
2156 * copy the string pointed to by lp
2162 * release memory in s
2163 * s has been allocated memory in bfsearch()
2170 * split section-specifier if file-name
2171 * suffix differs from section-suffix
2174 if ((p
= strchr(lp
, '/'))) {
2183 * Binary file search to find matching man
2184 * pages in whatis database.
2188 bfsearch(FILE *fp
, char **matchv
, char *key
, char **secv
)
2197 (void) fseek(fp
, 0L, 2);
2201 (void) fseek(fp
, mid
, 0);
2205 } while (c
!= EOF
&& c
!= '\n');
2206 if (fgets(entry
, sizeof (entry
), fp
) == NULL
)
2208 switch (compare(key
, entry
, secv
)) {
2223 (void) fseek(fp
, bot
, 0);
2224 while (ftell(fp
) < top
) {
2225 if (fgets(entry
, sizeof (entry
), fp
) == NULL
) {
2227 return (matchv
- vp
);
2229 switch (compare(key
, entry
, secv
)) {
2232 return (matchv
- vp
);
2235 *matchv
= strdup(entry
);
2236 if (*matchv
== NULL
)
2247 while (fgets(entry
, sizeof (entry
), fp
)) {
2248 switch (compare(key
, entry
, secv
)) {
2251 *matchv
= strdup(entry
);
2252 if (*matchv
== NULL
)
2261 return (matchv
- vp
);
2265 compare(char *key
, char *entry
, char **secv
)
2270 int mbcurmax
= MB_CUR_MAX
;
2275 entbuf
= strdup(entry
);
2276 if (entbuf
== NULL
) {
2279 eblen
= strlen(entbuf
);
2283 if (*s
== '\t' || *s
== ' ') {
2287 mlen
= mblen(s
, mbcurmax
);
2289 (void) fprintf(stderr
, gettext(
2290 "Invalid character in windex file.\n"));
2296 * Find the section within parantheses
2298 if (secv
!= NULL
&& (s
- entbuf
) < eblen
) {
2299 if ((secp
= strchr(s
+ 1, ')')) != NULL
) {
2301 if ((secp
= strchr(s
+ 1, '(')) != NULL
) {
2307 comp
= strcmp(key
, entbuf
);
2312 while (*secv
!= NULL
) {
2313 if ((strcmp(*secv
, secp
)) == 0) {
2320 } else if (comp
< 0) {
2331 * Format a man page and follow .so references
2336 format(char *path
, char *dir
, char *name
, char *pg
)
2338 char manpname
[MAXPATHLEN
+1], catpname
[MAXPATHLEN
+1];
2339 char manpname_sgml
[MAXPATHLEN
+1], smantmpname
[MAXPATHLEN
+1];
2340 char soed
[MAXPATHLEN
+1], soref
[MAXPATHLEN
+1];
2341 char manbuf
[BUFSIZ
], cmdbuf
[BUFSIZ
], tmpbuf
[BUFSIZ
];
2342 char tmpdir
[MAXPATHLEN
+1];
2343 int socount
, updatedcat
, regencat
;
2344 struct stat mansb
, catsb
, smansb
;
2352 int temp
, sgml_flag
= 0, check_flag
= 0;
2353 char prntbuf
[BUFSIZ
+ 1];
2360 if (*dir
!= 'm' && *dir
!= 's')
2365 tmpsubdir
= SGMLDIR
;
2367 (void) sprintf(manpname_sgml
, "%s/man%s/%s",
2368 path
, dir
+plen
, pg
);
2370 tmpsubdir
= MANDIRNAME
;
2373 (void) printf(gettext("%s (%s)\t-M %s\n"),
2374 name
, dir
+plen
, path
);
2378 (void) sprintf(manpname
, "%s/%s%s/%s", path
, tmpsubdir
, dir
+plen
, pg
);
2379 (void) sprintf(catpname
, "%s/%s%s/%s", path
, subdirs
[1], dir
+plen
, pg
);
2381 (void) sprintf(smantmpname
, "%s/%s%s/%s", path
, SGMLDIR
, dir
+plen
, pg
);
2384 * TRANSLATION_NOTE - message for man -d or catman -p
2385 * ex. unformatted = /usr/share/man/ja/man3s/printf.3s
2388 " unformatted = %s\n"), catonly
? "" : manpname
);
2390 * TRANSLATION_NOTE - message for man -d or catman -p
2391 * ex. formatted = /usr/share/man/ja/cat3s/printf.3s
2394 " formatted = %s\n"), catpname
);
2397 * Take care of indirect references to other man pages;
2398 * i.e., resolve files containing only ".so manx/file.x".
2399 * We follow .so chains, replacing title with the .so'ed
2400 * file at each stage, and keeping track of how many times
2401 * we've done so, so that we can avoid looping.
2414 * Grab manpname's first line, stashing it in manbuf.
2418 if ((md
= fopen(manpname
, "r")) == NULL
) {
2419 if (*soed
&& errno
== ENOENT
) {
2420 (void) fprintf(stderr
,
2421 gettext("Can't find referent of "
2422 ".so in %s\n"), soed
);
2423 (void) fflush(stderr
);
2431 * If this is a directory, just ignore it.
2433 if (fstat(fileno(md
), &statb
) == NULL
) {
2434 if (S_ISDIR(statb
.st_mode
)) {
2436 (void) fprintf(stderr
,
2437 "\tignoring directory %s\n",
2439 (void) fflush(stderr
);
2446 if (fgets(manbuf
, BUFSIZ
-1, md
) == NULL
) {
2448 (void) fprintf(stderr
, gettext("%s: null file\n"),
2450 (void) fflush(stderr
);
2455 if (strncmp(manbuf
, DOT_SO
, sizeof (DOT_SO
) - 1))
2457 so_again
: if (++socount
> SOLIMIT
) {
2458 (void) fprintf(stderr
, gettext(".so chain too long\n"));
2459 (void) fflush(stderr
);
2462 s
= manbuf
+ sizeof (DOT_SO
) - 1;
2463 if ((check_flag
== 1) && ((new_s
= strrchr(s
, '/')) != NULL
)) {
2465 (void) sprintf(s
, "%s%s/%s",
2466 tmpsubdir
, dir
+plen
, new_s
);
2469 cp
= strrchr(s
, '\n');
2473 * Compensate for sloppy typists by stripping
2474 * trailing white space.
2477 while (--cp
>= s
&& (*cp
== ' ' || *cp
== '\t'))
2481 * Go off and find the next link in the chain.
2483 (void) strcpy(soed
, manpname
);
2484 (void) strcpy(soref
, s
);
2485 (void) sprintf(manpname
, "%s/%s", path
, s
);
2487 * TRANSLATION_NOTE - message for man -d or catman -p
2488 * ex. .so ref = man3c/string.3c
2490 DPRINTF(gettext(".so ref = %s\n"), s
);
2494 * Make symlinks if so'ed and cattin'
2496 if (socount
&& catmando
) {
2497 (void) sprintf(cmdbuf
, "cd %s; rm -f %s; ln -s ../%s%s %s",
2498 path
, catpname
, subdirs
[1], soref
+plen
, catpname
);
2504 * Obtain the cat page that corresponds to the man page.
2505 * If it already exists, is up to date, and if we haven't
2506 * been told not to use it, use it as it stands.
2508 regencat
= updatedcat
= 0;
2509 if (compargs
|| (!catonly
&& stat(manpname
, &mansb
) >= 0 &&
2510 (stat(catpname
, &catsb
) < 0 || catsb
.st_mtime
< mansb
.st_mtime
)) ||
2511 (access(catpname
, R_OK
) != 0)) {
2513 * Construct a shell command line for formatting manpname.
2514 * The resulting file goes initially into /tmp. If possible,
2515 * it will later be moved to catpname.
2522 regencat
= updatedcat
= 1;
2524 if (!catmando
&& !debug
&& !check_flag
) {
2525 (void) fprintf(stderr
, gettext(
2526 "Reformatting page. Please Wait..."));
2527 if (sargs
&& (newsection
!= NULL
) &&
2528 (*newsection
!= '\0')) {
2529 (void) fprintf(stderr
, gettext(
2530 "\nThe directory name has been changed "
2531 "to %s\n"), newsection
);
2533 (void) fflush(stderr
);
2537 * in catman command, if the file exists in sman dir already,
2538 * don't need to convert the file in man dir to cat dir
2541 if (!no_sroff
&& catmando
&&
2542 match(tmpsubdir
, MANDIRNAME
, PLEN
) &&
2543 stat(smantmpname
, &smansb
) >= 0)
2547 * cd to path so that relative .so commands will work
2550 (void) sprintf(cbp
, "cd %s; ", path
);
2555 * check to see whether it is a sgml file
2556 * assume sgml symbol(>!DOCTYPE) can be found in the first
2560 if ((temp
= open(manpname
, 0)) == -1) {
2565 if ((count
= read(temp
, prntbuf
, BUFSIZ
)) <= 0) {
2570 prntbuf
[count
] = '\0'; /* null terminate */
2572 if (sgmlcheck((const char *)ptr
) == 1) {
2574 if (defaultmandir
&& *localedir
) {
2575 (void) sprintf(cbp
, "LC_MESSAGES=C %s %s ",
2576 SROFF_CMD
, manpname
);
2578 (void) sprintf(cbp
, "%s %s ",
2579 SROFF_CMD
, manpname
);
2582 } else if (*dir
== 's') {
2589 * Check for special formatting requirements by examining
2590 * manpname's first line preprocessor specifications.
2593 if (strncmp(manbuf
, PREPROC_SPEC
,
2594 sizeof (PREPROC_SPEC
) - 1) == 0) {
2597 ptp
= manbuf
+ sizeof (PREPROC_SPEC
) - 1;
2598 while (*ptp
&& *ptp
!= '\n') {
2599 const struct preprocessor
*pp
;
2602 * Check for a preprocessor we know about.
2604 for (pp
= preprocessors
; pp
->p_tag
; pp
++) {
2605 if (pp
->p_tag
== *ptp
)
2608 if (pp
->p_tag
== 0) {
2609 (void) fprintf(stderr
,
2610 gettext("unknown preprocessor "
2611 "specifier %c\n"), *ptp
);
2612 (void) fflush(stderr
);
2617 * Add it to the pipeline.
2619 (void) sprintf(cbp
, "%s %s |",
2620 troffit
? pp
->p_troff
: pp
->p_nroff
,
2621 pipestage
++ == 0 ? manpname
:
2626 * Special treatment: if tbl is among the
2627 * preprocessors and we'll process with
2628 * nroff, we have to pass things through
2629 * col at the end of the pipeline.
2631 if (pp
->p_tag
== 't' && !troffit
)
2639 * if catman, use the cat page name
2640 * otherwise, dup template and create another
2641 * (needed for multiple pages)
2646 tmpname
= strdup(TEMPLATE
);
2647 if (tmpname
== NULL
)
2649 (void) close(mkstemp(tmpname
));
2653 if (*localedir
!= '\0') {
2654 (void) sprintf(macros
, "%s/%s", path
, MACROF
);
2656 * TRANSLATION_NOTE - message for man -d or catman -p
2657 * ex. locale macros = /usr/share/man/ja/tmac.an
2660 (void) printf(gettext(
2661 "\nlocale macros = %s "),
2663 if (stat(macros
, &statb
) < 0)
2664 (void) strcpy(macros
, TMAC_AN
);
2666 * TRANSLATION_NOTE - message for man -d or catman -p
2667 * ex. macros = /usr/share/man/ja/tman.an
2670 (void) printf(gettext(
2677 if (sgml_flag
== 1) {
2678 if (check_flag
== 0) {
2679 strcpy(tmpdir
, "/tmp/sman_XXXXXX");
2680 if ((tempfd
= mkstemp(tmpdir
)) == -1) {
2681 (void) fprintf(stderr
, gettext(
2682 "%s: null file\n"), tmpdir
);
2683 (void) fflush(stderr
);
2690 (void) sprintf(tmpbuf
, "%s > %s",
2694 * TRANSLATION_NOTE - message for man -d or catman -p
2695 * Error message if sys(%s) failed
2697 (void) fprintf(stderr
, gettext(
2698 "sys(%s) fail!\n"), tmpbuf
);
2699 (void) fprintf(stderr
,
2700 gettext(" aborted (sorry)\n"));
2701 (void) fflush(stderr
);
2702 /* release memory for tmpname */
2704 (void) unlink(tmpdir
);
2705 (void) unlink(tmpname
);
2709 } else if (debug
== 0) {
2710 if ((md
= fdopen(tempfd
, "r"))
2712 (void) fprintf(stderr
, gettext(
2713 "%s: null file\n"), tmpdir
);
2714 (void) fflush(stderr
);
2716 /* release memory for tmpname */
2722 /* if the file is empty, */
2723 /* it's a fragment, do nothing */
2724 if (fgets(manbuf
, BUFSIZ
-1, md
)
2727 /* release memory for tmpname */
2734 if (strncmp(manbuf
, DOT_SO
,
2735 sizeof (DOT_SO
) - 1) == 0) {
2738 (void) unlink(tmpdir
);
2739 (void) unlink(tmpname
);
2740 /* release memory for tmpname */
2745 (void) unlink(tmpdir
);
2747 "/tmp/sman_XXXXXX");
2748 tempfd
= mkstemp(tmpdir
);
2749 if ((tempfd
== -1) ||
2750 (md
= fdopen(tempfd
, "w"))
2752 (void) fprintf(stderr
,
2756 (void) fflush(stderr
);
2759 /* release memory for tmpname */
2764 if ((new_m
= strrchr(manbuf
, '/')) != NULL
) {
2765 (void) fprintf(md
, ".so man%s%s\n", dir
+plen
, new_m
);
2768 * TRANSLATION_NOTE - message for catman -c
2769 * Error message if unable to get file name
2771 (void) fprintf(stderr
,
2772 gettext("file not found\n"));
2773 (void) fflush(stderr
);
2780 if (catmando
&& compargs
)
2781 (void) sprintf(cmdbuf
, "cat %s > %s",
2782 tmpdir
, manpname_sgml
);
2784 (void) sprintf(cmdbuf
, " cat %s | tbl | eqn | %s %s - %s > %s",
2785 tmpdir
, troffit
? troffcmd
: "nroff -u0 -Tlp",
2786 macros
, troffit
? "" : " | col -x", tmpname
);
2788 if (catmando
&& compargs
)
2789 (void) sprintf(cbp
, " > %s",
2792 (void) sprintf(cbp
, " | tbl | eqn | %s %s - %s > %s",
2793 troffit
? troffcmd
: "nroff -u0 -Tlp",
2794 macros
, troffit
? "" : " | col -x", tmpname
);
2797 (void) sprintf(cbp
, "%s %s %s%s > %s",
2798 troffit
? troffcmd
: "nroff -u0 -Tlp",
2799 macros
, pipestage
== 0 ? manpname
: "-",
2800 troffit
? "" : " | col -x", tmpname
);
2802 /* Reformat the page. */
2805 * TRANSLATION_NOTE - message for man -d or catman -p
2806 * Error message if sys(%s) failed
2808 (void) fprintf(stderr
, gettext(
2809 "sys(%s) fail!\n"), cmdbuf
);
2810 (void) fprintf(stderr
, gettext(" aborted (sorry)\n"));
2811 (void) fflush(stderr
);
2812 (void) unlink(tmpname
);
2813 /* release memory for tmpname */
2819 if (tmpdir
[0] != '\0')
2820 (void) unlink(tmpdir
);
2826 * Attempt to move the cat page to its proper home.
2828 (void) sprintf(cmdbuf
,
2829 "trap '' 1 15; /usr/bin/mv -f %s %s 2> /dev/null",
2834 else if (debug
== 0)
2835 (void) chmod(catpname
, 0644);
2838 /* release memory for tmpname */
2841 (void) unlink(tmpname
);
2845 (void) fprintf(stderr
, gettext(" done\n"));
2846 (void) fflush(stderr
);
2850 * Save file name (dup if necessary)
2852 * fix for 1123802 - don't save names if we are invoked as catman
2859 if (regencat
&& !updatedcat
)
2862 newpage
= strdup(catpname
);
2863 if (newpage
== NULL
)
2866 /* make sure we don't add a dup */
2868 for (tmpp
= pages
; tmpp
< endp
; tmpp
++) {
2869 if (strcmp(*tmpp
, newpage
) == 0) {
2876 if (endp
>= &pages
[MAXPAGES
]) {
2878 gettext("Internal pages array overflow!\n"));
2887 * Add <localedir> to the path.
2891 addlocale(char *path
)
2896 tmp
= malloc(strlen(path
) + strlen(localedir
) + 2);
2899 (void) sprintf(tmp
, "%s/%s", path
, localedir
);
2905 * From the configuration file "man.cf", get the order of suffices of
2906 * sub-mandirs to be used in the search path for a given mandir.
2910 check_config(char *path
)
2913 static char submandir
[BUFSIZ
];
2915 char fname
[MAXPATHLEN
];
2917 (void) sprintf(fname
, "%s/%s", path
, CONFIG
);
2919 if ((fp
= fopen(fname
, "r")) == NULL
)
2922 if (get_manconfig(fp
, submandir
) == -1) {
2929 sect
= strchr(submandir
, '=');
2938 * This routine is for getting the MANSECTS entry from man.cf.
2939 * It sets submandir to the line in man.cf that contains
2940 * MANSECTS=sections[,sections]...
2944 get_manconfig(FILE *fp
, char *submandir
)
2949 while ((rc
= fgets(buf
, sizeof (buf
), fp
)) != NULL
) {
2952 * skip leading blanks
2954 for (t
= buf
; *t
!= '\0'; t
++) {
2959 * skip line that starts with '#' or empty line
2961 if (*t
== '#' || *t
== '\0')
2964 if (strstr(buf
, "MANSECTS") != NULL
)
2969 * the man.cf file doesn't have a MANSECTS entry
2974 s
= strchr(buf
, '\n');
2975 *s
= '\0'; /* replace '\n' with '\0' */
2977 (void) strcpy(submandir
, buf
);
2984 (void) fprintf(stderr
, gettext(
2985 "Memory allocation failed.\n"));
2990 sgmlcheck(const char *s1
)
2992 const char *s2
= SGML_SYMBOL
;
2997 * Assume the first character of SGML_SYMBOL(*s2) is '<'.
2998 * Therefore, not necessary to do toupper(*s1) here.
3002 * *s1 is '<'. Check the following substring matches
3006 if (strncasecmp(s1
, s2
+ 1, SGML_SYMBOL_LEN
- 1)
3014 } else if (isascii(*s1
)) {
3016 * *s1 is an ASCII char
3017 * Skip one character
3023 * *s1 is a non-ASCII char or
3024 * the first byte of the multibyte char.
3025 * Skip one character
3027 len
= mblen(s1
, MB_CUR_MAX
);
3035 * SGML_SYMBOL not found
3041 * Initializes the bintoman array with appropriate device and inode info
3050 for (i
= 0; bintoman
[i
].bindir
!= NULL
; i
++) {
3051 if (stat(bintoman
[i
].bindir
, &sb
) == 0) {
3052 bintoman
[i
].dev
= sb
.st_dev
;
3053 bintoman
[i
].ino
= sb
.st_ino
;
3055 bintoman
[i
].dev
= NODEV
;
3061 * If a duplicate is found, return 1
3062 * If a duplicate is not found, add it to the dupnode list and return 0
3065 dupcheck(struct man_node
*mnp
, struct dupnode
**dnp
)
3067 struct dupnode
*curdnp
;
3068 struct secnode
*cursnp
;
3075 * If the path doesn't exist, treat it as a duplicate
3077 if (stat(mnp
->path
, &sb
) != 0) {
3082 * If no sections were found in the man dir, treat it as duplicate
3084 if (mnp
->secv
== NULL
) {
3089 * Find the dupnode structure for the previous time this directory
3090 * was looked at. Device and inode numbers are compared so that
3091 * directories that are reached via different paths (e.g. /usr/man vs.
3092 * /usr/share/man) are treated as equivalent.
3094 for (curdnp
= *dnp
; curdnp
!= NULL
; curdnp
= curdnp
->next
) {
3095 if (curdnp
->dev
== sb
.st_dev
&& curdnp
->ino
== sb
.st_ino
) {
3101 * First time this directory has been seen. Add a new node to the
3102 * head of the list. Since all entries are guaranteed to be unique
3103 * copy all sections to new node.
3105 if (curdnp
== NULL
) {
3106 if ((curdnp
= calloc(1, sizeof (struct dupnode
))) == NULL
) {
3109 for (i
= 0; mnp
->secv
[i
] != NULL
; i
++) {
3110 if ((cursnp
= calloc(1, sizeof (struct secnode
)))
3114 cursnp
->next
= curdnp
->secl
;
3115 curdnp
->secl
= cursnp
;
3116 if ((cursnp
->secp
= strdup(mnp
->secv
[i
])) == NULL
) {
3120 curdnp
->dev
= sb
.st_dev
;
3121 curdnp
->ino
= sb
.st_ino
;
3122 curdnp
->next
= *dnp
;
3128 * Traverse the section vector in the man_node and the section list
3129 * in dupnode cache to eliminate all duplicates from man_node
3131 for (i
= 0; mnp
->secv
[i
] != NULL
; i
++) {
3133 for (cursnp
= curdnp
->secl
; cursnp
!= NULL
;
3134 cursnp
= cursnp
->next
) {
3135 if (strcmp(mnp
->secv
[i
], cursnp
->secp
) == 0) {
3141 mnp
->secv
[i
][0] = '\0';
3147 * Update curdnp and set return value to indicate that this
3148 * was not all duplicates.
3150 if ((cursnp
= calloc(1, sizeof (struct secnode
))) == NULL
) {
3153 cursnp
->next
= curdnp
->secl
;
3154 curdnp
->secl
= cursnp
;
3155 if ((cursnp
->secp
= strdup(mnp
->secv
[i
])) == NULL
) {
3165 * Given a bin directory, return the corresponding man directory.
3166 * Return string must be free()d by the caller.
3168 * NULL will be returned if no matching man directory can be found.
3172 path_to_manpath(char *bindir
)
3179 * First look for known translations for specific bin paths
3181 if (stat(bindir
, &sb
) != 0) {
3184 for (i
= 0; bintoman
[i
].bindir
!= NULL
; i
++) {
3185 if (sb
.st_dev
== bintoman
[i
].dev
&&
3186 sb
.st_ino
== bintoman
[i
].ino
) {
3187 if ((mand
= strdup(bintoman
[i
].mandir
)) == NULL
) {
3190 if ((p
= strchr(mand
, ',')) != NULL
) {
3193 if (stat(mand
, &sb
) != 0) {
3205 * No specific translation found. Try `dirname $bindir`/man
3206 * and `dirname $bindir`/share/man
3208 if ((mand
= malloc(PATH_MAX
)) == NULL
) {
3212 if (strlcpy(mand
, bindir
, PATH_MAX
) >= PATH_MAX
) {
3218 * Advance to end of buffer, strip trailing /'s then remove last
3219 * directory component.
3221 for (p
= mand
; *p
!= '\0'; p
++)
3223 for (; p
> mand
&& *p
== '/'; p
--)
3225 for (; p
> mand
&& *p
!= '/'; p
--)
3227 if (p
== mand
&& *p
== '.') {
3228 if (realpath("..", mand
) == NULL
) {
3232 for (; *p
!= '\0'; p
++)
3238 if (strlcat(mand
, "/man", PATH_MAX
) >= PATH_MAX
) {
3243 if ((stat(mand
, &sb
) == 0) && S_ISDIR(sb
.st_mode
)) {
3248 * Strip the /man off and try /share/man
3251 if (strlcat(mand
, "/share/man", PATH_MAX
) >= PATH_MAX
) {
3255 if ((stat(mand
, &sb
) == 0) && S_ISDIR(sb
.st_mode
)) {
3260 * No man or share/man directory found
3267 * Free a linked list of dupnode structs
3270 free_dupnode(struct dupnode
*dnp
) {
3271 struct dupnode
*dnp2
;
3272 struct secnode
*snp
;
3274 while (dnp
!= NULL
) {
3277 while (dnp2
->secl
!= NULL
) {
3279 dnp2
->secl
= dnp2
->secl
->next
;
3288 * prints manp linked list to stdout.
3290 * If namep is NULL, output can be used for setting MANPATH.
3292 * If namep is not NULL output is two columns. First column is the string
3293 * pointed to by namep. Second column is a MANPATH-compatible representation
3294 * of manp linked list.
3297 print_manpath(struct man_node
*manp
, char *namep
)
3302 if (namep
!= NULL
) {
3303 (void) printf("%s ", namep
);
3309 for (; manp
!= NULL
; manp
= manp
->next
) {
3310 (void) printf("%s%s", colon
, manp
->path
);
3314 * If man.cf or a directory scan was used to create section
3315 * list, do not print section list again. If the output of
3316 * man -p is used to set MANPATH, subsequent runs of man
3317 * will re-read man.cf and/or scan man directories as
3320 if (manp
->defsrch
!= 0) {
3324 for (secp
= manp
->secv
; *secp
!= NULL
; secp
++) {
3326 * Section deduplication may have eliminated some
3327 * sections from the vector. Avoid displaying this
3328 * detail which would appear as ",," in output
3330 if ((*secp
)[0] != '\0') {
3331 (void) printf(",%s", *secp
);
3335 (void) printf("\n");