1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved.
25 * Copyright 2013 Nexenta Systems, Inc. All rights reserved.
26 */
27
28 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. */
29 /* All rights reserved. */
30
31 /*
32 * University Copyright- Copyright (c) 1982, 1986, 1988
33 * The Regents of the University of California
34 * All Rights Reserved
35 *
36 * University Acknowledgment- Portions of this document are derived from
37 * software developed by the University of California, Berkeley, and its
38 * contributors.
39 */
40
41 /*
42 * Find and display reference manual pages. This version includes makewhatis
43 * functionality as well.
44 */
45
46 #include <sys/param.h>
47 #include <sys/stat.h>
48 #include <sys/termios.h>
49 #include <sys/types.h>
50
51 #include <ctype.h>
52 #include <dirent.h>
53 #include <err.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <fnmatch.h>
57 #include <limits.h>
58 #include <locale.h>
59 #include <malloc.h>
60 #include <memory.h>
61 #include <regex.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <unistd.h>
66
67 #include "man.h"
68
69
70 /* Mapping of old directories to new directories */
71 static const struct map_entry {
72 char *old_name;
73 char *new_name;
74 } map[] = {
75 { "3b", "3ucb" },
76 { "3e", "3elf" },
77 { "3g", "3gen" },
78 { "3k", "3kstat" },
79 { "3n", "3socket" },
80 { "3r", "3rt" },
81 { "3s", "3c" },
82 { "3t", "3thr" },
83 { "3x", "3curses" },
84 { "3xc", "3xcurses" },
85 { "3xn", "3xnet" }
86 };
87
88 struct suffix {
89 char *ds;
90 char *fs;
91 };
92
93 /*
94 * Flags that control behavior of build_manpath()
95 *
96 * BMP_ISPATH pathv is a vector constructed from PATH.
97 * Perform appropriate path translations for
98 * manpath.
99 * BMP_APPEND_DEFMANDIR Add DEFMANDIR to the end if it hasn't
100 * already appeared earlier.
101 * BMP_FALLBACK_DEFMANDIR Append /usr/share/man only if no other
102 * manpath (including derived from PATH)
103 * elements are valid.
104 */
105 #define BMP_ISPATH 1
106 #define BMP_APPEND_DEFMANDIR 2
107 #define BMP_FALLBACK_DEFMANDIR 4
108
109 /*
110 * When doing equality comparisons of directories, device and inode
111 * comparisons are done. The secnode and dupnode structures are used
112 * to form a list of lists for this processing.
113 */
114 struct secnode {
115 char *secp;
116 struct secnode *next;
117 };
118 struct dupnode {
119 dev_t dev; /* from struct stat st_dev */
120 ino_t ino; /* from struct stat st_ino */
121 struct secnode *secl; /* sections already considered */
122 struct dupnode *next;
123 };
124
125 /*
126 * Map directories that may appear in PATH to the corresponding
127 * man directory.
128 */
129 static struct pathmap {
130 char *bindir;
131 char *mandir;
132 dev_t dev;
133 ino_t ino;
134 } bintoman[] = {
135 { "/sbin", "/usr/share/man,1m", 0, 0 },
136 { "/usr/sbin", "/usr/share/man,1m", 0, 0 },
137 { "/usr/ucb", "/usr/share/man,1b", 0, 0 },
138 { "/usr/bin", "/usr/share/man,1,1m,1s,1t,1c", 0, 0 },
139 { "/usr/xpg4/bin", "/usr/share/man,1", 0, 0 },
140 { "/usr/xpg6/bin", "/usr/share/man,1", 0, 0 },
141 { NULL, NULL, 0, 0 }
142 };
143
144 struct man_node {
145 char *path; /* mandir path */
146 char **secv; /* submandir suffices */
147 int defsrch; /* hint for man -p */
148 int frompath; /* hint for man -d */
149 struct man_node *next;
150 };
151
152 static int all = 0;
153 static int apropos = 0;
154 static int debug = 0;
155 static int found = 0;
156 static int list = 0;
157 static int makewhatis = 0;
158 static int printmp = 0;
159 static int sargs = 0;
160 static int psoutput = 0;
161 static int whatis = 0;
162 static int makewhatishere = 0;
163
164 static char *mansec;
165 static char *pager;
166
167 static char *addlocale(char *);
168 static struct man_node *build_manpath(char **, int);
169 static void do_makewhatis(struct man_node *);
170 static char *check_config(char *);
171 static int cmp(const void *, const void *);
172 static int dupcheck(struct man_node *, struct dupnode **);
173 static int format(char *, char *, char *, char *);
174 static void free_dupnode(struct dupnode *);
175 static void free_manp(struct man_node *manp);
176 static void freev(char **);
177 static void fullpaths(struct man_node **);
178 static void get_all_sect(struct man_node *);
179 static int getdirs(char *, char ***, int);
180 static void getpath(struct man_node *, char **);
181 static void getsect(struct man_node *, char **);
182 static void init_bintoman(void);
183 static void lower(char *);
184 static void mandir(char **, char *, char *, int);
185 static int manual(struct man_node *, char *);
186 static char *map_section(char *, char *);
187 static char *path_to_manpath(char *);
188 static void print_manpath(struct man_node *);
189 static void search_whatis(char *, char *);
190 static int searchdir(char *, char *, char *);
191 static void sortdir(DIR *, char ***);
192 static char **split(char *, char);
193 static void usage_man(void);
194 static void usage_whatapro(void);
195 static void usage_catman(void);
196 static void usage_makewhatis(void);
197 static void whatapro(struct man_node *, char *);
198
199 static char language[MAXPATHLEN]; /* LC_MESSAGES */
200 static char localedir[MAXPATHLEN]; /* locale specific path component */
201
202 static char *newsection = NULL;
203
204 static int manwidth = 0;
205
206 extern const char *__progname;
207
208 int
209 main(int argc, char **argv)
210 {
211 int c, i;
212 char **pathv;
213 char *manpath = NULL;
214 static struct man_node *mandirs = NULL;
215 int bmp_flags = 0;
216 int ret = 0;
217 char *opts;
218 char *mwstr;
219 int catman = 0;
220
221 (void) setlocale(LC_ALL, "");
222 (void) strcpy(language, setlocale(LC_MESSAGES, (char *)NULL));
223 if (strcmp("C", language) != 0)
224 (void) strlcpy(localedir, language, MAXPATHLEN);
225
226 #if !defined(TEXT_DOMAIN)
227 #define TEXT_DOMAIN "SYS_TEST"
228 #endif
229 (void) textdomain(TEXT_DOMAIN);
230
231 if (strcmp(__progname, "apropos") == 0) {
232 apropos++;
233 opts = "M:ds:";
234 } else if (strcmp(__progname, "whatis") == 0) {
235 apropos++;
236 whatis++;
237 opts = "M:ds:";
238 } else if (strcmp(__progname, "catman") == 0) {
239 catman++;
240 makewhatis++;
241 opts = "M:w";
242 } else if (strcmp(__progname, "makewhatis") == 0) {
243 makewhatis++;
244 makewhatishere++;
245 manpath = ".";
246 opts = "";
247 } else {
248 opts = "M:adfklps:tw";
249 }
250
251 opterr = 0;
252 while ((c = getopt(argc, argv, opts)) != -1) {
253 switch (c) {
254 case 'M': /* Respecify path for man pages */
255 manpath = optarg;
256 break;
257 case 'a':
258 all++;
259 break;
260 case 'd':
261 debug++;
262 break;
263 case 'f':
264 whatis++;
265 /*FALLTHROUGH*/
266 case 'k':
267 apropos++;
268 break;
269 case 'l':
270 list++;
271 /*FALLTHROUGH*/
272 case 'p':
273 printmp++;
274 break;
275 case 's':
276 mansec = optarg;
277 sargs++;
278 break;
279 case 't':
280 psoutput++;
281 break;
282 case 'w':
283 makewhatis++;
284 break;
285 case '?':
286 default:
287 if (apropos)
288 usage_whatapro();
289 else if (catman)
290 usage_catman();
291 else if (makewhatishere)
292 usage_makewhatis();
293 else
294 usage_man();
295 }
296 }
297 argc -= optind;
298 argv += optind;
299
300 if (argc == 0) {
301 if (apropos) {
302 (void) fprintf(stderr, gettext("%s what?\n"),
303 __progname);
304 exit(1);
305 } else if (!printmp && !makewhatis) {
306 (void) fprintf(stderr,
307 gettext("What manual page do you want?\n"));
308 exit(1);
309 }
310 }
311
312 init_bintoman();
313 if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) {
314 if ((manpath = getenv("PATH")) != NULL)
315 bmp_flags = BMP_ISPATH | BMP_APPEND_DEFMANDIR;
316 else
317 manpath = DEFMANDIR;
318 }
319 pathv = split(manpath, ':');
320 mandirs = build_manpath(pathv, bmp_flags);
321 freev(pathv);
322 fullpaths(&mandirs);
323
324 if (makewhatis) {
325 do_makewhatis(mandirs);
326 exit(0);
327 }
328
329 if (printmp) {
330 print_manpath(mandirs);
331 exit(0);
332 }
333
334 /* Collect environment information */
335 if (isatty(STDOUT_FILENO) && (mwstr = getenv("MANWIDTH")) != NULL &&
336 *mwstr != '\0') {
337 if (strcasecmp(mwstr, "tty") == 0) {
338 struct winsize ws;
339
340 if (ioctl(0, TIOCGWINSZ, &ws) != 0)
341 warn("TIOCGWINSZ");
342 else
343 manwidth = ws.ws_col;
344 } else {
345 manwidth = (int)strtol(mwstr, (char **)NULL, 10);
346 if (manwidth < 0)
347 manwidth = 0;
348 }
349 }
350 if (manwidth != 0) {
351 DPRINTF("-- Using non-standard page width: %d\n", manwidth);
352 }
353
354 if ((pager = getenv("PAGER")) == NULL || *pager == '\0')
355 pager = PAGER;
356 DPRINTF("-- Using pager: %s\n", pager);
357
358 for (i = 0; i < argc; i++) {
359 char *cmd;
360 static struct man_node *mp;
361 char *pv[2];
362
363 /*
364 * If full path to command specified, customize
365 * the manpath accordingly.
366 */
367 if ((cmd = strrchr(argv[i], '/')) != NULL) {
368 *cmd = '\0';
369 if ((pv[0] = strdup(argv[i])) == NULL)
370 err(1, "strdup");
371 pv[1] = NULL;
372 *cmd = '/';
373 mp = build_manpath(pv,
374 BMP_ISPATH | BMP_FALLBACK_DEFMANDIR);
375 } else {
376 mp = mandirs;
377 }
378
379 if (apropos)
380 whatapro(mp, argv[i]);
381 else
382 ret += manual(mp, argv[i]);
383
384 if (mp != NULL && mp != mandirs) {
385 free(pv[0]);
386 free_manp(mp);
387 }
388 }
389
390 return (ret == 0 ? 0 : 1);
391 }
392
393 /*
394 * This routine builds the manpage structure from MANPATH or PATH,
395 * depending on flags. See BMP_* definitions above for valid
396 * flags.
397 */
398 static struct man_node *
399 build_manpath(char **pathv, int flags)
400 {
401 struct man_node *manpage = NULL;
402 struct man_node *currp = NULL;
403 struct man_node *lastp = NULL;
404 char **p;
405 char **q;
406 char *mand = NULL;
407 char *mandir = DEFMANDIR;
408 int s;
409 struct dupnode *didup = NULL;
410 struct stat sb;
411
412 s = sizeof (struct man_node);
413 for (p = pathv; *p != NULL; ) {
414 if (flags & BMP_ISPATH) {
415 if ((mand = path_to_manpath(*p)) == NULL)
416 goto next;
417 free(*p);
418 *p = mand;
419 }
420 q = split(*p, ',');
421 if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) {
422 freev(q);
423 goto next;
424 }
425
426 if (access(q[0], R_OK | X_OK) == 0) {
427 /*
428 * Some element exists. Do not append DEFMANDIR as a
429 * fallback.
430 */
431 flags &= ~BMP_FALLBACK_DEFMANDIR;
432
433 if ((currp = (struct man_node *)calloc(1, s)) == NULL)
434 err(1, "calloc");
435
436 currp->frompath = (flags & BMP_ISPATH);
437
438 if (manpage == NULL)
439 lastp = manpage = currp;
440
441 getpath(currp, p);
442 getsect(currp, p);
443
444 /*
445 * If there are no new elements in this path,
446 * do not add it to the manpage list.
447 */
448 if (dupcheck(currp, &didup) != 0) {
449 freev(currp->secv);
450 free(currp);
451 } else {
452 currp->next = NULL;
453 if (currp != manpage)
454 lastp->next = currp;
455 lastp = currp;
456 }
457 }
458 freev(q);
459 next:
460 /*
461 * Special handling of appending DEFMANDIR. After all pathv
462 * elements have been processed, append DEFMANDIR if needed.
463 */
464 if (p == &mandir)
465 break;
466 p++;
467 if (*p != NULL)
468 continue;
469 if (flags & (BMP_APPEND_DEFMANDIR | BMP_FALLBACK_DEFMANDIR)) {
470 p = &mandir;
471 flags &= ~BMP_ISPATH;
472 }
473 }
474
475 free_dupnode(didup);
476
477 return (manpage);
478 }
479
480 /*
481 * Store the mandir path into the manp structure.
482 */
483 static void
484 getpath(struct man_node *manp, char **pv)
485 {
486 char *s = *pv;
487 int i = 0;
488
489 while (*s != '\0' && *s != ',')
490 i++, s++;
491
492 if ((manp->path = (char *)malloc(i + 1)) == NULL)
493 err(1, "malloc");
494 (void) strlcpy(manp->path, *pv, i + 1);
495 }
496
497 /*
498 * Store the mandir's corresponding sections (submandir
499 * directories) into the manp structure.
500 */
501 static void
502 getsect(struct man_node *manp, char **pv)
503 {
504 char *sections;
505 char **sectp;
506
507 /* Just store all sections when doing makewhatis or apropos/whatis */
508 if (makewhatis || apropos) {
509 manp->defsrch = 1;
510 DPRINTF("-- Adding %s\n", manp->path);
511 manp->secv = NULL;
512 get_all_sect(manp);
513 } else if (sargs) {
514 manp->secv = split(mansec, ',');
515 for (sectp = manp->secv; *sectp; sectp++)
516 lower(*sectp);
517 } else if ((sections = strchr(*pv, ',')) != NULL) {
518 DPRINTF("-- Adding %s: MANSECTS=%s\n", manp->path, sections);
519 manp->secv = split(++sections, ',');
520 for (sectp = manp->secv; *sectp; sectp++)
521 lower(*sectp);
522 if (*manp->secv == NULL)
523 get_all_sect(manp);
524 } else if ((sections = check_config(*pv)) != NULL) {
525 manp->defsrch = 1;
526 DPRINTF("-- Adding %s: from %s, MANSECTS=%s\n", manp->path,
527 CONFIG, sections);
528 manp->secv = split(sections, ',');
529 for (sectp = manp->secv; *sectp; sectp++)
530 lower(*sectp);
531 if (*manp->secv == NULL)
532 get_all_sect(manp);
533 } else {
534 manp->defsrch = 1;
535 DPRINTF("-- Adding %s: default sort order\n", manp->path);
536 manp->secv = NULL;
537 get_all_sect(manp);
538 }
539 }
540
541 /*
542 * Get suffices of all sub-mandir directories in a mandir.
543 */
544 static void
545 get_all_sect(struct man_node *manp)
546 {
547 DIR *dp;
548 char **dirv;
549 char **dv;
550 char **p;
551 char *prev = NULL;
552 char *tmp = NULL;
553 int maxentries = MAXTOKENS;
554 int entries = 0;
555
556 if ((dp = opendir(manp->path)) == 0)
557 return;
558
559 sortdir(dp, &dirv);
560
561 (void) closedir(dp);
562
563 if (manp->secv == NULL) {
564 if ((manp->secv = malloc(maxentries * sizeof (char *))) == NULL)
565 err(1, "malloc");
566 }
567
568 for (dv = dirv, p = manp->secv; *dv; dv++) {
569 if (strcmp(*dv, CONFIG) == 0) {
570 free(*dv);
571 continue;
572 }
573
574 free(tmp);
575 if ((tmp = strdup(*dv + 3)) == NULL)
576 err(1, "strdup");
577
578 if (prev != NULL && strcmp(prev, tmp) == 0) {
579 free(*dv);
580 continue;
581 }
582
583 free(prev);
584 if ((prev = strdup(*dv + 3)) == NULL)
585 err(1, "strdup");
586
587 if ((*p = strdup(*dv + 3)) == NULL)
588 err(1, "strdup");
589
590 p++; entries++;
591
592 if (entries == maxentries) {
593 maxentries += MAXTOKENS;
594 if ((manp->secv = realloc(manp->secv,
595 sizeof (char *) * maxentries)) == NULL)
596 err(1, "realloc");
597 p = manp->secv + entries;
598 }
599 free(*dv);
600 }
601 free(tmp);
602 free(prev);
603 *p = NULL;
604 free(dirv);
605 }
606
607 /*
608 * Build whatis databases.
609 */
610 static void
611 do_makewhatis(struct man_node *manp)
612 {
613 struct man_node *p;
614 char *ldir;
615
616 for (p = manp; p != NULL; p = p->next) {
617 ldir = addlocale(p->path);
618 if (*localedir != '\0' && getdirs(ldir, NULL, 0) > 0)
619 mwpath(ldir);
620 free(ldir);
621 mwpath(p->path);
622 }
623 }
624
625 /*
626 * Count mandirs under the given manpath
627 */
628 static int
629 getdirs(char *path, char ***dirv, int flag)
630 {
631 DIR *dp;
632 struct dirent *d;
633 int n = 0;
634 int maxentries = MAXDIRS;
635 char **dv = NULL;
636
637 if ((dp = opendir(path)) == NULL)
638 return (0);
639
640 if (flag) {
641 if ((*dirv = malloc(sizeof (char *) *
642 maxentries)) == NULL)
643 err(1, "malloc");
644 dv = *dirv;
645 }
646 while ((d = readdir(dp))) {
647 if (strncmp(d->d_name, "man", 3) != 0)
648 continue;
649 n++;
650
651 if (flag) {
652 if ((*dv = strdup(d->d_name + 3)) == NULL)
653 err(1, "strdup");
654 dv++;
655 if ((dv - *dirv) == maxentries) {
656 int entries = maxentries;
657
658 maxentries += MAXTOKENS;
659 if ((*dirv = realloc(*dirv,
660 sizeof (char *) * maxentries)) == NULL)
661 err(1, "realloc");
662 dv = *dirv + entries;
663 }
664 }
665 }
666
667 (void) closedir(dp);
668 return (n);
669 }
670
671
672 /*
673 * Find matching whatis or apropos entries.
674 */
675 static void
676 whatapro(struct man_node *manp, char *word)
677 {
678 char whatpath[MAXPATHLEN];
679 struct man_node *b;
680 char *ldir;
681
682 for (b = manp; b != NULL; b = b->next) {
683 if (*localedir != '\0') {
684 ldir = addlocale(b->path);
685 if (getdirs(ldir, NULL, 0) != 0) {
686 (void) snprintf(whatpath, sizeof (whatpath),
687 "%s/%s", ldir, WHATIS);
688 search_whatis(whatpath, word);
689 }
690 free(ldir);
691 }
692 (void) snprintf(whatpath, sizeof (whatpath), "%s/%s", b->path,
693 WHATIS);
694 search_whatis(whatpath, word);
695 }
696 }
697
698 static void
699 search_whatis(char *whatpath, char *word)
700 {
701 FILE *fp;
702 char *line = NULL;
703 size_t linecap = 0;
704 char *pkwd;
705 regex_t preg;
706 char **ss = NULL;
707 char s[MAXNAMELEN];
708 int i;
709
710 if ((fp = fopen(whatpath, "r")) == NULL) {
711 perror(whatpath);
712 return;
713 }
714
715 DPRINTF("-- Found %s: %s\n", WHATIS, whatpath);
716
717 /* Build keyword regex */
718 if (asprintf(&pkwd, "%s%s%s", (whatis) ? "\\<" : "",
719 word, (whatis) ? "\\>" : "") == -1)
720 err(1, "asprintf");
721
722 if (regcomp(&preg, pkwd, REG_BASIC | REG_ICASE | REG_NOSUB) != 0)
723 err(1, "regcomp");
724
725 if (sargs)
726 ss = split(mansec, ',');
727
728 while (getline(&line, &linecap, fp) > 0) {
729 if (regexec(&preg, line, 0, NULL, 0) == 0) {
730 if (sargs) {
731 /* Section-restricted search */
732 for (i = 0; ss[i] != NULL; i++) {
733 (void) snprintf(s, sizeof (s), "(%s)",
734 ss[i]);
735 if (strstr(line, s) != NULL) {
736 (void) printf("%s", line);
737 break;
738 }
739 }
740 } else {
741 (void) printf("%s", line);
742 }
743 }
744 }
745
746 if (ss != NULL)
747 freev(ss);
748 free(pkwd);
749 (void) fclose(fp);
750 }
751
752
753 /*
754 * Split a string by specified separator.
755 */
756 static char **
757 split(char *s1, char sep)
758 {
759 char **tokv, **vp;
760 char *mp = s1, *tp;
761 int maxentries = MAXTOKENS;
762 int entries = 0;
763
764 if ((tokv = vp = malloc(maxentries * sizeof (char *))) == NULL)
765 err(1, "malloc");
766
767 for (; mp && *mp; mp = tp) {
768 tp = strchr(mp, sep);
769 if (mp == tp) {
770 tp++;
771 continue;
772 }
773 if (tp) {
774 size_t len;
775
776 len = tp - mp;
777 if ((*vp = (char *)malloc(sizeof (char) *
778 len + 1)) == NULL)
779 err(1, "malloc");
780 (void) strncpy(*vp, mp, len);
781 *(*vp + len) = '\0';
782 tp++;
783 vp++;
784 } else {
785 if ((*vp = strdup(mp)) == NULL)
786 err(1, "strdup");
787 vp++;
788 }
789 entries++;
790 if (entries == maxentries) {
791 maxentries += MAXTOKENS;
792 if ((tokv = realloc(tokv,
793 maxentries * sizeof (char *))) == NULL)
794 err(1, "realloc");
795 vp = tokv + entries;
796 }
797 }
798 *vp = 0;
799
800 return (tokv);
801 }
802
803 /*
804 * Free a vector allocated by split()
805 */
806 static void
807 freev(char **v)
808 {
809 int i;
810 if (v != NULL) {
811 for (i = 0; v[i] != NULL; i++) {
812 free(v[i]);
813 }
814 free(v);
815 }
816 }
817
818 /*
819 * Convert paths to full paths if necessary
820 */
821 static void
822 fullpaths(struct man_node **manp_head)
823 {
824 char *cwd = NULL;
825 char *p;
826 int cwd_gotten = 0;
827 struct man_node *manp = *manp_head;
828 struct man_node *b;
829 struct man_node *prev = NULL;
830
831 for (b = manp; b != NULL; b = b->next) {
832 if (*(b->path) == '/') {
833 prev = b;
834 continue;
835 }
836
837 if (!cwd_gotten) {
838 cwd = getcwd(NULL, MAXPATHLEN);
839 cwd_gotten = 1;
840 }
841
842 if (cwd) {
843 /* Relative manpath with cwd: make absolute */
844 if (asprintf(&p, "%s/%s", cwd, b->path) == -1)
845 err(1, "asprintf");
846 free(b->path);
847 b->path = p;
848 } else {
849 /* Relative manpath but no cwd: omit path entry */
850 if (prev)
851 prev->next = b->next;
852 else
853 *manp_head = b->next;
854
855 free_manp(b);
856 }
857 }
858 free(cwd);
859 }
860
861 /*
862 * Free a man_node structure and its contents
863 */
864 static void
865 free_manp(struct man_node *manp)
866 {
867 char **p;
868
869 free(manp->path);
870 p = manp->secv;
871 while ((p != NULL) && (*p != NULL)) {
872 free(*p);
873 p++;
874 }
875 free(manp->secv);
876 free(manp);
877 }
878
879
880 /*
881 * Map (in place) to lower case.
882 */
883 static void
884 lower(char *s)
885 {
886
887 if (s == 0)
888 return;
889 while (*s) {
890 if (isupper(*s))
891 *s = tolower(*s);
892 s++;
893 }
894 }
895
896
897 /*
898 * Compare function for qsort().
899 * Sort first by section, then by prefix.
900 */
901 static int
902 cmp(const void *arg1, const void *arg2)
903 {
904 int n;
905 char **p1 = (char **)arg1;
906 char **p2 = (char **)arg2;
907
908 /* By section */
909 if ((n = strcmp(*p1 + 3, *p2 + 3)) != 0)
910 return (n);
911
912 /* By prefix reversed */
913 return (strncmp(*p2, *p1, 3));
914 }
915
916
917 /*
918 * Find a manpage.
919 */
920 static int
921 manual(struct man_node *manp, char *name)
922 {
923 struct man_node *p;
924 struct man_node *local;
925 int ndirs = 0;
926 char *ldir;
927 char *ldirs[2];
928 char *fullname = name;
929 char *slash;
930
931 if ((slash = strrchr(name, '/')) != NULL)
932 name = slash + 1;
933
934 /* For each path in MANPATH */
935 found = 0;
936
937 for (p = manp; p != NULL; p = p->next) {
938 DPRINTF("-- Searching mandir: %s\n", p->path);
939
940 if (*localedir != '\0') {
941 ldir = addlocale(p->path);
942 ndirs = getdirs(ldir, NULL, 0);
943 if (ndirs != 0) {
944 ldirs[0] = ldir;
945 ldirs[1] = NULL;
946 local = build_manpath(ldirs, 0);
947 DPRINTF("-- Locale specific subdir: %s\n",
948 ldir);
949 mandir(local->secv, ldir, name, 1);
950 free_manp(local);
951 }
952 free(ldir);
953 }
954
955 /*
956 * Locale mandir not valid, man page in locale
957 * mandir not found, or -a option present
958 */
959 if (ndirs == 0 || !found || all)
960 mandir(p->secv, p->path, name, 0);
961
962 if (found && !all)
963 break;
964 }
965
966 if (!found) {
967 if (sargs) {
968 (void) fprintf(stderr, gettext(
969 "No manual entry for %s in section(s) %s\n"),
970 fullname, mansec);
971 } else {
972 (void) fprintf(stderr,
973 gettext("No manual entry for %s\n"), fullname);
974 }
975
976 }
977
978 return (!found);
979 }
980
981
982 /*
983 * For a specified manual directory, read, store and sort section subdirs.
984 * For each section specified, find and search matching subdirs.
985 */
986 static void
987 mandir(char **secv, char *path, char *name, int lspec)
988 {
989 DIR *dp;
990 char **dirv;
991 char **dv, **pdv;
992 int len, dslen;
993
994 if ((dp = opendir(path)) == NULL)
995 return;
996
997 if (lspec)
998 DPRINTF("-- Searching mandir: %s\n", path);
999
1000 sortdir(dp, &dirv);
1001
1002 /* Search in the order specified by MANSECTS */
1003 for (; *secv; secv++) {
1004 len = strlen(*secv);
1005 for (dv = dirv; *dv; dv++) {
1006 dslen = strlen(*dv + 3);
1007 if (dslen > len)
1008 len = dslen;
1009 if (**secv == '\\') {
1010 if (strcmp(*secv + 1, *dv + 3) != 0)
1011 continue;
1012 } else if (strncasecmp(*secv, *dv + 3, len) != 0) {
1013 if (!all &&
1014 (newsection = map_section(*secv, path))
1015 == NULL) {
1016 continue;
1017 }
1018 if (newsection == NULL)
1019 newsection = "";
1020 if (strncmp(newsection, *dv + 3, len) != 0) {
1021 continue;
1022 }
1023 }
1024
1025 if (searchdir(path, *dv, name) == 0)
1026 continue;
1027
1028 if (!all) {
1029 pdv = dirv;
1030 while (*pdv) {
1031 free(*pdv);
1032 pdv++;
1033 }
1034 (void) closedir(dp);
1035 free(dirv);
1036 return;
1037 }
1038
1039 if (all && **dv == 'm' && *(dv + 1) &&
1040 strcmp(*(dv + 1) + 3, *dv + 3) == 0)
1041 dv++;
1042 }
1043 }
1044 pdv = dirv;
1045 while (*pdv != NULL) {
1046 free(*pdv);
1047 pdv++;
1048 }
1049 free(dirv);
1050 (void) closedir(dp);
1051 }
1052
1053 /*
1054 * Sort directories.
1055 */
1056 static void
1057 sortdir(DIR *dp, char ***dirv)
1058 {
1059 struct dirent *d;
1060 char **dv;
1061 int maxentries = MAXDIRS;
1062 int entries = 0;
1063
1064 if ((dv = *dirv = malloc(sizeof (char *) *
1065 maxentries)) == NULL)
1066 err(1, "malloc");
1067 dv = *dirv;
1068
1069 while ((d = readdir(dp))) {
1070 if (strcmp(d->d_name, ".") == 0 ||
1071 strcmp(d->d_name, "..") == 0)
1072 continue;
1073
1074 if (strncmp(d->d_name, "man", 3) == 0 ||
1075 strncmp(d->d_name, "cat", 3) == 0) {
1076 if ((*dv = strdup(d->d_name)) == NULL)
1077 err(1, "strdup");
1078 dv++;
1079 entries++;
1080 if (entries == maxentries) {
1081 maxentries += MAXDIRS;
1082 if ((*dirv = realloc(*dirv,
1083 sizeof (char *) * maxentries)) == NULL)
1084 err(1, "realloc");
1085 dv = *dirv + entries;
1086 }
1087 }
1088 }
1089 *dv = 0;
1090
1091 qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp);
1092
1093 }
1094
1095
1096 /*
1097 * Search a section subdir for a given manpage.
1098 */
1099 static int
1100 searchdir(char *path, char *dir, char *name)
1101 {
1102 DIR *sdp;
1103 struct dirent *sd;
1104 char sectpath[MAXPATHLEN];
1105 char file[MAXNAMLEN];
1106 char dname[MAXPATHLEN];
1107 char *last;
1108 int nlen;
1109
1110 (void) snprintf(sectpath, sizeof (sectpath), "%s/%s", path, dir);
1111 (void) snprintf(file, sizeof (file), "%s.", name);
1112
1113 if ((sdp = opendir(sectpath)) == NULL)
1114 return (0);
1115
1116 while ((sd = readdir(sdp))) {
1117 char *pname;
1118
1119 if ((pname = strdup(sd->d_name)) == NULL)
1120 err(1, "strdup");
1121 if ((last = strrchr(pname, '.')) != NULL &&
1122 (strcmp(last, ".gz") == 0 || strcmp(last, ".bz2") == 0))
1123 *last = '\0';
1124 last = strrchr(pname, '.');
1125 nlen = last - pname;
1126 (void) snprintf(dname, sizeof (dname), "%.*s.", nlen, pname);
1127 if (strcmp(dname, file) == 0 ||
1128 strcmp(pname, name) == 0) {
1129 (void) format(path, dir, name, sd->d_name);
1130 (void) closedir(sdp);
1131 free(pname);
1132 return (1);
1133 }
1134 free(pname);
1135 }
1136 (void) closedir(sdp);
1137
1138 return (0);
1139 }
1140
1141 /*
1142 * Check the hash table of old directory names to see if there is a
1143 * new directory name.
1144 */
1145 static char *
1146 map_section(char *section, char *path)
1147 {
1148 int i;
1149 int len;
1150 char fullpath[MAXPATHLEN];
1151
1152 if (list) /* -l option fall through */
1153 return (NULL);
1154
1155 for (i = 0; i <= ((sizeof (map)/sizeof (map[0]) - 1)); i++) {
1156 if (strlen(section) > strlen(map[i].new_name)) {
1157 len = strlen(section);
1158 } else {
1159 len = strlen(map[i].new_name);
1160 }
1161 if (strncmp(section, map[i].old_name, len) == 0) {
1162 (void) snprintf(fullpath, sizeof (fullpath),
1163 "%s/sman%s", path, map[i].new_name);
1164 if (!access(fullpath, R_OK | X_OK)) {
1165 return (map[i].new_name);
1166 } else {
1167 return (NULL);
1168 }
1169 }
1170 }
1171
1172 return (NULL);
1173 }
1174
1175 /*
1176 * Format the manpage.
1177 */
1178 static int
1179 format(char *path, char *dir, char *name, char *pg)
1180 {
1181 char manpname[MAXPATHLEN], catpname[MAXPATHLEN];
1182 char cmdbuf[BUFSIZ], tmpbuf[BUFSIZ];
1183 char *cattool;
1184 int utf8 = 0;
1185 struct stat sbman, sbcat;
1186
1187 found++;
1188
1189 if (list) {
1190 (void) printf(gettext("%s(%s)\t-M %s\n"), name, dir + 3, path);
1191 return (-1);
1192 }
1193
1194 (void) snprintf(manpname, sizeof (manpname), "%s/man%s/%s", path,
1195 dir + 3, pg);
1196 (void) snprintf(catpname, sizeof (catpname), "%s/cat%s/%s", path,
1197 dir + 3, pg);
1198
1199 /* Can't do PS output if manpage doesn't exist */
1200 if (stat(manpname, &sbman) != 0 && psoutput)
1201 return (-1);
1202
1203 /*
1204 * If both manpage and catpage do not exist, manpname is
1205 * broken symlink, most likely.
1206 */
1207 if (stat(catpname, &sbcat) != 0 && stat(manpname, &sbman) != 0)
1208 err(1, "%s", manpname);
1209
1210 /* Setup cattool */
1211 if (fnmatch("*.gz", manpname, 0) == 0)
1212 cattool = "gzcat";
1213 else if (fnmatch("*.bz2", manpname, 0) == 0)
1214 cattool = "bzcat";
1215 else
1216 cattool = "gzcat -f";
1217
1218 /* Preprocess UTF-8 input with preconv (could be smarter) */
1219 if (strstr(path, "UTF-8") != NULL)
1220 utf8 = 1;
1221
1222 if (psoutput) {
1223 (void) snprintf(cmdbuf, BUFSIZ,
1224 "cd %s; %s %s%s | mandoc -Tps | lp -Tpostscript",
1225 path, cattool, manpname,
1226 utf8 ? " | " PRECONV " -e UTF-8" : "");
1227 DPRINTF("-- Using manpage: %s\n", manpname);
1228 goto cmd;
1229 }
1230
1231 /*
1232 * Output catpage if:
1233 * - manpage doesn't exist
1234 * - output width is standard and catpage is recent enough
1235 */
1236 if (stat(manpname, &sbman) != 0 || (manwidth == 0 &&
1237 stat(catpname, &sbcat) == 0 && sbcat.st_mtime >= sbman.st_mtime)) {
1238 DPRINTF("-- Using catpage: %s\n", catpname);
1239 (void) snprintf(cmdbuf, BUFSIZ, "%s %s", pager, catpname);
1240 goto cmd;
1241 }
1242
1243 DPRINTF("-- Using manpage: %s\n", manpname);
1244 if (manwidth > 0)
1245 (void) snprintf(tmpbuf, BUFSIZ, "-Owidth=%d ", manwidth);
1246 (void) snprintf(cmdbuf, BUFSIZ, "cd %s; %s %s%s | mandoc -T%s %s| %s",
1247 path, cattool, manpname,
1248 utf8 ? " | " PRECONV " -e UTF-8 " : "",
1249 utf8 ? "utf8" : "ascii", (manwidth > 0) ? tmpbuf : "", pager);
1250
1251 cmd:
1252 DPRINTF("-- Command: %s\n", cmdbuf);
1253
1254 if (!debug)
1255 return (system(cmdbuf) == 0);
1256 else
1257 return (0);
1258 }
1259
1260 /*
1261 * Add <localedir> to the path.
1262 */
1263 static char *
1264 addlocale(char *path)
1265 {
1266 char *tmp;
1267
1268 if (asprintf(&tmp, "%s/%s", path, localedir) == -1)
1269 err(1, "asprintf");
1270
1271 return (tmp);
1272 }
1273
1274 /*
1275 * Get the order of sections from man.cf.
1276 */
1277 static char *
1278 check_config(char *path)
1279 {
1280 FILE *fp;
1281 char *rc = NULL;
1282 char *sect;
1283 char fname[MAXPATHLEN];
1284 char *line = NULL;
1285 size_t linecap = 0;
1286
1287 (void) snprintf(fname, MAXPATHLEN, "%s/%s", path, CONFIG);
1288
1289 if ((fp = fopen(fname, "r")) == NULL)
1290 return (NULL);
1291
1292 while (getline(&line, &linecap, fp) > 0) {
1293 if ((rc = strstr(line, "MANSECTS")) != NULL)
1294 break;
1295 }
1296
1297 (void) fclose(fp);
1298
1299 if (rc == NULL || (sect = strchr(line, '=')) == NULL)
1300 return (NULL);
1301 else
1302 return (++sect);
1303 }
1304
1305
1306 /*
1307 * Initialize the bintoman array with appropriate device and inode info.
1308 */
1309 static void
1310 init_bintoman(void)
1311 {
1312 int i;
1313 struct stat sb;
1314
1315 for (i = 0; bintoman[i].bindir != NULL; i++) {
1316 if (stat(bintoman[i].bindir, &sb) == 0) {
1317 bintoman[i].dev = sb.st_dev;
1318 bintoman[i].ino = sb.st_ino;
1319 } else {
1320 bintoman[i].dev = NODEV;
1321 }
1322 }
1323 }
1324
1325 /*
1326 * If a duplicate is found, return 1.
1327 * If a duplicate is not found, add it to the dupnode list and return 0.
1328 */
1329 static int
1330 dupcheck(struct man_node *mnp, struct dupnode **dnp)
1331 {
1332 struct dupnode *curdnp;
1333 struct secnode *cursnp;
1334 struct stat sb;
1335 int i;
1336 int rv = 1;
1337 int dupfound;
1338
1339 /* If the path doesn't exist, treat it as a duplicate */
1340 if (stat(mnp->path, &sb) != 0)
1341 return (1);
1342
1343 /* If no sections were found in the man dir, treat it as duplicate */
1344 if (mnp->secv == NULL)
1345 return (1);
1346
1347 /*
1348 * Find the dupnode structure for the previous time this directory
1349 * was looked at. Device and inode numbers are compared so that
1350 * directories that are reached via different paths (e.g. /usr/man and
1351 * /usr/share/man) are treated as equivalent.
1352 */
1353 for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) {
1354 if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino)
1355 break;
1356 }
1357
1358 /*
1359 * First time this directory has been seen. Add a new node to the
1360 * head of the list. Since all entries are guaranteed to be unique
1361 * copy all sections to new node.
1362 */
1363 if (curdnp == NULL) {
1364 if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL)
1365 err(1, "calloc");
1366 for (i = 0; mnp->secv[i] != NULL; i++) {
1367 if ((cursnp = calloc(1, sizeof (struct secnode)))
1368 == NULL)
1369 err(1, "calloc");
1370 cursnp->next = curdnp->secl;
1371 curdnp->secl = cursnp;
1372 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1373 err(1, "strdup");
1374 }
1375 curdnp->dev = sb.st_dev;
1376 curdnp->ino = sb.st_ino;
1377 curdnp->next = *dnp;
1378 *dnp = curdnp;
1379 return (0);
1380 }
1381
1382 /*
1383 * Traverse the section vector in the man_node and the section list
1384 * in dupnode cache to eliminate all duplicates from man_node.
1385 */
1386 for (i = 0; mnp->secv[i] != NULL; i++) {
1387 dupfound = 0;
1388 for (cursnp = curdnp->secl; cursnp != NULL;
1389 cursnp = cursnp->next) {
1390 if (strcmp(mnp->secv[i], cursnp->secp) == 0) {
1391 dupfound = 1;
1392 break;
1393 }
1394 }
1395 if (dupfound) {
1396 mnp->secv[i][0] = '\0';
1397 continue;
1398 }
1399
1400
1401 /*
1402 * Update curdnp and set return value to indicate that this
1403 * was not all duplicates.
1404 */
1405 if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL)
1406 err(1, "calloc");
1407 cursnp->next = curdnp->secl;
1408 curdnp->secl = cursnp;
1409 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL)
1410 err(1, "strdup");
1411 rv = 0;
1412 }
1413
1414 return (rv);
1415 }
1416
1417 /*
1418 * Given a bindir, return corresponding mandir.
1419 */
1420 static char *
1421 path_to_manpath(char *bindir)
1422 {
1423 char *mand, *p;
1424 int i;
1425 struct stat sb;
1426
1427 /* First look for known translations for specific bin paths */
1428 if (stat(bindir, &sb) != 0) {
1429 return (NULL);
1430 }
1431 for (i = 0; bintoman[i].bindir != NULL; i++) {
1432 if (sb.st_dev == bintoman[i].dev &&
1433 sb.st_ino == bintoman[i].ino) {
1434 if ((mand = strdup(bintoman[i].mandir)) == NULL)
1435 err(1, "strdup");
1436 if ((p = strchr(mand, ',')) != NULL)
1437 *p = '\0';
1438 if (stat(mand, &sb) != 0) {
1439 free(mand);
1440 return (NULL);
1441 }
1442 if (p != NULL)
1443 *p = ',';
1444 return (mand);
1445 }
1446 }
1447
1448 /*
1449 * No specific translation found. Try `dirname $bindir`/share/man
1450 * and `dirname $bindir`/man
1451 */
1452 if ((mand = malloc(MAXPATHLEN)) == NULL)
1453 err(1, "malloc");
1454 if (strlcpy(mand, bindir, MAXPATHLEN) >= MAXPATHLEN) {
1455 free(mand);
1456 return (NULL);
1457 }
1458
1459 /*
1460 * Advance to end of buffer, strip trailing /'s then remove last
1461 * directory component.
1462 */
1463 for (p = mand; *p != '\0'; p++)
1464 ;
1465 for (; p > mand && *p == '/'; p--)
1466 ;
1467 for (; p > mand && *p != '/'; p--)
1468 ;
1469 if (p == mand && *p == '.') {
1470 if (realpath("..", mand) == NULL) {
1471 free(mand);
1472 return (NULL);
1473 }
1474 for (; *p != '\0'; p++)
1475 ;
1476 } else {
1477 *p = '\0';
1478 }
1479
1480 if (strlcat(mand, "/share/man", MAXPATHLEN) >= MAXPATHLEN) {
1481 free(mand);
1482 return (NULL);
1483 }
1484
1485 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1486 return (mand);
1487 }
1488
1489 /*
1490 * Strip the /share/man off and try /man
1491 */
1492 *p = '\0';
1493 if (strlcat(mand, "/man", MAXPATHLEN) >= MAXPATHLEN) {
1494 free(mand);
1495 return (NULL);
1496 }
1497 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) {
1498 return (mand);
1499 }
1500
1501 /*
1502 * No man or share/man directory found
1503 */
1504 free(mand);
1505 return (NULL);
1506 }
1507
1508 /*
1509 * Free a linked list of dupnode structs.
1510 */
1511 void
1512 free_dupnode(struct dupnode *dnp) {
1513 struct dupnode *dnp2;
1514 struct secnode *snp;
1515
1516 while (dnp != NULL) {
1517 dnp2 = dnp;
1518 dnp = dnp->next;
1519 while (dnp2->secl != NULL) {
1520 snp = dnp2->secl;
1521 dnp2->secl = dnp2->secl->next;
1522 free(snp->secp);
1523 free(snp);
1524 }
1525 free(dnp2);
1526 }
1527 }
1528
1529 /*
1530 * Print manp linked list to stdout.
1531 */
1532 void
1533 print_manpath(struct man_node *manp)
1534 {
1535 char colon[2] = "\0\0";
1536 char **secp;
1537
1538 for (; manp != NULL; manp = manp->next) {
1539 (void) printf("%s%s", colon, manp->path);
1540 colon[0] = ':';
1541
1542 /*
1543 * If man.cf or a directory scan was used to create section
1544 * list, do not print section list again. If the output of
1545 * man -p is used to set MANPATH, subsequent runs of man
1546 * will re-read man.cf and/or scan man directories as
1547 * required.
1548 */
1549 if (manp->defsrch != 0)
1550 continue;
1551
1552 for (secp = manp->secv; *secp != NULL; secp++) {
1553 /*
1554 * Section deduplication may have eliminated some
1555 * sections from the vector. Avoid displaying this
1556 * detail which would appear as ",," in output
1557 */
1558 if ((*secp)[0] != '\0')
1559 (void) printf(",%s", *secp);
1560 }
1561 }
1562 (void) printf("\n");
1563 }
1564
1565 static void
1566 usage_man(void)
1567 {
1568
1569 (void) fprintf(stderr, gettext(
1570 "usage: man [-alptw] [-M path] [-s section] name ...\n"
1571 " man [-M path] [-s section] -k keyword -- emulate apropos\n"
1572 " man [-M path] [-s section] -f keyword -- emulate whatis\n"));
1573
1574 exit(1);
1575 }
1576
1577 static void
1578 usage_whatapro(void)
1579 {
1580
1581 (void) fprintf(stderr, gettext(
1582 "usage: %s [-M path] [-s section] keyword ...\n"),
1583 whatis ? "whatis" : "apropos");
1584
1585 exit(1);
1586 }
1587
1588 static void
1589 usage_catman(void)
1590 {
1591 (void) fprintf(stderr, gettext(
1592 "usage: catman [-M path] [-w]\n"));
1593
1594 exit(1);
1595 }
1596
1597 static void
1598 usage_makewhatis(void)
1599 {
1600 (void) fprintf(stderr, gettext("usage: makewhatis\n"));
1601
1602 exit(1);
1603 }