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 * Copyright 2014 Garrett D'Amore <garrett@damore.org> 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 manp->secv = split(mansec, ','); 533 for (sectp = manp->secv; *sectp; sectp++) 534 lower(*sectp); 535 } else if ((sections = strchr(*pv, ',')) != NULL) { 536 DPRINTF("-- Adding %s: MANSECTS=%s\n", manp->path, sections); 537 manp->secv = split(++sections, ','); 538 for (sectp = manp->secv; *sectp; sectp++) 539 lower(*sectp); 540 if (*manp->secv == NULL) 541 get_all_sect(manp); 542 } else if ((sections = check_config(*pv)) != NULL) { 543 manp->defsrch = 1; 544 DPRINTF("-- Adding %s: from %s, MANSECTS=%s\n", manp->path, 545 CONFIG, sections); 546 manp->secv = split(sections, ','); 547 for (sectp = manp->secv; *sectp; sectp++) 548 lower(*sectp); 549 if (*manp->secv == NULL) 550 get_all_sect(manp); 551 } else { 552 manp->defsrch = 1; 553 DPRINTF("-- Adding %s: default sort order\n", manp->path); 554 manp->secv = NULL; 555 get_all_sect(manp); 556 } 557 } 558 559 /* 560 * Get suffices of all sub-mandir directories in a mandir. 561 */ 562 static void 563 get_all_sect(struct man_node *manp) 564 { 565 DIR *dp; 566 char **dirv; 567 char **dv; 568 char **p; 569 char *prev = NULL; 570 char *tmp = NULL; 571 int maxentries = MAXTOKENS; 572 int entries = 0; 573 574 if ((dp = opendir(manp->path)) == 0) 575 return; 576 577 sortdir(dp, &dirv); 578 579 (void) closedir(dp); 580 581 if (manp->secv == NULL) { 582 if ((manp->secv = malloc(maxentries * sizeof (char *))) == NULL) 583 err(1, "malloc"); 584 } 585 586 for (dv = dirv, p = manp->secv; *dv; dv++) { 587 if (strcmp(*dv, CONFIG) == 0) { 588 free(*dv); 589 continue; 590 } 591 592 free(tmp); 593 if ((tmp = strdup(*dv + 3)) == NULL) 594 err(1, "strdup"); 595 596 if (prev != NULL && strcmp(prev, tmp) == 0) { 597 free(*dv); 598 continue; 599 } 600 601 free(prev); 602 if ((prev = strdup(*dv + 3)) == NULL) 603 err(1, "strdup"); 604 605 if ((*p = strdup(*dv + 3)) == NULL) 606 err(1, "strdup"); 607 608 p++; entries++; 609 610 if (entries == maxentries) { 611 maxentries += MAXTOKENS; 612 if ((manp->secv = realloc(manp->secv, 613 sizeof (char *) * maxentries)) == NULL) 614 err(1, "realloc"); 615 p = manp->secv + entries; 616 } 617 free(*dv); 618 } 619 free(tmp); 620 free(prev); 621 *p = NULL; 622 free(dirv); 623 } 624 625 /* 626 * Build whatis databases. 627 */ 628 static void 629 do_makewhatis(struct man_node *manp) 630 { 631 struct man_node *p; 632 char *ldir; 633 634 for (p = manp; p != NULL; p = p->next) { 635 ldir = addlocale(p->path); 636 if (*localedir != '\0' && getdirs(ldir, NULL, 0) > 0) 637 mwpath(ldir); 638 free(ldir); 639 mwpath(p->path); 640 } 641 } 642 643 /* 644 * Count mandirs under the given manpath 645 */ 646 static int 647 getdirs(char *path, char ***dirv, int flag) 648 { 649 DIR *dp; 650 struct dirent *d; 651 int n = 0; 652 int maxentries = MAXDIRS; 653 char **dv = NULL; 654 655 if ((dp = opendir(path)) == NULL) 656 return (0); 657 658 if (flag) { 659 if ((*dirv = malloc(sizeof (char *) * 660 maxentries)) == NULL) 661 err(1, "malloc"); 662 dv = *dirv; 663 } 664 while ((d = readdir(dp))) { 665 if (strncmp(d->d_name, "man", 3) != 0) 666 continue; 667 n++; 668 669 if (flag) { 670 if ((*dv = strdup(d->d_name + 3)) == NULL) 671 err(1, "strdup"); 672 dv++; 673 if ((dv - *dirv) == maxentries) { 674 int entries = maxentries; 675 676 maxentries += MAXTOKENS; 677 if ((*dirv = realloc(*dirv, 678 sizeof (char *) * maxentries)) == NULL) 679 err(1, "realloc"); 680 dv = *dirv + entries; 681 } 682 } 683 } 684 685 (void) closedir(dp); 686 return (n); 687 } 688 689 690 /* 691 * Find matching whatis or apropos entries. 692 */ 693 static void 694 whatapro(struct man_node *manp, char *word) 695 { 696 char whatpath[MAXPATHLEN]; 697 struct man_node *b; 698 char *ldir; 699 700 for (b = manp; b != NULL; b = b->next) { 701 if (*localedir != '\0') { 702 ldir = addlocale(b->path); 703 if (getdirs(ldir, NULL, 0) != 0) { 704 (void) snprintf(whatpath, sizeof (whatpath), 705 "%s/%s", ldir, WHATIS); 706 search_whatis(whatpath, word); 707 } 708 free(ldir); 709 } 710 (void) snprintf(whatpath, sizeof (whatpath), "%s/%s", b->path, 711 WHATIS); 712 search_whatis(whatpath, word); 713 } 714 } 715 716 static void 717 search_whatis(char *whatpath, char *word) 718 { 719 FILE *fp; 720 char *line = NULL; 721 size_t linecap = 0; 722 char *pkwd; 723 regex_t preg; 724 char **ss = NULL; 725 char s[MAXNAMELEN]; 726 int i; 727 728 if ((fp = fopen(whatpath, "r")) == NULL) { 729 perror(whatpath); 730 return; 731 } 732 733 DPRINTF("-- Found %s: %s\n", WHATIS, whatpath); 734 735 /* Build keyword regex */ 736 if (asprintf(&pkwd, "%s%s%s", (whatis) ? "\\<" : "", 737 word, (whatis) ? "\\>" : "") == -1) 738 err(1, "asprintf"); 739 740 if (regcomp(&preg, pkwd, REG_BASIC | REG_ICASE | REG_NOSUB) != 0) 741 err(1, "regcomp"); 742 743 if (sargs) 744 ss = split(mansec, ','); 745 746 while (getline(&line, &linecap, fp) > 0) { 747 if (regexec(&preg, line, 0, NULL, 0) == 0) { 748 if (sargs) { 749 /* Section-restricted search */ 750 for (i = 0; ss[i] != NULL; i++) { 751 (void) snprintf(s, sizeof (s), "(%s)", 752 ss[i]); 753 if (strstr(line, s) != NULL) { 754 (void) printf("%s", line); 755 break; 756 } 757 } 758 } else { 759 (void) printf("%s", line); 760 } 761 } 762 } 763 764 if (ss != NULL) 765 freev(ss); 766 free(pkwd); 767 (void) fclose(fp); 768 } 769 770 771 /* 772 * Split a string by specified separator. 773 */ 774 static char ** 775 split(char *s1, char sep) 776 { 777 char **tokv, **vp; 778 char *mp = s1, *tp; 779 int maxentries = MAXTOKENS; 780 int entries = 0; 781 782 if ((tokv = vp = malloc(maxentries * sizeof (char *))) == NULL) 783 err(1, "malloc"); 784 785 for (; mp && *mp; mp = tp) { 786 tp = strchr(mp, sep); 787 if (mp == tp) { 788 tp++; 789 continue; 790 } 791 if (tp) { 792 size_t len; 793 794 len = tp - mp; 795 if ((*vp = (char *)malloc(sizeof (char) * 796 len + 1)) == NULL) 797 err(1, "malloc"); 798 (void) strncpy(*vp, mp, len); 799 *(*vp + len) = '\0'; 800 tp++; 801 vp++; 802 } else { 803 if ((*vp = strdup(mp)) == NULL) 804 err(1, "strdup"); 805 vp++; 806 } 807 entries++; 808 if (entries == maxentries) { 809 maxentries += MAXTOKENS; 810 if ((tokv = realloc(tokv, 811 maxentries * sizeof (char *))) == NULL) 812 err(1, "realloc"); 813 vp = tokv + entries; 814 } 815 } 816 *vp = 0; 817 818 return (tokv); 819 } 820 821 /* 822 * Free a vector allocated by split() 823 */ 824 static void 825 freev(char **v) 826 { 827 int i; 828 if (v != NULL) { 829 for (i = 0; v[i] != NULL; i++) { 830 free(v[i]); 831 } 832 free(v); 833 } 834 } 835 836 /* 837 * Convert paths to full paths if necessary 838 */ 839 static void 840 fullpaths(struct man_node **manp_head) 841 { 842 char *cwd = NULL; 843 char *p; 844 int cwd_gotten = 0; 845 struct man_node *manp = *manp_head; 846 struct man_node *b; 847 struct man_node *prev = NULL; 848 849 for (b = manp; b != NULL; b = b->next) { 850 if (*(b->path) == '/') { 851 prev = b; 852 continue; 853 } 854 855 if (!cwd_gotten) { 856 cwd = getcwd(NULL, MAXPATHLEN); 857 cwd_gotten = 1; 858 } 859 860 if (cwd) { 861 /* Relative manpath with cwd: make absolute */ 862 if (asprintf(&p, "%s/%s", cwd, b->path) == -1) 863 err(1, "asprintf"); 864 free(b->path); 865 b->path = p; 866 } else { 867 /* Relative manpath but no cwd: omit path entry */ 868 if (prev) 869 prev->next = b->next; 870 else 871 *manp_head = b->next; 872 873 free_manp(b); 874 } 875 } 876 free(cwd); 877 } 878 879 /* 880 * Free a man_node structure and its contents 881 */ 882 static void 883 free_manp(struct man_node *manp) 884 { 885 char **p; 886 887 free(manp->path); 888 p = manp->secv; 889 while ((p != NULL) && (*p != NULL)) { 890 free(*p); 891 p++; 892 } 893 free(manp->secv); 894 free(manp); 895 } 896 897 898 /* 899 * Map (in place) to lower case. 900 */ 901 static void 902 lower(char *s) 903 { 904 905 if (s == 0) 906 return; 907 while (*s) { 908 if (isupper(*s)) 909 *s = tolower(*s); 910 s++; 911 } 912 } 913 914 915 /* 916 * Compare function for qsort(). 917 * Sort first by section, then by prefix. 918 */ 919 static int 920 cmp(const void *arg1, const void *arg2) 921 { 922 int n; 923 char **p1 = (char **)arg1; 924 char **p2 = (char **)arg2; 925 926 /* By section */ 927 if ((n = strcmp(*p1 + 3, *p2 + 3)) != 0) 928 return (n); 929 930 /* By prefix reversed */ 931 return (strncmp(*p2, *p1, 3)); 932 } 933 934 935 /* 936 * Find a manpage. 937 */ 938 static int 939 manual(struct man_node *manp, char *name) 940 { 941 struct man_node *p; 942 struct man_node *local; 943 int ndirs = 0; 944 char *ldir; 945 char *ldirs[2]; 946 char *fullname = name; 947 char *slash; 948 949 if ((slash = strrchr(name, '/')) != NULL) 950 name = slash + 1; 951 952 /* For each path in MANPATH */ 953 found = 0; 954 955 for (p = manp; p != NULL; p = p->next) { 956 DPRINTF("-- Searching mandir: %s\n", p->path); 957 958 if (*localedir != '\0') { 959 ldir = addlocale(p->path); 960 ndirs = getdirs(ldir, NULL, 0); 961 if (ndirs != 0) { 962 ldirs[0] = ldir; 963 ldirs[1] = NULL; 964 local = build_manpath(ldirs, 0); 965 DPRINTF("-- Locale specific subdir: %s\n", 966 ldir); 967 mandir(local->secv, ldir, name, 1); 968 free_manp(local); 969 } 970 free(ldir); 971 } 972 973 /* 974 * Locale mandir not valid, man page in locale 975 * mandir not found, or -a option present 976 */ 977 if (ndirs == 0 || !found || all) 978 mandir(p->secv, p->path, name, 0); 979 980 if (found && !all) 981 break; 982 } 983 984 if (!found) { 985 if (sargs) { 986 (void) fprintf(stderr, gettext( 987 "No manual entry for %s in section(s) %s\n"), 988 fullname, mansec); 989 } else { 990 (void) fprintf(stderr, 991 gettext("No manual entry for %s\n"), fullname); 992 } 993 994 } 995 996 return (!found); 997 } 998 999 1000 /* 1001 * For a specified manual directory, read, store and sort section subdirs. 1002 * For each section specified, find and search matching subdirs. 1003 */ 1004 static void 1005 mandir(char **secv, char *path, char *name, int lspec) 1006 { 1007 DIR *dp; 1008 char **dirv; 1009 char **dv, **pdv; 1010 int len, dslen; 1011 1012 if ((dp = opendir(path)) == NULL) 1013 return; 1014 1015 if (lspec) 1016 DPRINTF("-- Searching mandir: %s\n", path); 1017 1018 sortdir(dp, &dirv); 1019 1020 /* Search in the order specified by MANSECTS */ 1021 for (; *secv; secv++) { 1022 len = strlen(*secv); 1023 for (dv = dirv; *dv; dv++) { 1024 dslen = strlen(*dv + 3); 1025 if (dslen > len) 1026 len = dslen; 1027 if (**secv == '\\') { 1028 if (strcmp(*secv + 1, *dv + 3) != 0) 1029 continue; 1030 } else if (strncasecmp(*secv, *dv + 3, len) != 0) { 1031 if (!all && 1032 (newsection = map_section(*secv, path)) 1033 == NULL) { 1034 continue; 1035 } 1036 if (newsection == NULL) 1037 newsection = ""; 1038 if (strncmp(newsection, *dv + 3, len) != 0) { 1039 continue; 1040 } 1041 } 1042 1043 if (searchdir(path, *dv, name) == 0) 1044 continue; 1045 1046 if (!all) { 1047 pdv = dirv; 1048 while (*pdv) { 1049 free(*pdv); 1050 pdv++; 1051 } 1052 (void) closedir(dp); 1053 free(dirv); 1054 return; 1055 } 1056 1057 if (all && **dv == 'm' && *(dv + 1) && 1058 strcmp(*(dv + 1) + 3, *dv + 3) == 0) 1059 dv++; 1060 } 1061 } 1062 pdv = dirv; 1063 while (*pdv != NULL) { 1064 free(*pdv); 1065 pdv++; 1066 } 1067 free(dirv); 1068 (void) closedir(dp); 1069 } 1070 1071 /* 1072 * Sort directories. 1073 */ 1074 static void 1075 sortdir(DIR *dp, char ***dirv) 1076 { 1077 struct dirent *d; 1078 char **dv; 1079 int maxentries = MAXDIRS; 1080 int entries = 0; 1081 1082 if ((dv = *dirv = malloc(sizeof (char *) * 1083 maxentries)) == NULL) 1084 err(1, "malloc"); 1085 dv = *dirv; 1086 1087 while ((d = readdir(dp))) { 1088 if (strcmp(d->d_name, ".") == 0 || 1089 strcmp(d->d_name, "..") == 0) 1090 continue; 1091 1092 if (strncmp(d->d_name, "man", 3) == 0 || 1093 strncmp(d->d_name, "cat", 3) == 0) { 1094 if ((*dv = strdup(d->d_name)) == NULL) 1095 err(1, "strdup"); 1096 dv++; 1097 entries++; 1098 if (entries == maxentries) { 1099 maxentries += MAXDIRS; 1100 if ((*dirv = realloc(*dirv, 1101 sizeof (char *) * maxentries)) == NULL) 1102 err(1, "realloc"); 1103 dv = *dirv + entries; 1104 } 1105 } 1106 } 1107 *dv = 0; 1108 1109 qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp); 1110 1111 } 1112 1113 1114 /* 1115 * Search a section subdir for a given manpage. 1116 */ 1117 static int 1118 searchdir(char *path, char *dir, char *name) 1119 { 1120 DIR *sdp; 1121 struct dirent *sd; 1122 char sectpath[MAXPATHLEN]; 1123 char file[MAXNAMLEN]; 1124 char dname[MAXPATHLEN]; 1125 char *last; 1126 int nlen; 1127 1128 (void) snprintf(sectpath, sizeof (sectpath), "%s/%s", path, dir); 1129 (void) snprintf(file, sizeof (file), "%s.", name); 1130 1131 if ((sdp = opendir(sectpath)) == NULL) 1132 return (0); 1133 1134 while ((sd = readdir(sdp))) { 1135 char *pname; 1136 1137 if ((pname = strdup(sd->d_name)) == NULL) 1138 err(1, "strdup"); 1139 if ((last = strrchr(pname, '.')) != NULL && 1140 (strcmp(last, ".gz") == 0 || strcmp(last, ".bz2") == 0)) 1141 *last = '\0'; 1142 last = strrchr(pname, '.'); 1143 nlen = last - pname; 1144 (void) snprintf(dname, sizeof (dname), "%.*s.", nlen, pname); 1145 if (strcmp(dname, file) == 0 || 1146 strcmp(pname, name) == 0) { 1147 (void) format(path, dir, name, sd->d_name); 1148 (void) closedir(sdp); 1149 free(pname); 1150 return (1); 1151 } 1152 free(pname); 1153 } 1154 (void) closedir(sdp); 1155 1156 return (0); 1157 } 1158 1159 /* 1160 * Check the hash table of old directory names to see if there is a 1161 * new directory name. 1162 */ 1163 static char * 1164 map_section(char *section, char *path) 1165 { 1166 int i; 1167 char fullpath[MAXPATHLEN]; 1168 1169 if (list) /* -l option fall through */ 1170 return (NULL); 1171 1172 for (i = 0; map[i].new_name != NULL; i++) { 1173 if (strcmp(section, map[i].old_name) == 0) { 1174 (void) snprintf(fullpath, sizeof (fullpath), 1175 "%s/man%s", path, map[i].new_name); 1176 if (!access(fullpath, R_OK | X_OK)) { 1177 return (map[i].new_name); 1178 } else { 1179 return (NULL); 1180 } 1181 } 1182 } 1183 1184 return (NULL); 1185 } 1186 1187 /* 1188 * Format the manpage. 1189 */ 1190 static int 1191 format(char *path, char *dir, char *name, char *pg) 1192 { 1193 char manpname[MAXPATHLEN], catpname[MAXPATHLEN]; 1194 char cmdbuf[BUFSIZ], tmpbuf[BUFSIZ]; 1195 char *cattool; 1196 int utf8 = 0; 1197 struct stat sbman, sbcat; 1198 1199 found++; 1200 1201 if (list) { 1202 (void) printf(gettext("%s(%s)\t-M %s\n"), name, dir + 3, path); 1203 return (-1); 1204 } 1205 1206 (void) snprintf(manpname, sizeof (manpname), "%s/man%s/%s", path, 1207 dir + 3, pg); 1208 (void) snprintf(catpname, sizeof (catpname), "%s/cat%s/%s", path, 1209 dir + 3, pg); 1210 1211 /* Can't do PS output if manpage doesn't exist */ 1212 if (stat(manpname, &sbman) != 0 && (psoutput|lintout)) 1213 return (-1); 1214 1215 /* 1216 * If both manpage and catpage do not exist, manpname is 1217 * broken symlink, most likely. 1218 */ 1219 if (stat(catpname, &sbcat) != 0 && stat(manpname, &sbman) != 0) 1220 err(1, "%s", manpname); 1221 1222 /* Setup cattool */ 1223 if (fnmatch("*.gz", manpname, 0) == 0) 1224 cattool = "gzcat"; 1225 else if (fnmatch("*.bz2", manpname, 0) == 0) 1226 cattool = "bzcat"; 1227 else 1228 cattool = "cat"; 1229 1230 /* Preprocess UTF-8 input with preconv (could be smarter) */ 1231 if (strstr(path, "UTF-8") != NULL) 1232 utf8 = 1; 1233 1234 if (psoutput) { 1235 (void) snprintf(cmdbuf, BUFSIZ, 1236 "cd %s; %s %s%s | mandoc -Tps | lp -Tpostscript", 1237 path, cattool, manpname, 1238 utf8 ? " | " PRECONV " -e UTF-8" : ""); 1239 DPRINTF("-- Using manpage: %s\n", manpname); 1240 goto cmd; 1241 } else if (lintout) { 1242 (void) snprintf(cmdbuf, BUFSIZ, 1243 "cd %s; %s %s%s | mandoc -Tlint", 1244 path, cattool, manpname, 1245 utf8 ? " | " PRECONV " -e UTF-8" : ""); 1246 DPRINTF("-- Linting manpage: %s\n", manpname); 1247 goto cmd; 1248 } 1249 1250 /* 1251 * Output catpage if: 1252 * - manpage doesn't exist 1253 * - output width is standard and catpage is recent enough 1254 */ 1255 if (stat(manpname, &sbman) != 0 || (manwidth == 0 && 1256 stat(catpname, &sbcat) == 0 && sbcat.st_mtime >= sbman.st_mtime)) { 1257 DPRINTF("-- Using catpage: %s\n", catpname); 1258 (void) snprintf(cmdbuf, BUFSIZ, "%s %s", pager, catpname); 1259 goto cmd; 1260 } 1261 1262 DPRINTF("-- Using manpage: %s\n", manpname); 1263 if (manwidth > 0) 1264 (void) snprintf(tmpbuf, BUFSIZ, "-Owidth=%d ", manwidth); 1265 (void) snprintf(cmdbuf, BUFSIZ, "cd %s; %s %s%s | mandoc -T%s %s| %s", 1266 path, cattool, manpname, 1267 utf8 ? " | " PRECONV " -e UTF-8 " : "", 1268 utf8 ? "utf8" : "ascii", (manwidth > 0) ? tmpbuf : "", pager); 1269 1270 cmd: 1271 DPRINTF("-- Command: %s\n", cmdbuf); 1272 1273 if (!debug) 1274 return (system(cmdbuf) == 0); 1275 else 1276 return (0); 1277 } 1278 1279 /* 1280 * Add <localedir> to the path. 1281 */ 1282 static char * 1283 addlocale(char *path) 1284 { 1285 char *tmp; 1286 1287 if (asprintf(&tmp, "%s/%s", path, localedir) == -1) 1288 err(1, "asprintf"); 1289 1290 return (tmp); 1291 } 1292 1293 /* 1294 * Get the order of sections from man.cf. 1295 */ 1296 static char * 1297 check_config(char *path) 1298 { 1299 FILE *fp; 1300 char *rc = NULL; 1301 char *sect; 1302 char fname[MAXPATHLEN]; 1303 char *line = NULL; 1304 size_t linecap = 0; 1305 1306 (void) snprintf(fname, MAXPATHLEN, "%s/%s", path, CONFIG); 1307 1308 if ((fp = fopen(fname, "r")) == NULL) 1309 return (NULL); 1310 1311 while (getline(&line, &linecap, fp) > 0) { 1312 if ((rc = strstr(line, "MANSECTS")) != NULL) 1313 break; 1314 } 1315 1316 (void) fclose(fp); 1317 1318 if (rc == NULL || (sect = strchr(line, '=')) == NULL) 1319 return (NULL); 1320 else 1321 return (++sect); 1322 } 1323 1324 1325 /* 1326 * Initialize the bintoman array with appropriate device and inode info. 1327 */ 1328 static void 1329 init_bintoman(void) 1330 { 1331 int i; 1332 struct stat sb; 1333 1334 for (i = 0; bintoman[i].bindir != NULL; i++) { 1335 if (stat(bintoman[i].bindir, &sb) == 0) { 1336 bintoman[i].dev = sb.st_dev; 1337 bintoman[i].ino = sb.st_ino; 1338 } else { 1339 bintoman[i].dev = NODEV; 1340 } 1341 } 1342 } 1343 1344 /* 1345 * If a duplicate is found, return 1. 1346 * If a duplicate is not found, add it to the dupnode list and return 0. 1347 */ 1348 static int 1349 dupcheck(struct man_node *mnp, struct dupnode **dnp) 1350 { 1351 struct dupnode *curdnp; 1352 struct secnode *cursnp; 1353 struct stat sb; 1354 int i; 1355 int rv = 1; 1356 int dupfound; 1357 1358 /* If the path doesn't exist, treat it as a duplicate */ 1359 if (stat(mnp->path, &sb) != 0) 1360 return (1); 1361 1362 /* If no sections were found in the man dir, treat it as duplicate */ 1363 if (mnp->secv == NULL) 1364 return (1); 1365 1366 /* 1367 * Find the dupnode structure for the previous time this directory 1368 * was looked at. Device and inode numbers are compared so that 1369 * directories that are reached via different paths (e.g. /usr/man and 1370 * /usr/share/man) are treated as equivalent. 1371 */ 1372 for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) { 1373 if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino) 1374 break; 1375 } 1376 1377 /* 1378 * First time this directory has been seen. Add a new node to the 1379 * head of the list. Since all entries are guaranteed to be unique 1380 * copy all sections to new node. 1381 */ 1382 if (curdnp == NULL) { 1383 if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL) 1384 err(1, "calloc"); 1385 for (i = 0; mnp->secv[i] != NULL; i++) { 1386 if ((cursnp = calloc(1, sizeof (struct secnode))) 1387 == NULL) 1388 err(1, "calloc"); 1389 cursnp->next = curdnp->secl; 1390 curdnp->secl = cursnp; 1391 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) 1392 err(1, "strdup"); 1393 } 1394 curdnp->dev = sb.st_dev; 1395 curdnp->ino = sb.st_ino; 1396 curdnp->next = *dnp; 1397 *dnp = curdnp; 1398 return (0); 1399 } 1400 1401 /* 1402 * Traverse the section vector in the man_node and the section list 1403 * in dupnode cache to eliminate all duplicates from man_node. 1404 */ 1405 for (i = 0; mnp->secv[i] != NULL; i++) { 1406 dupfound = 0; 1407 for (cursnp = curdnp->secl; cursnp != NULL; 1408 cursnp = cursnp->next) { 1409 if (strcmp(mnp->secv[i], cursnp->secp) == 0) { 1410 dupfound = 1; 1411 break; 1412 } 1413 } 1414 if (dupfound) { 1415 mnp->secv[i][0] = '\0'; 1416 continue; 1417 } 1418 1419 1420 /* 1421 * Update curdnp and set return value to indicate that this 1422 * was not all duplicates. 1423 */ 1424 if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL) 1425 err(1, "calloc"); 1426 cursnp->next = curdnp->secl; 1427 curdnp->secl = cursnp; 1428 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) 1429 err(1, "strdup"); 1430 rv = 0; 1431 } 1432 1433 return (rv); 1434 } 1435 1436 /* 1437 * Given a bindir, return corresponding mandir. 1438 */ 1439 static char * 1440 path_to_manpath(char *bindir) 1441 { 1442 char *mand, *p; 1443 int i; 1444 struct stat sb; 1445 1446 /* First look for known translations for specific bin paths */ 1447 if (stat(bindir, &sb) != 0) { 1448 return (NULL); 1449 } 1450 for (i = 0; bintoman[i].bindir != NULL; i++) { 1451 if (sb.st_dev == bintoman[i].dev && 1452 sb.st_ino == bintoman[i].ino) { 1453 if ((mand = strdup(bintoman[i].mandir)) == NULL) 1454 err(1, "strdup"); 1455 if ((p = strchr(mand, ',')) != NULL) 1456 *p = '\0'; 1457 if (stat(mand, &sb) != 0) { 1458 free(mand); 1459 return (NULL); 1460 } 1461 if (p != NULL) 1462 *p = ','; 1463 return (mand); 1464 } 1465 } 1466 1467 /* 1468 * No specific translation found. Try `dirname $bindir`/share/man 1469 * and `dirname $bindir`/man 1470 */ 1471 if ((mand = malloc(MAXPATHLEN)) == NULL) 1472 err(1, "malloc"); 1473 if (strlcpy(mand, bindir, MAXPATHLEN) >= MAXPATHLEN) { 1474 free(mand); 1475 return (NULL); 1476 } 1477 1478 /* 1479 * Advance to end of buffer, strip trailing /'s then remove last 1480 * directory component. 1481 */ 1482 for (p = mand; *p != '\0'; p++) 1483 ; 1484 for (; p > mand && *p == '/'; p--) 1485 ; 1486 for (; p > mand && *p != '/'; p--) 1487 ; 1488 if (p == mand && *p == '.') { 1489 if (realpath("..", mand) == NULL) { 1490 free(mand); 1491 return (NULL); 1492 } 1493 for (; *p != '\0'; p++) 1494 ; 1495 } else { 1496 *p = '\0'; 1497 } 1498 1499 if (strlcat(mand, "/share/man", MAXPATHLEN) >= MAXPATHLEN) { 1500 free(mand); 1501 return (NULL); 1502 } 1503 1504 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) { 1505 return (mand); 1506 } 1507 1508 /* 1509 * Strip the /share/man off and try /man 1510 */ 1511 *p = '\0'; 1512 if (strlcat(mand, "/man", MAXPATHLEN) >= MAXPATHLEN) { 1513 free(mand); 1514 return (NULL); 1515 } 1516 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) { 1517 return (mand); 1518 } 1519 1520 /* 1521 * No man or share/man directory found 1522 */ 1523 free(mand); 1524 return (NULL); 1525 } 1526 1527 /* 1528 * Free a linked list of dupnode structs. 1529 */ 1530 void 1531 free_dupnode(struct dupnode *dnp) { 1532 struct dupnode *dnp2; 1533 struct secnode *snp; 1534 1535 while (dnp != NULL) { 1536 dnp2 = dnp; 1537 dnp = dnp->next; 1538 while (dnp2->secl != NULL) { 1539 snp = dnp2->secl; 1540 dnp2->secl = dnp2->secl->next; 1541 free(snp->secp); 1542 free(snp); 1543 } 1544 free(dnp2); 1545 } 1546 } 1547 1548 /* 1549 * Print manp linked list to stdout. 1550 */ 1551 void 1552 print_manpath(struct man_node *manp) 1553 { 1554 char colon[2] = "\0\0"; 1555 char **secp; 1556 1557 for (; manp != NULL; manp = manp->next) { 1558 (void) printf("%s%s", colon, manp->path); 1559 colon[0] = ':'; 1560 1561 /* 1562 * If man.cf or a directory scan was used to create section 1563 * list, do not print section list again. If the output of 1564 * man -p is used to set MANPATH, subsequent runs of man 1565 * will re-read man.cf and/or scan man directories as 1566 * required. 1567 */ 1568 if (manp->defsrch != 0) 1569 continue; 1570 1571 for (secp = manp->secv; *secp != NULL; secp++) { 1572 /* 1573 * Section deduplication may have eliminated some 1574 * sections from the vector. Avoid displaying this 1575 * detail which would appear as ",," in output 1576 */ 1577 if ((*secp)[0] != '\0') 1578 (void) printf(",%s", *secp); 1579 } 1580 } 1581 (void) printf("\n"); 1582 } 1583 1584 static void 1585 usage_man(void) 1586 { 1587 1588 (void) fprintf(stderr, gettext( 1589 "usage: man [-alptw] [-M path] [-s section] name ...\n" 1590 " man [-M path] [-s section] -k keyword ...\n" 1591 " man [-M path] [-s section] -f keyword ...\n")); 1592 1593 exit(1); 1594 } 1595 1596 static void 1597 usage_whatapro(void) 1598 { 1599 1600 (void) fprintf(stderr, gettext( 1601 "usage: %s [-M path] [-s section] keyword ...\n"), 1602 whatis ? "whatis" : "apropos"); 1603 1604 exit(1); 1605 } 1606 1607 static void 1608 usage_catman(void) 1609 { 1610 (void) fprintf(stderr, gettext( 1611 "usage: catman [-M path] [-w]\n")); 1612 1613 exit(1); 1614 } 1615 1616 static void 1617 usage_makewhatis(void) 1618 { 1619 (void) fprintf(stderr, gettext("usage: makewhatis\n")); 1620 1621 exit(1); 1622 }