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