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