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