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