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