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