2075 man outputs "geqn should have been given a `-Tutf8' option"
[illumos-gate.git] / usr / src / cmd / man / src / man.c
blobf272ecab6edf2f3c703594078961b3f825663426
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
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
32 * All Rights Reserved
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
41 * man
42 * links to apropos, whatis, and catman
43 * This version uses more for underlining and paging.
46 #include <stdio.h>
47 #include <ctype.h>
48 #include <sgtty.h>
49 #include <sys/param.h>
50 #include <sys/types.h>
51 #include <sys/stat.h>
52 #include <signal.h>
53 #include <string.h>
54 #include <malloc.h>
55 #include <dirent.h>
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <locale.h>
59 #include <stdlib.h>
60 #include <unistd.h>
61 #include <memory.h>
62 #include <limits.h>
63 #include <wchar.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 */
98 #define MAXTOKENS 64
100 #define DOT_SO ".so "
101 #define PREPROC_SPEC "'\\\" "
103 #define DPRINTF if (debug && !catmando) \
104 (void) printf
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
122 typedef struct {
123 char *old_name;
124 char *new_name;
125 } map_entry;
127 static const map_entry map[] = {
128 { "3b", "3ucb" },
129 { "3e", "3elf" },
130 { "3g", "3gen" },
131 { "3k", "3kstat" },
132 { "3n", "3socket" },
133 { "3r", "3rt" },
134 { "3s", "3c" },
135 { "3t", "3thr" },
136 { "3x", "3curses" },
137 { "3xc", "3xcurses" },
138 { "3xn", "3xnet" }
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:
145 * '\" X
146 * where X is a string consisting of letters from the p_tag fields
147 * below.
149 static const struct preprocessor {
150 char p_tag;
151 char *p_nroff,
152 *p_troff,
153 *p_stdin_char;
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", "-"},
162 {0, 0, 0, 0}
165 struct suffix {
166 char *ds;
167 char *fs;
171 * Flags that control behavior of build_manpath()
173 * BMP_ISPATH pathv is a vector constructed from PATH.
174 * Perform appropriate path translations for
175 * manpath.
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.
182 #define BMP_ISPATH 1
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.
191 struct secnode {
192 char *secp;
193 struct secnode *next;
195 struct dupnode {
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
204 * man directory
206 static struct pathmap {
207 char *bindir;
208 char *mandir;
209 dev_t dev;
210 ino_t ino;
211 } bintoman[] = {
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},
223 {NULL, NULL, 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 };
236 #define MAN_USAGE "\
237 usage:\tman [-] [-adFlprt] [-M path] [-T macro-package ] [ -s section ] \
238 name ...\n\
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 */
248 struct man_node {
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;
260 * flags (options)
262 static int nomore;
263 static int troffit;
264 static int debug;
265 static int Tflag;
266 static int sargs;
267 static int margs;
268 static int force;
269 static int found;
270 static int list;
271 static int all;
272 static int whatis;
273 static int apropos;
274 static int catmando;
275 static int nowhatis;
276 static int whatonly;
277 static int compargs; /* -c option for catman */
278 static int printmp;
280 static char *CAT = CAT_;
281 static char macros[MAXPATHLEN];
282 static char *mansec;
283 static char *pager;
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[])
352 int badopts = 0;
353 int c;
354 char **pathv;
355 char *cmdname;
356 char *manpath = NULL;
357 static struct man_node *manpage = NULL;
358 int bmp_flags = 0;
359 int err = 0;
361 if (access(SROFF_CMD, F_OK | X_OK) != 0)
362 no_sroff = 1;
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"
371 #endif
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)
380 cmdname++;
381 else
382 cmdname = argv[0];
384 if (eq(cmdname, "apropos") || eq(cmdname, "whatis")) {
385 whatis++;
386 apropos = (*cmdname == 'a');
387 if ((optind = 1) == argc) {
388 (void) fprintf(stderr, gettext("%s what?\n"), cmdname);
389 exit(2);
391 goto doargs;
392 } else if (eq(cmdname, "catman"))
393 catmando++;
395 opterr = 0;
396 while ((c = getopt(argc, argv, opts[catmando])) != -1)
397 switch (c) {
400 * man specific options
402 case 'k':
403 apropos++;
404 /*FALLTHROUGH*/
405 case 'f':
406 whatis++;
407 break;
408 case 'F':
409 force++; /* do lookups the hard way */
410 break;
411 case 's':
412 mansec = optarg;
413 sargs++;
414 break;
415 case 'r':
416 nomore++, troffit++;
417 break;
418 case 'l':
419 list++; /* implies all */
420 /*FALLTHROUGH*/
421 case 'a':
422 all++;
423 break;
424 case 'd':
425 debug++;
426 break;
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.
432 case 'p':
433 if (catmando == 0) {
434 printmp++;
435 } else {
436 debug++;
438 break;
439 case 'n':
440 nowhatis++;
441 break;
442 case 'w':
443 whatonly++;
444 break;
445 case 'c': /* n|troff compatibility */
446 if (no_sroff)
447 (void) fprintf(stderr, gettext(
448 "catman: SGML conversion not "
449 "available -- -c flag ignored\n"));
450 else
451 compargs++;
452 continue;
455 * shared options
457 case 'P': /* Backwards compatibility */
458 case 'M': /* Respecify path for man pages. */
459 manpath = optarg;
460 margs++;
461 break;
462 case 'T': /* Respecify man macros */
463 (void) strcpy(macros, optarg);
464 Tflag++;
465 break;
466 case 't':
467 troffit++;
468 break;
469 case '?':
470 badopts++;
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));
480 exit(2);
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"));
487 exit(2);
490 if (sargs && margs && catmando) {
491 (void) fprintf(stderr, "%s\n", gettext(CATMAN_USAGE));
492 exit(2);
495 if (troffit == 0 && nomore == 0 && !isatty(fileno(stdout)))
496 nomore++;
499 * Collect environment information.
501 if (troffit) {
502 if ((troffcmd = getenv("TROFF")) == NULL)
503 troffcmd = TROFF;
504 if ((troffcat = getenv("TCAT")) == NULL)
505 troffcat = TCAT;
506 } else {
507 if (((pager = getenv("PAGER")) == NULL) ||
508 (*pager == NULL))
509 pager = MORE;
512 doargs:
513 subdirs = troffit ? troffdirs : nroffdirs;
515 init_bintoman();
517 if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) {
518 if ((manpath = getenv("PATH")) != NULL) {
519 bmp_flags = BMP_ISPATH | BMP_APPEND_MANDIR;
520 } else {
521 manpath = MANDIR;
525 pathv = split(manpath, ':');
527 manpage = build_manpath(pathv, bmp_flags);
529 /* release pathv allocated by split() */
530 freev(pathv);
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) {
537 perror("putenv");
538 exit(1);
541 fullpaths(&manpage);
543 if (catmando) {
544 catman(manpage, argv+optind, argc-optind);
545 exit(0);
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);
564 exit(0);
567 for (; optind < argc; optind++) {
568 if (strcmp(argv[optind], "-") == 0) {
569 nomore++;
570 CAT = CAT_S;
571 } else {
572 char *cmd;
573 static struct man_node *mp;
574 char *pv[2];
577 * If full path to command specified, customize
578 * manpath accordingly
580 if ((cmd = strrchr(argv[optind], '/')) != NULL) {
581 *cmd = '\0';
582 if ((pv[0] = strdup(argv[optind])) == NULL) {
583 malloc_error();
585 pv[1] = NULL;
586 *cmd = '/';
587 mp = build_manpath(pv,
588 BMP_ISPATH|BMP_FALLBACK_MANDIR);
589 } else {
590 mp = manpage;
593 if (whatis) {
594 whatapro(mp, argv[optind], apropos);
595 } else if (printmp != 0) {
596 print_manpath(mp, argv[optind]);
597 } else {
598 err += manual(mp, argv[optind]);
601 if (mp != NULL && mp != manpage) {
602 free(pv[0]);
603 free_manp(mp);
607 return (err == 0 ? 0 : 1);
608 /*NOTREACHED*/
612 * This routine builds the manpage structure from MANPATH or PATH,
613 * depending on flags. See BMP_* definitions above for valid
614 * flags.
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;
626 char **p;
627 char **q;
628 char *mand = NULL;
629 char *mandir = MANDIR;
630 int s;
631 struct dupnode *didup = NULL;
632 struct stat sb;
634 s = sizeof (struct man_node);
635 for (p = pathv; *p; ) {
637 if (flags & BMP_ISPATH) {
638 if ((mand = path_to_manpath(*p)) == NULL) {
639 goto next;
641 free(*p);
642 *p = mand;
644 q = split(*p, ',');
645 if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) {
646 freev(q);
647 goto next;
650 if (access(q[0], R_OK|X_OK) != 0) {
651 if (catmando) {
652 (void) fprintf(stderr,
653 gettext("%s is not accessible.\n"),
654 q[0]);
655 (void) fflush(stderr);
657 } else {
660 * Some element exists. Do not append MANDIR as a
661 * fallback.
663 flags &= ~BMP_FALLBACK_MANDIR;
665 if ((currp = (struct man_node *)calloc(1, s)) == NULL) {
666 malloc_error();
669 currp->frompath = (flags & BMP_ISPATH);
671 if (manpage == NULL) {
672 lastp = manpage = currp;
675 getpath(currp, p);
676 getsect(currp, p);
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) {
683 freev(currp->secv);
684 free(currp);
685 } else {
686 currp->next = NULL;
687 if (currp != manpage) {
688 lastp->next = currp;
690 lastp = currp;
693 freev(q);
694 next:
696 * Special handling of appending MANDIR.
697 * After all pathv elements have been processed, append MANDIR
698 * if needed.
700 if (p == &mandir) {
701 break;
703 p++;
704 if (*p != NULL) {
705 continue;
707 if (flags & (BMP_APPEND_MANDIR|BMP_FALLBACK_MANDIR)) {
708 p = &mandir;
709 flags &= ~BMP_ISPATH;
713 free_dupnode(didup);
715 return (manpage);
719 * Stores the mandir path into the manp structure.
722 static void
723 getpath(struct man_node *manp, char **pv)
725 char *s;
726 int i = 0;
728 s = *pv;
730 while (*s != NULL && *s != ',')
731 i++, s++;
733 manp->path = (char *)malloc(i+1);
734 if (manp->path == NULL)
735 malloc_error();
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.
745 static void
746 getsect(struct man_node *manp, char **pv)
748 char *sections;
749 char **sectp;
751 if (sargs) {
752 manp->secv = split(mansec, ',');
754 for (sectp = manp->secv; *sectp; sectp++)
755 lower(*sectp);
756 } else if ((sections = strchr(*pv, ',')) != NULL) {
757 if (debug) {
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);
766 } else {
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++)
778 lower(*sectp);
780 if (*manp->secv == NULL)
781 get_all_sect(manp);
782 } else if ((sections = check_config(*pv)) != NULL) {
783 manp->defsrch = 1;
785 * TRANSLATION_NOTE - message for man -d or catman -p
786 * ex. /usr/share/man: from man.cf, MANSECTS=1,1m,1c
788 if (debug)
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++)
795 lower(*sectp);
797 if (*manp->secv == NULL)
798 get_all_sect(manp);
799 } else {
800 manp->defsrch = 1;
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.
806 if (debug)
807 (void) fprintf(stdout, gettext(
808 "%s: search the sections lexicographically\n"),
809 manp->path);
810 manp->secv = NULL;
811 get_all_sect(manp);
816 * Get suffices of all sub-mandir directories in a mandir.
819 static void
820 get_all_sect(struct man_node *manp)
822 DIR *dp;
823 char **dirv;
824 char **dv;
825 char **p;
826 char *prev = NULL;
827 char *tmp = NULL;
828 int plen;
829 int maxentries = MAXTOKENS;
830 int entries = 0;
832 if ((dp = opendir(manp->path)) == 0)
833 return;
836 * sortdir() allocates memory for dirv and dirv[].
838 sortdir(dp, &dirv);
840 (void) closedir(dp);
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)
848 malloc_error();
851 for (dv = dirv, p = manp->secv; *dv; dv++) {
852 plen = PLEN;
853 if (match(*dv, SGMLDIR, PLEN+1))
854 ++plen;
856 if (strcmp(*dv, CONFIG) == 0) {
857 /* release memory allocated by sortdir */
858 free(*dv);
859 continue;
862 if (tmp != NULL)
863 free(tmp);
864 tmp = strdup(*dv + plen);
865 if (tmp == NULL)
866 malloc_error();
867 (void) sprintf(tmp, "%s", *dv + plen);
869 if (prev != NULL) {
870 if (strcmp(prev, tmp) == 0) {
871 /* release memory allocated by sortdir */
872 free(*dv);
873 continue;
877 if (prev != NULL)
878 free(prev);
879 prev = strdup(*dv + plen);
880 if (prev == NULL)
881 malloc_error();
882 (void) sprintf(prev, "%s", *dv + plen);
884 * copy the string in (*dv + plen) to *p
886 *p = strdup(*dv + plen);
887 if (*p == NULL)
888 malloc_error();
889 p++;
890 entries++;
891 if (entries == maxentries) {
892 maxentries += MAXTOKENS;
893 manp->secv = (char **)realloc(manp->secv,
894 sizeof (char *) * maxentries);
895 if (manp->secv == NULL)
896 malloc_error();
897 p = manp->secv + entries;
899 /* release memory allocated by sortdir */
900 free(*dv);
902 *p = 0;
903 /* release memory allocated by sortdir */
904 free(dirv);
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.
922 static void
923 catman(struct man_node *manp, char **argv, int argc)
925 char cmdbuf[BUFSIZ];
926 char **dv;
927 int changed;
928 struct man_node *p;
929 int ndirs = 0;
930 char *ldir;
931 int i;
932 struct dupnode *dnp = NULL;
933 char **realsecv;
935 * May be overwritten in dupcheck() so must be kept out of .rodata.
937 char fakename[] = " catman ";
938 char *fakesecv[2];
940 fakesecv[0] = fakename;
941 fakesecv[1] = NULL;
943 for (p = manp; p != NULL; p = p->next) {
945 * prevent catman from doing very heavy lifting multiple
946 * times on some directory
948 realsecv = p->secv;
949 p->secv = fakesecv;
950 if (dupcheck(p, &dnp) != 0) {
951 p->secv = realsecv;
952 continue;
956 * TRANSLATION_NOTE - message for catman -p
957 * ex. mandir path = /usr/share/man
959 if (debug)
960 (void) fprintf(stdout, gettext(
961 "\nmandir path = %s\n"), p->path);
962 ndirs = 0;
965 * Build cat pages
966 * addlocale() allocates memory and returns it
968 ldir = addlocale(p->path);
969 if (!whatonly) {
970 if (*localedir != '\0') {
971 if (defaultmandir)
972 defaultmandir = 0;
973 /* getdirs allocate memory for dv */
974 ndirs = getdirs(ldir, &dv, 1);
975 if (ndirs != 0) {
976 changed = argc ?
977 makecat(ldir, argv, argc) :
978 makecat(ldir, dv, ndirs);
979 /* release memory by getdirs */
980 for (i = 0; i < ndirs; i++) {
981 free(dv[i]);
983 free(dv);
987 /* default man dir is always processed */
988 defaultmandir = 1;
989 ndirs = getdirs(p->path, &dv, 1);
990 changed = argc ?
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++) {
995 free(dv[i]);
997 free(dv);
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",
1010 MAKEWHATIS, ldir);
1011 (void) sys(cmdbuf);
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);
1019 (void) sys(cmdbuf);
1021 /* release memory allocated by addlocale() */
1022 free(ldir);
1024 free_dupnode(dnp);
1028 * Build cat pages for given sections
1031 static int
1032 makecat(char *path, char **dv, int ndirs)
1034 DIR *dp, *sdp;
1035 struct dirent *d;
1036 struct stat sbuf;
1037 char mandir[MAXPATHLEN+1];
1038 char smandir[MAXPATHLEN+1];
1039 char catdir[MAXPATHLEN+1];
1040 char *dirp, *sdirp;
1041 int i, fmt;
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)
1057 manflag = 1;
1059 if (!no_sroff && (sdp = opendir(smandir)) != NULL)
1060 smanflag = 1;
1062 if (dp == 0 && sdp == 0) {
1063 if (strcmp(mandir, CONFIG) == 0)
1064 perror(mandir);
1065 continue;
1068 * TRANSLATION_NOTE - message for catman -p
1069 * ex. Building cat pages for mandir = /usr/share/man/ja
1071 if (debug)
1072 (void) fprintf(stdout, gettext(
1073 "Building cat pages for mandir = %s\n"), path);
1075 if (!compargs && stat(catdir, &sbuf) < 0) {
1076 (void) umask(02);
1078 * TRANSLATION_NOTE - message for catman -p
1079 * ex. mkdir /usr/share/man/ja/cat3c
1081 if (debug)
1082 (void) fprintf(stdout, gettext("mkdir %s\n"),
1083 catdir);
1084 else {
1085 if (mkdir(catdir, 0755) < 0) {
1086 perror(catdir);
1087 continue;
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,
1096 * make the man dir
1099 if (compargs && !manflag) {
1100 if (mkdir(mandir, 0755) < 0) {
1101 perror(mandir);
1102 continue;
1104 (void) chmod(mandir, 0755);
1107 if (smanflag) {
1108 while ((d = readdir(sdp))) {
1109 if (eq(".", d->d_name) || eq("..", d->d_name))
1110 continue;
1112 if (format(path, sdirp, (char *)0, d->d_name)
1113 > 0)
1114 fmt++;
1118 if (manflag && !compargs) {
1119 while ((d = readdir(dp))) {
1120 if (eq(".", d->d_name) || eq("..", d->d_name))
1121 continue;
1123 if (format(path, dirp, (char *)0, d->d_name)
1124 > 0)
1125 fmt++;
1129 if (manflag)
1130 (void) closedir(dp);
1132 if (smanflag)
1133 (void) closedir(sdp);
1136 return (fmt);
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
1146 static int
1147 getdirs(char *path, char ***dirv, short flag)
1149 DIR *dp;
1150 struct dirent *d;
1151 int n = 0;
1152 int plen, sgml_flag, man_flag;
1153 int i = 0;
1154 int maxentries = MAXDIRS;
1155 char **dv;
1157 if ((dp = opendir(path)) == 0) {
1158 if (debug) {
1159 if (*localedir != '\0')
1160 (void) printf(gettext("\
1161 locale is %s, search in %s\n"), localedir, path);
1162 perror(path);
1164 return (0);
1167 if (flag) {
1168 /* allocate memory for dirv */
1169 *dirv = (char **)malloc(sizeof (char *) *
1170 maxentries);
1171 if (*dirv == NULL)
1172 malloc_error();
1173 dv = *dirv;
1175 while ((d = readdir(dp))) {
1176 plen = PLEN;
1177 man_flag = sgml_flag = 0;
1178 if (match(d->d_name, SGMLDIR, PLEN+1)) {
1179 plen = PLEN + 1;
1180 sgml_flag = 1;
1181 i++;
1184 if (match(subdirs[0], d->d_name, PLEN))
1185 man_flag = 1;
1187 if (compargs && sgml_flag) {
1188 if (flag) {
1189 *dv = strdup(d->d_name+plen);
1190 if (*dv == NULL)
1191 malloc_error();
1192 dv++;
1193 n = i;
1195 } else if (!compargs && (sgml_flag || man_flag)) {
1196 if (flag) {
1197 *dv = strdup(d->d_name+plen);
1198 if (*dv == NULL)
1199 malloc_error();
1200 dv++;
1202 n++;
1204 if (flag) {
1205 if ((dv - *dirv) == maxentries) {
1206 int entries = maxentries;
1207 maxentries += MAXTOKENS;
1208 *dirv = (char **)realloc(*dirv,
1209 sizeof (char *) * maxentries);
1210 if (*dirv == NULL)
1211 malloc_error();
1212 dv = *dirv + entries;
1217 (void) closedir(dp);
1218 return (n);
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).
1229 static void
1230 whatapro(struct man_node *manp, char *word, int apropos)
1232 char whatpath[MAXPATHLEN+1];
1233 char *p;
1234 struct man_node *b;
1235 int ndirs = 0;
1236 char *ldir;
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
1248 if (!apropos) {
1249 if ((p = strrchr(word, '/')) == NULL)
1250 p = word;
1251 else
1252 p++;
1253 } else {
1254 p = word;
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);
1262 if (defaultmandir)
1263 defaultmandir = 0;
1264 ndirs = getdirs(ldir, NULL, 0);
1265 if (ndirs != 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() */
1275 free(ldir);
1278 defaultmandir = 1;
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);
1291 static void
1292 lookup_windex(char *whatpath, char *word, char **secv)
1294 FILE *fp;
1295 char *matches[MAXPAGES];
1296 char **pp;
1297 wchar_t wbuf[BUFSIZ];
1298 wchar_t *word_wchar = NULL;
1299 wchar_t *ws;
1300 size_t word_len, ret;
1302 if ((fp = fopen(whatpath, "r")) == NULL) {
1303 perror(whatpath);
1304 return;
1307 if (apropos) {
1308 word_len = strlen(word) + 1;
1309 if ((word_wchar = (wchar_t *)malloc(sizeof (wchar_t) *
1310 word_len)) == NULL) {
1311 malloc_error();
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"));
1317 exit(1);
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);
1323 break;
1325 } else {
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()
1333 free(*pp);
1336 (void) fclose(fp);
1337 if (word_wchar)
1338 free(word_wchar);
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.
1351 static int
1352 icmp(wchar_t *ws, wchar_t *wt)
1354 for (; (*ws == 0) ||
1355 (*ws == (iswupper(*ws) ? *wt: towlower(*wt)));
1356 ws++, wt++)
1357 if (*ws == 0)
1358 return (0);
1360 return (1);
1365 * Invoke PAGER with all matching man pages
1368 static void
1369 more(char **pages, int plain)
1371 char cmdbuf[BUFSIZ];
1372 char **vp;
1375 * Dont bother.
1377 if (list || (*pages == 0))
1378 return;
1380 if (plain && troffit) {
1381 cleanup(pages);
1382 return;
1384 (void) sprintf(cmdbuf, "%s", troffit ? troffcat :
1385 plain ? CAT : pager);
1388 * Build arg list
1390 for (vp = pages; vp < endp; vp++) {
1391 (void) strcat(cmdbuf, " ");
1392 (void) strcat(cmdbuf, *vp);
1394 (void) sys(cmdbuf);
1395 cleanup(pages);
1400 * Get rid of dregs.
1403 static void
1404 cleanup(char **pages)
1406 char **vp;
1408 for (vp = pages; vp < endp; vp++) {
1409 if (match(TEMPLATE, *vp, TMPLEN))
1410 (void) unlink(*vp);
1411 free(*vp);
1414 endp = pages; /* reset */
1419 * Clean things up after receiving a signal.
1422 /*ARGSUSED*/
1423 static void
1424 bye(int sig)
1426 cleanup(pages);
1427 exit(1);
1428 /*NOTREACHED*/
1433 * Split a string by specified separator.
1434 * ignore empty components/adjacent separators.
1435 * returns vector to all tokens
1438 static char **
1439 split(char *s1, char sep)
1441 char **tokv, **vp;
1442 char *mp, *tp;
1443 int maxentries = MAXTOKENS;
1444 int entries = 0;
1446 tokv = vp = (char **)malloc(maxentries * sizeof (char *));
1447 if (tokv == NULL)
1448 malloc_error();
1449 mp = s1;
1450 for (; mp && *mp; mp = tp) {
1451 tp = strchr(mp, sep);
1452 if (mp == tp) { /* empty component */
1453 tp++; /* ignore */
1454 continue;
1456 if (tp) {
1457 /* a component found */
1458 size_t len;
1460 len = tp - mp;
1461 *vp = (char *)malloc(sizeof (char) * len + 1);
1462 if (*vp == NULL)
1463 malloc_error();
1464 (void) strncpy(*vp, mp, len);
1465 *(*vp + len) = '\0';
1466 tp++;
1467 vp++;
1468 } else {
1469 /* the last component */
1470 *vp = strdup(mp);
1471 if (*vp == NULL)
1472 malloc_error();
1473 vp++;
1475 entries++;
1476 if (entries == maxentries) {
1477 maxentries += MAXTOKENS;
1478 tokv = (char **)realloc(tokv,
1479 maxentries * sizeof (char *));
1480 if (tokv == NULL)
1481 malloc_error();
1482 vp = tokv + entries;
1485 *vp = 0;
1486 return (tokv);
1490 * Free a vector allocated by split();
1492 static void
1493 freev(char **v)
1495 int i;
1496 if (v != NULL) {
1497 for (i = 0; v[i] != NULL; i++) {
1498 free(v[i]);
1500 free(v);
1505 * Convert paths to full paths if necessary
1509 static void
1510 fullpaths(struct man_node **manp_head)
1512 char *cwd = NULL;
1513 char *p;
1514 char cwd_gotten = 0;
1515 struct man_node *manp = *manp_head;
1516 struct man_node *b;
1517 struct man_node *prev = NULL;
1519 for (b = manp; b != NULL; b = b->next) {
1520 if (*(b->path) == '/') {
1521 prev = b;
1522 continue;
1525 /* try to get cwd if haven't already */
1526 if (!cwd_gotten) {
1527 cwd = getcwd(NULL, MAXPATHLEN+1);
1528 cwd_gotten = 1;
1531 if (cwd) {
1532 /* case: relative manpath with cwd: make absolute */
1533 if ((p = malloc(strlen(b->path)+strlen(cwd)+2)) ==
1534 NULL) {
1535 malloc_error();
1537 (void) sprintf(p, "%s/%s", cwd, b->path);
1539 * resetting b->path
1541 free(b->path);
1542 b->path = p;
1543 } else {
1544 /* case: relative manpath but no cwd: omit path entry */
1545 if (prev)
1546 prev->next = b->next;
1547 else
1548 *manp_head = b->next;
1550 free_manp(b);
1554 * release memory allocated by getcwd()
1556 free(cwd);
1560 * Free a man_node structure and its contents
1563 static void
1564 free_manp(struct man_node *manp)
1566 char **p;
1568 free(manp->path);
1569 p = manp->secv;
1570 while ((p != NULL) && (*p != NULL)) {
1571 free(*p);
1572 p++;
1574 free(manp->secv);
1575 free(manp);
1580 * Map (in place) to lower case
1583 static void
1584 lower(char *s)
1586 if (s == 0)
1587 return;
1588 while (*s) {
1589 if (isupper(*s))
1590 *s = tolower(*s);
1591 s++;
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}
1603 static int
1604 cmp(const void *arg1, const void *arg2)
1606 int n;
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))))
1614 return (n);
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.
1628 static int
1629 manual(struct man_node *manp, char *name)
1631 struct man_node *p;
1632 struct man_node *local;
1633 int ndirs = 0;
1634 char *ldir;
1635 char *ldirs[2];
1636 char *fullname = name;
1637 char *slash;
1639 if ((slash = strrchr(name, '/')) != NULL) {
1640 name = slash + 1;
1644 * for each path in MANPATH
1646 found = 0;
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);
1658 if (defaultmandir)
1659 defaultmandir = 0;
1661 * TRANSLATION_NOTE - message for man -d
1662 * ex. localedir = ja, ldir = /usr/share/man/ja
1664 if (debug)
1665 (void) printf(gettext(
1666 "localedir = %s, ldir = %s\n"),
1667 localedir, ldir);
1668 ndirs = getdirs(ldir, NULL, 0);
1669 if (ndirs != 0) {
1670 ldirs[0] = ldir;
1671 ldirs[1] = NULL;
1672 local = build_manpath(ldirs, 0);
1673 if (force ||
1674 windex(local->secv, ldir, name) < 0)
1675 mandir(local->secv, ldir, name);
1676 free_manp(local);
1678 /* release memory allocated by addlocale() */
1679 free(ldir);
1682 defaultmandir = 1;
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);
1692 if (found && !all)
1693 break;
1696 if (found) {
1697 more(pages, nomore);
1698 } else {
1699 if (sargs) {
1700 (void) fprintf(stderr, gettext("No entry for %s in "
1701 "section(s) %s of the manual.\n"),
1702 fullname, mansec);
1703 } else {
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"),
1711 fullname, mansec);
1713 sman_no_man_no_sroff = 0;
1714 return (!found);
1719 * For a specified manual directory,
1720 * read, store, & sort section subdirs,
1721 * for each section specified
1722 * find and search matching subdirs
1725 static void
1726 mandir(char **secv, char *path, char *name)
1728 DIR *dp;
1729 char **dirv;
1730 char **dv, **pdv;
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
1738 if (debug)
1739 (void) fprintf(stdout, gettext(
1740 " opendir on %s failed\n"), path);
1741 return;
1745 * TRANSLATION_NOTE - message for man -d or catman -p
1746 * ex. mandir path = /usr/share/man/ja
1748 if (debug)
1749 (void) printf(gettext("mandir path = %s\n"), path);
1752 * sordir() allocates memory for dirv and dirv[].
1754 sortdir(dp, &dirv);
1756 * Search in the order specified by MANSECTS
1758 for (; *secv; secv++) {
1760 * TRANSLATION_NOTE - message for man -d or catman -p
1761 * ex. section = 3c
1763 DPRINTF(gettext(" section = %s\n"), *secv);
1764 len = strlen(*secv);
1765 for (dv = dirv; *dv; dv++) {
1766 plen = PLEN;
1767 if (*dv[0] == 's')
1768 plen++;
1769 dslen = strlen(*dv+plen);
1770 if (dslen > len)
1771 len = dslen;
1772 if (**secv == '\\') {
1773 if (!eq(*secv + 1, *dv+plen))
1774 continue;
1775 } else if (strncasecmp(*secv, *dv+plen, len) != 0) {
1776 /* check to see if directory name changed */
1777 if (!all &&
1778 (newsection = map_section(*secv, path))
1779 == NULL) {
1780 continue;
1782 if (newsection == NULL)
1783 newsection = "";
1784 if (!match(newsection, *dv+plen, len)) {
1785 continue;
1789 if (searchdir(path, *dv, name) == 0)
1790 continue;
1792 if (!all) {
1793 /* release memory allocated by sortdir() */
1794 pdv = dirv;
1795 while (*pdv) {
1796 free(*pdv);
1797 pdv++;
1799 (void) closedir(dp);
1800 /* release memory allocated by sortdir() */
1801 free(dirv);
1802 return;
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))
1810 dv++;
1813 /* release memory allocated by sortdir() */
1814 pdv = dirv;
1815 while (*pdv) {
1816 free(*pdv);
1817 pdv++;
1819 free(dirv);
1820 (void) closedir(dp);
1824 * Sort directories.
1827 static void
1828 sortdir(DIR *dp, char ***dirv)
1830 struct dirent *d;
1831 char **dv;
1832 int maxentries = MAXDIRS;
1833 int entries = 0;
1835 *dirv = (char **)malloc(sizeof (char *) * maxentries);
1836 dv = *dirv;
1837 while ((d = readdir(dp))) { /* store dirs */
1838 if (eq(d->d_name, ".") || eq(d->d_name, "..")) /* ignore */
1839 continue;
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);
1846 if (*dv == NULL)
1847 malloc_error();
1848 (void) strcpy(*dv, d->d_name);
1849 dv++;
1850 entries++;
1851 if (entries == maxentries) {
1852 maxentries += MAXDIRS;
1853 *dirv = (char **)realloc(*dirv,
1854 sizeof (char *) * maxentries);
1855 if (*dirv == NULL)
1856 malloc_error();
1857 dv = *dirv + entries;
1861 *dv = 0;
1863 qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
1869 * Search a section subdirectory for a
1870 * given man page, return 1 for success
1873 static int
1874 searchdir(char *path, char *dir, char *name)
1876 DIR *sdp;
1877 struct dirent *sd;
1878 char sectpath[MAXPATHLEN+1];
1879 char file[MAXNAMLEN+1];
1880 char dname[MAXPATHLEN+1];
1881 char *last;
1882 int nlen;
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 */
1894 perror(sectpath);
1895 return (0);
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;
1904 return (0);
1906 (void) format(path, dir, name, sd->d_name);
1907 (void) closedir(sdp);
1908 return (1);
1911 (void) closedir(sdp);
1912 return (0);
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
1919 * directory exists.
1920 * Otherwise returns NULL
1923 static char *
1924 map_section(char *section, char *path)
1926 int i;
1927 int len;
1928 char fullpath[MAXPATHLEN];
1930 if (list) /* -l option fall through */
1931 return (NULL);
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);
1936 } else {
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);
1944 } else {
1945 return (NULL);
1950 return (NULL);
1955 * Use windex database for quick lookup of man pages
1956 * instead of mandir() (brute force search)
1959 static int
1960 windex(char **secv, char *path, char *name)
1962 FILE *fp;
1963 struct stat sbuf;
1964 struct suffix *sp;
1965 struct suffix psecs[MAXPAGES+1];
1966 char whatfile[MAXPATHLEN+1];
1967 char page[MAXPATHLEN+1];
1968 char *matches[MAXPAGES];
1969 char *file, *dir;
1970 char **sv, **vp;
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)
1979 return (-1);
1980 return (0);
1984 * TRANSLATION_NOTE - message for man -d or catman -p
1985 * ex. search in = /usr/share/man/ja/windex file
1987 if (debug)
1988 (void) fprintf(stdout, gettext(
1989 " search in = %s file\n"), whatfile);
1991 if (bfsearch(fp, matches, name, NULL) == 0) {
1992 (void) fclose(fp);
1993 return (-1); /* force search in mandir */
1996 (void) fclose(fp);
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) {
2004 section(sp, *vp);
2005 } else {
2006 if (debug)
2007 (void) fprintf(stderr, gettext(
2008 "too many sections in %s windex entry\n"),
2009 name);
2011 /* Setting sp->ds to NULL signifies end-of-data. */
2012 sp->ds = 0;
2013 goto finish;
2017 sp->ds = 0;
2020 * Search in the order specified
2021 * by MANSECTS
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
2030 if (debug)
2031 (void) fprintf(stdout, gettext(
2032 " search an entry to match %s.%s\n"), name, *secv);
2034 * For every whatis entry that
2035 * was matched
2037 for (sp = psecs; sp->ds; sp++) {
2038 dslen = strlen(sp->ds);
2039 if (dslen > len)
2040 len = dslen;
2041 if (**secv == '\\') {
2042 if (!eq(*secv + 1, sp->ds))
2043 continue;
2044 } else if (!match(*secv, sp->ds, len)) {
2045 /* check to see if directory name changed */
2046 if (!all &&
2047 (newsection = map_section(*secv, path))
2048 == NULL) {
2049 continue;
2051 if (newsection == NULL)
2052 newsection = "";
2053 if (!match(newsection, sp->ds, len)) {
2054 continue;
2058 * here to form "sman", "man", "cat"|"fmt" in
2059 * order
2061 if (!no_sroff) {
2062 tmp[0] = SGMLDIR;
2063 for (i = 1; i < 4; i++)
2064 tmp[i] = subdirs[i-1];
2065 } else {
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 ? "." : "",
2074 sp->fs);
2075 exist = (stat(page, &sbuf) == 0);
2076 if (exist)
2077 break;
2079 if (!exist) {
2080 (void) fprintf(stderr, gettext(
2081 "%s entry incorrect: %s(%s) not found.\n"),
2082 WHATIS, name, sp->ds);
2083 continue;
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);
2095 if (!all)
2096 goto finish;
2099 finish:
2101 * release memory allocated by section()
2103 sp = psecs;
2104 while (sp->ds) {
2105 free(sp->ds);
2106 sp->ds = NULL;
2107 sp++;
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)
2129 return (0);
2130 else
2131 return (-1);
2136 * Return pointers to the section-spec
2137 * and file-suffix of a whatis entry
2140 static void
2141 section(struct suffix *sp, char *s)
2143 char *lp, *p;
2145 lp = strchr(s, '(');
2146 p = strchr(s, ')');
2148 if (++lp == 0 || p == 0 || lp == p) {
2149 (void) fprintf(stderr,
2150 gettext("mangled windex entry:\n\t%s\n"), s);
2151 return;
2153 *p = 0;
2156 * copy the string pointed to by lp
2158 lp = strdup(lp);
2159 if (lp == NULL)
2160 malloc_error();
2162 * release memory in s
2163 * s has been allocated memory in bfsearch()
2165 free(s);
2167 lower(lp);
2170 * split section-specifier if file-name
2171 * suffix differs from section-suffix
2173 sp->ds = lp;
2174 if ((p = strchr(lp, '/'))) {
2175 *p++ = 0;
2176 sp->fs = p;
2177 } else
2178 sp->fs = lp;
2183 * Binary file search to find matching man
2184 * pages in whatis database.
2187 static int
2188 bfsearch(FILE *fp, char **matchv, char *key, char **secv)
2190 char entry[BUFSIZ];
2191 char **vp;
2192 long top, bot, mid;
2193 int c;
2195 vp = matchv;
2196 bot = 0;
2197 (void) fseek(fp, 0L, 2);
2198 top = ftell(fp);
2199 for (;;) {
2200 mid = (top+bot)/2;
2201 (void) fseek(fp, mid, 0);
2202 do {
2203 c = getc(fp);
2204 mid++;
2205 } while (c != EOF && c != '\n');
2206 if (fgets(entry, sizeof (entry), fp) == NULL)
2207 break;
2208 switch (compare(key, entry, secv)) {
2209 case -2:
2210 case -1:
2211 case 0:
2212 if (top <= mid)
2213 break;
2214 top = mid;
2215 continue;
2216 case 1:
2217 case 2:
2218 bot = mid;
2219 continue;
2221 break;
2223 (void) fseek(fp, bot, 0);
2224 while (ftell(fp) < top) {
2225 if (fgets(entry, sizeof (entry), fp) == NULL) {
2226 *matchv = 0;
2227 return (matchv - vp);
2229 switch (compare(key, entry, secv)) {
2230 case -2:
2231 *matchv = 0;
2232 return (matchv - vp);
2233 case -1:
2234 case 0:
2235 *matchv = strdup(entry);
2236 if (*matchv == NULL)
2237 malloc_error();
2238 else
2239 matchv++;
2240 break;
2241 case 1:
2242 case 2:
2243 continue;
2245 break;
2247 while (fgets(entry, sizeof (entry), fp)) {
2248 switch (compare(key, entry, secv)) {
2249 case -1:
2250 case 0:
2251 *matchv = strdup(entry);
2252 if (*matchv == NULL)
2253 malloc_error();
2254 else
2255 matchv++;
2256 continue;
2258 break;
2260 *matchv = 0;
2261 return (matchv - vp);
2264 static int
2265 compare(char *key, char *entry, char **secv)
2267 char *entbuf;
2268 char *s;
2269 int comp, mlen;
2270 int mbcurmax = MB_CUR_MAX;
2271 char *secp = NULL;
2272 int rv;
2273 int eblen;
2275 entbuf = strdup(entry);
2276 if (entbuf == NULL) {
2277 malloc_error();
2279 eblen = strlen(entbuf);
2281 s = entbuf;
2282 while (*s) {
2283 if (*s == '\t' || *s == ' ') {
2284 *s = '\0';
2285 break;
2287 mlen = mblen(s, mbcurmax);
2288 if (mlen == -1) {
2289 (void) fprintf(stderr, gettext(
2290 "Invalid character in windex file.\n"));
2291 exit(1);
2293 s += mlen;
2296 * Find the section within parantheses
2298 if (secv != NULL && (s - entbuf) < eblen) {
2299 if ((secp = strchr(s + 1, ')')) != NULL) {
2300 *secp = '\0';
2301 if ((secp = strchr(s + 1, '(')) != NULL) {
2302 secp++;
2307 comp = strcmp(key, entbuf);
2308 if (comp == 0) {
2309 if (secp == NULL) {
2310 rv = 0;
2311 } else {
2312 while (*secv != NULL) {
2313 if ((strcmp(*secv, secp)) == 0) {
2314 rv = 0;
2315 break;
2317 secv++;
2320 } else if (comp < 0) {
2321 rv = -2;
2322 } else {
2323 rv = 2;
2325 free(entbuf);
2326 return (rv);
2331 * Format a man page and follow .so references
2332 * if necessary.
2335 static int
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;
2345 char *tmpname;
2346 int catonly = 0;
2347 struct stat statb;
2348 int plen = PLEN;
2349 FILE *md;
2350 int tempfd;
2351 ssize_t count;
2352 int temp, sgml_flag = 0, check_flag = 0;
2353 char prntbuf[BUFSIZ + 1];
2354 char *ptr;
2355 char *new_m;
2356 char *tmpsubdir;
2358 found++;
2360 if (*dir != 'm' && *dir != 's')
2361 catonly++;
2364 if (*dir == 's') {
2365 tmpsubdir = SGMLDIR;
2366 ++plen;
2367 (void) sprintf(manpname_sgml, "%s/man%s/%s",
2368 path, dir+plen, pg);
2369 } else
2370 tmpsubdir = MANDIRNAME;
2372 if (list) {
2373 (void) printf(gettext("%s (%s)\t-M %s\n"),
2374 name, dir+plen, path);
2375 return (-1);
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
2387 DPRINTF(gettext(
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
2393 DPRINTF(gettext(
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.
2403 *soed = 0;
2404 socount = 0;
2405 for (;;) {
2406 FILE *md;
2407 char *cp;
2408 char *s;
2409 char *new_s;
2411 if (catonly)
2412 break;
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);
2424 return (-1);
2426 perror(manpname);
2427 return (-1);
2431 * If this is a directory, just ignore it.
2433 if (fstat(fileno(md), &statb) == NULL) {
2434 if (S_ISDIR(statb.st_mode)) {
2435 if (debug) {
2436 (void) fprintf(stderr,
2437 "\tignoring directory %s\n",
2438 manpname);
2439 (void) fflush(stderr);
2441 (void) fclose(md);
2442 return (-1);
2446 if (fgets(manbuf, BUFSIZ-1, md) == NULL) {
2447 (void) fclose(md);
2448 (void) fprintf(stderr, gettext("%s: null file\n"),
2449 manpname);
2450 (void) fflush(stderr);
2451 return (-1);
2453 (void) fclose(md);
2455 if (strncmp(manbuf, DOT_SO, sizeof (DOT_SO) - 1))
2456 break;
2457 so_again: if (++socount > SOLIMIT) {
2458 (void) fprintf(stderr, gettext(".so chain too long\n"));
2459 (void) fflush(stderr);
2460 return (-1);
2462 s = manbuf + sizeof (DOT_SO) - 1;
2463 if ((check_flag == 1) && ((new_s = strrchr(s, '/')) != NULL)) {
2464 new_s++;
2465 (void) sprintf(s, "%s%s/%s",
2466 tmpsubdir, dir+plen, new_s);
2469 cp = strrchr(s, '\n');
2470 if (cp)
2471 *cp = '\0';
2473 * Compensate for sloppy typists by stripping
2474 * trailing white space.
2476 cp = s + strlen(s);
2477 while (--cp >= s && (*cp == ' ' || *cp == '\t'))
2478 *cp = '\0';
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);
2499 (void) sys(cmdbuf);
2500 return (1);
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.
2518 int pipestage = 0;
2519 int needcol = 0;
2520 char *cbp = cmdbuf;
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)
2544 return (1);
2547 * cd to path so that relative .so commands will work
2548 * correctly
2550 (void) sprintf(cbp, "cd %s; ", path);
2551 cbp += strlen(cbp);
2555 * check to see whether it is a sgml file
2556 * assume sgml symbol(>!DOCTYPE) can be found in the first
2557 * BUFSIZ bytes
2560 if ((temp = open(manpname, 0)) == -1) {
2561 perror(manpname);
2562 return (-1);
2565 if ((count = read(temp, prntbuf, BUFSIZ)) <= 0) {
2566 perror(manpname);
2567 return (-1);
2570 prntbuf[count] = '\0'; /* null terminate */
2571 ptr = prntbuf;
2572 if (sgmlcheck((const char *)ptr) == 1) {
2573 sgml_flag = 1;
2574 if (defaultmandir && *localedir) {
2575 (void) sprintf(cbp, "LC_MESSAGES=C %s %s ",
2576 SROFF_CMD, manpname);
2577 } else {
2578 (void) sprintf(cbp, "%s %s ",
2579 SROFF_CMD, manpname);
2581 cbp += strlen(cbp);
2582 } else if (*dir == 's') {
2583 (void) close(temp);
2584 return (-1);
2586 (void) close(temp);
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) {
2595 char *ptp;
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)
2606 break;
2608 if (pp->p_tag == 0) {
2609 (void) fprintf(stderr,
2610 gettext("unknown preprocessor "
2611 "specifier %c\n"), *ptp);
2612 (void) fflush(stderr);
2613 return (-1);
2617 * Add it to the pipeline.
2619 (void) sprintf(cbp, "%s %s |",
2620 troffit ? pp->p_troff : pp->p_nroff,
2621 pipestage++ == 0 ? manpname :
2622 pp->p_stdin_char);
2623 cbp += strlen(cbp);
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)
2632 needcol++;
2634 ptp++;
2639 * if catman, use the cat page name
2640 * otherwise, dup template and create another
2641 * (needed for multiple pages)
2643 if (catmando)
2644 tmpname = catpname;
2645 else {
2646 tmpname = strdup(TEMPLATE);
2647 if (tmpname == NULL)
2648 malloc_error();
2649 (void) close(mkstemp(tmpname));
2652 if (! Tflag) {
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
2659 if (debug)
2660 (void) printf(gettext(
2661 "\nlocale macros = %s "),
2662 macros);
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
2669 if (debug)
2670 (void) printf(gettext(
2671 "\nmacros = %s\n"),
2672 macros);
2676 tmpdir[0] = '\0';
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);
2684 return (-1);
2687 if (debug)
2688 close(tempfd);
2690 (void) sprintf(tmpbuf, "%s > %s",
2691 cmdbuf, tmpdir);
2692 if (sys(tmpbuf)) {
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 */
2703 if (!catmando) {
2704 (void) unlink(tmpdir);
2705 (void) unlink(tmpname);
2706 free(tmpname);
2708 return (-1);
2709 } else if (debug == 0) {
2710 if ((md = fdopen(tempfd, "r"))
2711 == NULL) {
2712 (void) fprintf(stderr, gettext(
2713 "%s: null file\n"), tmpdir);
2714 (void) fflush(stderr);
2715 close(tempfd);
2716 /* release memory for tmpname */
2717 if (!catmando)
2718 free(tmpname);
2719 return (-1);
2722 /* if the file is empty, */
2723 /* it's a fragment, do nothing */
2724 if (fgets(manbuf, BUFSIZ-1, md)
2725 == NULL) {
2726 (void) fclose(md);
2727 /* release memory for tmpname */
2728 if (!catmando)
2729 free(tmpname);
2730 return (1);
2732 (void) fclose(md);
2734 if (strncmp(manbuf, DOT_SO,
2735 sizeof (DOT_SO) - 1) == 0) {
2736 if (!compargs) {
2737 check_flag = 1;
2738 (void) unlink(tmpdir);
2739 (void) unlink(tmpname);
2740 /* release memory for tmpname */
2741 if (!catmando)
2742 free(tmpname);
2743 goto so_again;
2744 } else {
2745 (void) unlink(tmpdir);
2746 strcpy(tmpdir,
2747 "/tmp/sman_XXXXXX");
2748 tempfd = mkstemp(tmpdir);
2749 if ((tempfd == -1) ||
2750 (md = fdopen(tempfd, "w"))
2751 == NULL) {
2752 (void) fprintf(stderr,
2753 gettext(
2754 "%s: null file\n"),
2755 tmpdir);
2756 (void) fflush(stderr);
2757 if (tempfd != -1)
2758 close(tempfd);
2759 /* release memory for tmpname */
2760 if (!catmando)
2761 free(tmpname);
2762 return (-1);
2764 if ((new_m = strrchr(manbuf, '/')) != NULL) {
2765 (void) fprintf(md, ".so man%s%s\n", dir+plen, new_m);
2766 } else {
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);
2774 return (-1);
2776 (void) fclose(md);
2780 if (catmando && compargs)
2781 (void) sprintf(cmdbuf, "cat %s > %s",
2782 tmpdir, manpname_sgml);
2783 else
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);
2787 } else
2788 if (catmando && compargs)
2789 (void) sprintf(cbp, " > %s",
2790 manpname_sgml);
2791 else
2792 (void) sprintf(cbp, " | tbl | eqn | %s %s - %s > %s",
2793 troffit ? troffcmd : "nroff -u0 -Tlp",
2794 macros, troffit ? "" : " | col -x", tmpname);
2796 } else
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. */
2803 if (sys(cmdbuf)) {
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 */
2814 if (!catmando)
2815 free(tmpname);
2816 return (-1);
2819 if (tmpdir[0] != '\0')
2820 (void) unlink(tmpdir);
2822 if (catmando)
2823 return (1);
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",
2830 tmpname,
2831 catpname);
2832 if (sys(cmdbuf))
2833 updatedcat = 0;
2834 else if (debug == 0)
2835 (void) chmod(catpname, 0644);
2837 if (debug) {
2838 /* release memory for tmpname */
2839 if (!catmando)
2840 free(tmpname);
2841 (void) unlink(tmpname);
2842 return (1);
2845 (void) fprintf(stderr, gettext(" done\n"));
2846 (void) fflush(stderr);
2850 * Save file name (dup if necessary)
2851 * to view later
2852 * fix for 1123802 - don't save names if we are invoked as catman
2854 if (!catmando) {
2855 char **tmpp;
2856 int dup;
2857 char *newpage;
2859 if (regencat && !updatedcat)
2860 newpage = tmpname;
2861 else {
2862 newpage = strdup(catpname);
2863 if (newpage == NULL)
2864 malloc_error();
2866 /* make sure we don't add a dup */
2867 dup = 0;
2868 for (tmpp = pages; tmpp < endp; tmpp++) {
2869 if (strcmp(*tmpp, newpage) == 0) {
2870 dup = 1;
2871 break;
2874 if (!dup)
2875 *endp++ = newpage;
2876 if (endp >= &pages[MAXPAGES]) {
2877 fprintf(stderr,
2878 gettext("Internal pages array overflow!\n"));
2879 exit(1);
2883 return (regencat);
2887 * Add <localedir> to the path.
2890 static char *
2891 addlocale(char *path)
2894 char *tmp;
2896 tmp = malloc(strlen(path) + strlen(localedir) + 2);
2897 if (tmp == NULL)
2898 malloc_error();
2899 (void) sprintf(tmp, "%s/%s", path, localedir);
2900 return (tmp);
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.
2909 static char *
2910 check_config(char *path)
2912 FILE *fp;
2913 static char submandir[BUFSIZ];
2914 char *sect;
2915 char fname[MAXPATHLEN];
2917 (void) sprintf(fname, "%s/%s", path, CONFIG);
2919 if ((fp = fopen(fname, "r")) == NULL)
2920 return (NULL);
2921 else {
2922 if (get_manconfig(fp, submandir) == -1) {
2923 (void) fclose(fp);
2924 return (NULL);
2927 (void) fclose(fp);
2929 sect = strchr(submandir, '=');
2930 if (sect != NULL)
2931 return (++sect);
2932 else
2933 return (NULL);
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]...
2943 static int
2944 get_manconfig(FILE *fp, char *submandir)
2946 char *s, *t, *rc;
2947 char buf[BUFSIZ];
2949 while ((rc = fgets(buf, sizeof (buf), fp)) != NULL) {
2952 * skip leading blanks
2954 for (t = buf; *t != '\0'; t++) {
2955 if (!isspace(*t))
2956 break;
2959 * skip line that starts with '#' or empty line
2961 if (*t == '#' || *t == '\0')
2962 continue;
2964 if (strstr(buf, "MANSECTS") != NULL)
2965 break;
2969 * the man.cf file doesn't have a MANSECTS entry
2971 if (rc == NULL)
2972 return (-1);
2974 s = strchr(buf, '\n');
2975 *s = '\0'; /* replace '\n' with '\0' */
2977 (void) strcpy(submandir, buf);
2978 return (0);
2981 static void
2982 malloc_error(void)
2984 (void) fprintf(stderr, gettext(
2985 "Memory allocation failed.\n"));
2986 exit(1);
2989 static int
2990 sgmlcheck(const char *s1)
2992 const char *s2 = SGML_SYMBOL;
2993 int len;
2995 while (*s1) {
2997 * Assume the first character of SGML_SYMBOL(*s2) is '<'.
2998 * Therefore, not necessary to do toupper(*s1) here.
3000 if (*s1 == *s2) {
3002 * *s1 is '<'. Check the following substring matches
3003 * with "!DOCTYPE".
3005 s1++;
3006 if (strncasecmp(s1, s2 + 1, SGML_SYMBOL_LEN - 1)
3007 == 0) {
3009 * SGML_SYMBOL found
3011 return (1);
3013 continue;
3014 } else if (isascii(*s1)) {
3016 * *s1 is an ASCII char
3017 * Skip one character
3019 s1++;
3020 continue;
3021 } else {
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);
3028 if (len == -1)
3029 len = 1;
3030 s1 += len;
3031 continue;
3035 * SGML_SYMBOL not found
3037 return (0);
3041 * Initializes the bintoman array with appropriate device and inode info
3044 static void
3045 init_bintoman(void)
3047 int i;
3048 struct stat sb;
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;
3054 } else {
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
3064 static int
3065 dupcheck(struct man_node *mnp, struct dupnode **dnp)
3067 struct dupnode *curdnp;
3068 struct secnode *cursnp;
3069 struct stat sb;
3070 int i;
3071 int rv = 1;
3072 int dupfound;
3075 * If the path doesn't exist, treat it as a duplicate
3077 if (stat(mnp->path, &sb) != 0) {
3078 return (1);
3082 * If no sections were found in the man dir, treat it as duplicate
3084 if (mnp->secv == NULL) {
3085 return (1);
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) {
3096 break;
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) {
3107 malloc_error();
3109 for (i = 0; mnp->secv[i] != NULL; i++) {
3110 if ((cursnp = calloc(1, sizeof (struct secnode)))
3111 == NULL) {
3112 malloc_error();
3114 cursnp->next = curdnp->secl;
3115 curdnp->secl = cursnp;
3116 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) {
3117 malloc_error();
3120 curdnp->dev = sb.st_dev;
3121 curdnp->ino = sb.st_ino;
3122 curdnp->next = *dnp;
3123 *dnp = curdnp;
3124 return (0);
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++) {
3132 dupfound = 0;
3133 for (cursnp = curdnp->secl; cursnp != NULL;
3134 cursnp = cursnp->next) {
3135 if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
3136 dupfound = 1;
3137 break;
3140 if (dupfound) {
3141 mnp->secv[i][0] = '\0';
3142 continue;
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) {
3151 malloc_error();
3153 cursnp->next = curdnp->secl;
3154 curdnp->secl = cursnp;
3155 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) {
3156 malloc_error();
3158 rv = 0;
3161 return (rv);
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.
3171 static char *
3172 path_to_manpath(char *bindir)
3174 char *mand, *p;
3175 int i;
3176 struct stat sb;
3179 * First look for known translations for specific bin paths
3181 if (stat(bindir, &sb) != 0) {
3182 return (NULL);
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) {
3188 malloc_error();
3190 if ((p = strchr(mand, ',')) != NULL) {
3191 *p = '\0';
3193 if (stat(mand, &sb) != 0) {
3194 free(mand);
3195 return (NULL);
3197 if (p != NULL) {
3198 *p = ',';
3200 return (mand);
3205 * No specific translation found. Try `dirname $bindir`/man
3206 * and `dirname $bindir`/share/man
3208 if ((mand = malloc(PATH_MAX)) == NULL) {
3209 malloc_error();
3212 if (strlcpy(mand, bindir, PATH_MAX) >= PATH_MAX) {
3213 free(mand);
3214 return (NULL);
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) {
3229 free(mand);
3230 return (NULL);
3232 for (; *p != '\0'; p++)
3234 } else {
3235 *p = '\0';
3238 if (strlcat(mand, "/man", PATH_MAX) >= PATH_MAX) {
3239 free(mand);
3240 return (NULL);
3243 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
3244 return (mand);
3248 * Strip the /man off and try /share/man
3250 *p = '\0';
3251 if (strlcat(mand, "/share/man", PATH_MAX) >= PATH_MAX) {
3252 free(mand);
3253 return (NULL);
3255 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
3256 return (mand);
3260 * No man or share/man directory found
3262 free(mand);
3263 return (NULL);
3267 * Free a linked list of dupnode structs
3269 void
3270 free_dupnode(struct dupnode *dnp) {
3271 struct dupnode *dnp2;
3272 struct secnode *snp;
3274 while (dnp != NULL) {
3275 dnp2 = dnp;
3276 dnp = dnp->next;
3277 while (dnp2->secl != NULL) {
3278 snp = dnp2->secl;
3279 dnp2->secl = dnp2->secl->next;
3280 free(snp->secp);
3281 free(snp);
3283 free(dnp2);
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.
3296 void
3297 print_manpath(struct man_node *manp, char *namep)
3299 char colon[2];
3300 char **secp;
3302 if (namep != NULL) {
3303 (void) printf("%s ", namep);
3306 colon[0] = '\0';
3307 colon[1] = '\0';
3309 for (; manp != NULL; manp = manp->next) {
3310 (void) printf("%s%s", colon, manp->path);
3311 colon[0] = ':';
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
3318 * required.
3320 if (manp->defsrch != 0) {
3321 continue;
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");