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