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 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 * 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 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 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 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 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 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 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 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 ** 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 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 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 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 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 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 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 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 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 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 * 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 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 * 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 * 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 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 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 * 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 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 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 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 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 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 1681 usage_makewhatis(void) 1682 { 1683 (void) fprintf(stderr, gettext("usage: makewhatis\n")); 1684 1685 exit(1); 1686 } 1687