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 * Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2012, Josef 'Jeff' Sipek <jeffpc@31bits.net>. All rights reserved. 24 */ 25 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T. */ 27 /* All rights reserved. */ 28 29 /* 30 * University Copyright- Copyright (c) 1982, 1986, 1988 31 * The Regents of the University of California 32 * All Rights Reserved 33 * 34 * University Acknowledgment- Portions of this document are derived from 35 * software developed by the University of California, Berkeley, and its 36 * contributors. 37 */ 38 39 40 /* 41 * man 42 * links to apropos, whatis, and catman 43 * This version uses more for underlining and paging. 44 */ 45 46 #include <stdio.h> 47 #include <ctype.h> 48 #include <sgtty.h> 49 #include <sys/param.h> 50 #include <sys/types.h> 51 #include <sys/stat.h> 52 #include <signal.h> 53 #include <string.h> 54 #include <malloc.h> 55 #include <dirent.h> 56 #include <errno.h> 57 #include <fcntl.h> 58 #include <locale.h> 59 #include <stdlib.h> 60 #include <unistd.h> 61 #include <memory.h> 62 #include <limits.h> 63 #include <wchar.h> 64 65 #define MACROF "tmac.an" /* name of <locale> macro file */ 66 #define TMAC_AN "-man" /* default macro file */ 67 68 /* 69 * The default search path for man subtrees. 70 */ 71 72 #define MANDIR "/usr/share/man" /* default mandir */ 73 #define MAKEWHATIS "/usr/lib/makewhatis" 74 #define WHATIS "windex" 75 #define TEMPLATE "/tmp/mpXXXXXX" 76 #define CONFIG "man.cf" 77 78 /* 79 * Names for formatting and display programs. The values given 80 * below are reasonable defaults, but sites with source may 81 * wish to modify them to match the local environment. The 82 * value for TCAT is particularly problematic as there's no 83 * accepted standard value available for it. (The definition 84 * below assumes C.A.T. troff output and prints it). 85 */ 86 87 #define MORE "more -s" /* default paging filter */ 88 #define CAT_S "/usr/bin/cat -s" /* for '-' opt (no more) */ 89 #define CAT_ "/usr/bin/cat" /* for when output is not a tty */ 90 #define TROFF "troff" /* local name for troff */ 91 #define TCAT "lp -c -T troff" /* command to "display" troff output */ 92 93 #define SOLIMIT 10 /* maximum allowed .so chain length */ 94 #define MAXDIRS 128 /* max # of subdirs per manpath */ 95 #define MAXPAGES 128 /* max # for multiple pages */ 96 #define PLEN 3 /* prefix length {man, cat, fmt} */ 97 #define TMPLEN 7 /* length of tmpfile prefix */ 98 #define MAXTOKENS 64 99 100 #define DOT_SO ".so " 101 #define PREPROC_SPEC "'\\\" " 102 103 #define DPRINTF if (debug && !catmando) \ 104 (void) printf 105 106 #define sys(s) (debug ? ((void)puts(s), 0) : system(s)) 107 #define eq(a, b) (strcmp(a, b) == 0) 108 #define match(a, b, c) (strncmp(a, b, c) == 0) 109 110 #define ISDIR(A) ((A.st_mode & S_IFMT) == S_IFDIR) 111 112 #define SROFF_CMD "/usr/lib/sgml/sgml2roff" /* sgml converter */ 113 #define MANDIRNAME "man" /* man directory */ 114 #define SGMLDIR "sman" /* sman directory */ 115 #define SGML_SYMBOL "<!DOCTYPE" /* a sgml file should contain this */ 116 #define SGML_SYMBOL_LEN 9 /* length of SGML_SYMBOL */ 117 118 /* 119 * Directory mapping of old directories to new directories 120 */ 121 122 typedef struct { 123 char *old_name; 124 char *new_name; 125 } map_entry; 126 127 static const map_entry map[] = { 128 { "3b", "3ucb" }, 129 { "3e", "3elf" }, 130 { "3g", "3gen" }, 131 { "3k", "3kstat" }, 132 { "3n", "3socket" }, 133 { "3r", "3rt" }, 134 { "3s", "3c" }, 135 { "3t", "3thr" }, 136 { "3x", "3curses" }, 137 { "3xc", "3xcurses" }, 138 { "3xn", "3xnet" } 139 }; 140 141 /* 142 * A list of known preprocessors to precede the formatter itself 143 * in the formatting pipeline. Preprocessors are specified by 144 * starting a manual page with a line of the form: 145 * '\" X 146 * where X is a string consisting of letters from the p_tag fields 147 * below. 148 */ 149 static const struct preprocessor { 150 char p_tag; 151 char *p_nroff, 152 *p_troff, 153 *p_stdin_char; 154 } preprocessors [] = { 155 {'c', "cw", "cw", "-"}, 156 {'e', "neqn /usr/share/lib/pub/eqnchar", 157 "eqn /usr/share/lib/pub/eqnchar", "-"}, 158 {'p', "gpic", "gpic", "-"}, 159 {'r', "refer", "refer", "-"}, 160 {'t', "tbl", "tbl", ""}, 161 {'v', "vgrind -f", "vgrind -f", "-"}, 162 {0, 0, 0, 0} 163 }; 164 165 struct suffix { 166 char *ds; 167 char *fs; 168 }; 169 170 /* 171 * Flags that control behavior of build_manpath() 172 * 173 * BMP_ISPATH pathv is a vector constructed from PATH. 174 * Perform appropriate path translations for 175 * manpath. 176 * BMP_APPEND_MANDIR Add /usr/share/man to the end if it 177 * hasn't already appeared earlier. 178 * BMP_FALLBACK_MANDIR Append /usr/share/man only if no other 179 * manpath (including derived from PATH) 180 * elements are valid. 181 */ 182 #define BMP_ISPATH 1 183 #define BMP_APPEND_MANDIR 2 184 #define BMP_FALLBACK_MANDIR 4 185 186 /* 187 * When doing equality comparisons of directories, device and inode 188 * comparisons are done. The dupsec and dupnode structures are used 189 * to form a list of lists for this processing. 190 */ 191 struct secnode { 192 char *secp; 193 struct secnode *next; 194 }; 195 struct dupnode { 196 dev_t dev; /* from struct stat st_dev */ 197 ino_t ino; /* from struct stat st_ino */ 198 struct secnode *secl; /* sections already considered */ 199 struct dupnode *next; 200 }; 201 202 /* 203 * Map directories that may appear in PATH to the corresponding 204 * man directory 205 */ 206 static struct pathmap { 207 char *bindir; 208 char *mandir; 209 dev_t dev; 210 ino_t ino; 211 } bintoman[] = { 212 {"/sbin", "/usr/share/man,1m", 0, 0}, 213 {"/usr/sbin", "/usr/share/man,1m", 0, 0}, 214 {"/usr/ucb", "/usr/share/man,1b", 0, 0}, 215 {"/usr/bin/X11", "/usr/X11/share/man", 0, 0}, 216 /* 217 * Restrict to section 1 so that whatis /usr/{,xpg4,xpg6}/bin/ls 218 * does not confuse users with section 1 and 1b 219 */ 220 {"/usr/bin", "/usr/share/man,1,1m,1s,1t,1c", 0, 0}, 221 {"/usr/xpg4/bin", "/usr/share/man,1", 0, 0}, 222 {"/usr/xpg6/bin", "/usr/share/man,1", 0, 0}, 223 {NULL, NULL, 0, 0} 224 }; 225 226 /* 227 * Subdirectories to search for unformatted/formatted man page 228 * versions, in nroff and troff variations. The searching 229 * code in manual() is structured to expect there to be two 230 * subdirectories apiece, the first for unformatted files 231 * and the second for formatted ones. 232 */ 233 static char *nroffdirs[] = { "man", "cat", 0 }; 234 static char *troffdirs[] = { "man", "fmt", 0 }; 235 236 #define MAN_USAGE "\ 237 usage:\tman [-] [-adFlprt] [-M path] [-T macro-package ] [ -s section ] \ 238 name ...\n\ 239 \tman [-M path] -k keyword ...\n\tman [-M path] -f file ..." 240 #define CATMAN_USAGE "\ 241 usage:\tcatman [-p] [-c|-ntw] [-M path] [-T macro-package ] [sections]" 242 243 static char *opts[] = { 244 "FfkrpP:M:T:ts:lad", /* man */ 245 "wpnP:M:T:tc" /* catman */ 246 }; 247 248 struct man_node { 249 char *path; /* mandir path */ 250 char **secv; /* submandir suffices */ 251 int defsrch; /* hint for man -p to avoid section list */ 252 int frompath; /* hint for man -d and catman -p */ 253 struct man_node *next; 254 }; 255 256 static char *pages[MAXPAGES]; 257 static char **endp = pages; 258 259 /* 260 * flags (options) 261 */ 262 static int nomore; 263 static int troffit; 264 static int debug; 265 static int Tflag; 266 static int sargs; 267 static int margs; 268 static int force; 269 static int found; 270 static int list; 271 static int all; 272 static int whatis; 273 static int apropos; 274 static int catmando; 275 static int nowhatis; 276 static int whatonly; 277 static int compargs; /* -c option for catman */ 278 static int printmp; 279 280 static char *CAT = CAT_; 281 static char macros[MAXPATHLEN]; 282 static char *mansec; 283 static char *pager; 284 static char *troffcmd; 285 static char *troffcat; 286 static char **subdirs; 287 288 static char *check_config(char *); 289 static struct man_node *build_manpath(char **, int); 290 static void getpath(struct man_node *, char **); 291 static void getsect(struct man_node *, char **); 292 static void get_all_sect(struct man_node *); 293 static void catman(struct man_node *, char **, int); 294 static int makecat(char *, char **, int); 295 static int getdirs(char *, char ***, short); 296 static void whatapro(struct man_node *, char *, int); 297 static void lookup_windex(char *, char *, char **); 298 static int icmp(wchar_t *, wchar_t *); 299 static void more(char **, int); 300 static void cleanup(char **); 301 static void bye(int); 302 static char **split(char *, char); 303 static void freev(char **); 304 static void fullpaths(struct man_node **); 305 static void lower(char *); 306 static int cmp(const void *, const void *); 307 static int manual(struct man_node *, char *); 308 static void mandir(char **, char *, char *); 309 static void sortdir(DIR *, char ***); 310 static int searchdir(char *, char *, char *); 311 static int windex(char **, char *, char *); 312 static void section(struct suffix *, char *); 313 static int bfsearch(FILE *, char **, char *, char **); 314 static int compare(char *, char *, char **); 315 static int format(char *, char *, char *, char *); 316 static char *addlocale(char *); 317 static int get_manconfig(FILE *, char *); 318 static void malloc_error(void); 319 static int sgmlcheck(const char *); 320 static char *map_section(char *, char *); 321 static void free_manp(struct man_node *manp); 322 static void init_bintoman(void); 323 static char *path_to_manpath(char *); 324 static int dupcheck(struct man_node *, struct dupnode **); 325 static void free_dupnode(struct dupnode *); 326 static void print_manpath(struct man_node *, char *); 327 328 /* 329 * This flag is used when the SGML-to-troff converter 330 * is absent - all the SGML searches are bypassed. 331 */ 332 static int no_sroff = 0; 333 334 /* 335 * This flag is used to describe the case where we've found 336 * an SGML formatted manpage in the sman directory, we haven't 337 * found a troff formatted manpage, and we don't have the SGML to troff 338 * conversion utility on the system. 339 */ 340 static int sman_no_man_no_sroff; 341 342 static char language[PATH_MAX + 1]; /* LC_MESSAGES */ 343 static char localedir[PATH_MAX + 1]; /* locale specific path component */ 344 345 static int defaultmandir = 1; /* if processing default mandir, 1 */ 346 347 static char *newsection = NULL; 348 349 int 350 main(int argc, char *argv[]) 351 { 352 int badopts = 0; 353 int c; 354 char **pathv; 355 char *cmdname; 356 char *manpath = NULL; 357 static struct man_node *manpage = NULL; 358 int bmp_flags = 0; 359 int err = 0; 360 361 if (access(SROFF_CMD, F_OK | X_OK) != 0) 362 no_sroff = 1; 363 364 (void) setlocale(LC_ALL, ""); 365 (void) strcpy(language, setlocale(LC_MESSAGES, (char *)0)); 366 if (strcmp("C", language) != 0) 367 (void) sprintf(localedir, "%s", language); 368 369 #if !defined(TEXT_DOMAIN) 370 #define TEXT_DOMAIN "SYS_TEST" 371 #endif 372 (void) textdomain(TEXT_DOMAIN); 373 374 (void) strcpy(macros, TMAC_AN); 375 376 /* 377 * get base part of command name 378 */ 379 if ((cmdname = strrchr(argv[0], '/')) != NULL) 380 cmdname++; 381 else 382 cmdname = argv[0]; 383 384 if (eq(cmdname, "apropos") || eq(cmdname, "whatis")) { 385 whatis++; 386 apropos = (*cmdname == 'a'); 387 if ((optind = 1) == argc) { 388 (void) fprintf(stderr, gettext("%s what?\n"), cmdname); 389 exit(2); 390 } 391 goto doargs; 392 } else if (eq(cmdname, "catman")) 393 catmando++; 394 395 opterr = 0; 396 while ((c = getopt(argc, argv, opts[catmando])) != -1) 397 switch (c) { 398 399 /* 400 * man specific options 401 */ 402 case 'k': 403 apropos++; 404 /*FALLTHROUGH*/ 405 case 'f': 406 whatis++; 407 break; 408 case 'F': 409 force++; /* do lookups the hard way */ 410 break; 411 case 's': 412 mansec = optarg; 413 sargs++; 414 break; 415 case 'r': 416 nomore++, troffit++; 417 break; 418 case 'l': 419 list++; /* implies all */ 420 /*FALLTHROUGH*/ 421 case 'a': 422 all++; 423 break; 424 case 'd': 425 debug++; 426 break; 427 /* 428 * man and catman use -p differently. In catman it 429 * enables debug mode and in man it prints the (possibly 430 * derived from PATH or name operand) MANPATH. 431 */ 432 case 'p': 433 if (catmando == 0) { 434 printmp++; 435 } else { 436 debug++; 437 } 438 break; 439 case 'n': 440 nowhatis++; 441 break; 442 case 'w': 443 whatonly++; 444 break; 445 case 'c': /* n|troff compatibility */ 446 if (no_sroff) 447 (void) fprintf(stderr, gettext( 448 "catman: SGML conversion not " 449 "available -- -c flag ignored\n")); 450 else 451 compargs++; 452 continue; 453 454 /* 455 * shared options 456 */ 457 case 'P': /* Backwards compatibility */ 458 case 'M': /* Respecify path for man pages. */ 459 manpath = optarg; 460 margs++; 461 break; 462 case 'T': /* Respecify man macros */ 463 (void) strcpy(macros, optarg); 464 Tflag++; 465 break; 466 case 't': 467 troffit++; 468 break; 469 case '?': 470 badopts++; 471 } 472 473 /* 474 * Bad options or no args? 475 * (man -p and catman don't need args) 476 */ 477 if (badopts || (!catmando && !printmp && optind == argc)) { 478 (void) fprintf(stderr, "%s\n", catmando ? 479 gettext(CATMAN_USAGE) : gettext(MAN_USAGE)); 480 exit(2); 481 } 482 483 if (compargs && (nowhatis || whatonly || troffit)) { 484 (void) fprintf(stderr, "%s\n", gettext(CATMAN_USAGE)); 485 (void) fprintf(stderr, gettext( 486 "-c option cannot be used with [-w][-n][-t]\n")); 487 exit(2); 488 } 489 490 if (sargs && margs && catmando) { 491 (void) fprintf(stderr, "%s\n", gettext(CATMAN_USAGE)); 492 exit(2); 493 } 494 495 if (troffit == 0 && nomore == 0 && !isatty(fileno(stdout))) 496 nomore++; 497 498 /* 499 * Collect environment information. 500 */ 501 if (troffit) { 502 if ((troffcmd = getenv("TROFF")) == NULL) 503 troffcmd = TROFF; 504 if ((troffcat = getenv("TCAT")) == NULL) 505 troffcat = TCAT; 506 } else { 507 if (((pager = getenv("PAGER")) == NULL) || 508 (*pager == NULL)) 509 pager = MORE; 510 } 511 512 doargs: 513 subdirs = troffit ? troffdirs : nroffdirs; 514 515 init_bintoman(); 516 517 if (manpath == NULL && (manpath = getenv("MANPATH")) == NULL) { 518 if ((manpath = getenv("PATH")) != NULL) { 519 bmp_flags = BMP_ISPATH | BMP_APPEND_MANDIR; 520 } else { 521 manpath = MANDIR; 522 } 523 } 524 525 pathv = split(manpath, ':'); 526 527 manpage = build_manpath(pathv, bmp_flags); 528 529 /* release pathv allocated by split() */ 530 freev(pathv); 531 532 /* 533 * Since we can't make use of GNU troff, set the path to ensure we 534 * find the one in /usr/bin first. 535 */ 536 if (putenv("PATH=/usr/bin") != 0) { 537 perror("putenv"); 538 exit(1); 539 } 540 541 fullpaths(&manpage); 542 543 if (catmando) { 544 catman(manpage, argv+optind, argc-optind); 545 exit(0); 546 } 547 548 /* 549 * The manual routine contains windows during which 550 * termination would leave a temp file behind. Thus 551 * we blanket the whole thing with a clean-up routine. 552 */ 553 if (signal(SIGINT, SIG_IGN) == SIG_DFL) { 554 (void) signal(SIGINT, bye); 555 (void) signal(SIGQUIT, bye); 556 (void) signal(SIGTERM, bye); 557 } 558 559 /* 560 * "man -p" without operands 561 */ 562 if ((printmp != 0) && (optind == argc)) { 563 print_manpath(manpage, NULL); 564 exit(0); 565 } 566 567 for (; optind < argc; optind++) { 568 if (strcmp(argv[optind], "-") == 0) { 569 nomore++; 570 CAT = CAT_S; 571 } else { 572 char *cmd; 573 static struct man_node *mp; 574 char *pv[2]; 575 576 /* 577 * If full path to command specified, customize 578 * manpath accordingly 579 */ 580 if ((cmd = strrchr(argv[optind], '/')) != NULL) { 581 *cmd = '\0'; 582 if ((pv[0] = strdup(argv[optind])) == NULL) { 583 malloc_error(); 584 } 585 pv[1] = NULL; 586 *cmd = '/'; 587 mp = build_manpath(pv, 588 BMP_ISPATH|BMP_FALLBACK_MANDIR); 589 } else { 590 mp = manpage; 591 } 592 593 if (whatis) { 594 whatapro(mp, argv[optind], apropos); 595 } else if (printmp != 0) { 596 print_manpath(mp, argv[optind]); 597 } else { 598 err += manual(mp, argv[optind]); 599 } 600 601 if (mp != NULL && mp != manpage) { 602 free(pv[0]); 603 free_manp(mp); 604 } 605 } 606 } 607 return (err == 0 ? 0 : 1); 608 /*NOTREACHED*/ 609 } 610 611 /* 612 * This routine builds the manpage structure from MANPATH or PATH, 613 * depending on flags. See BMP_* definitions above for valid 614 * flags. 615 * 616 * Assumes pathv elements were malloc'd, as done by split(). 617 * Elements may be freed and reallocated to have different contents. 618 */ 619 620 static struct man_node * 621 build_manpath(char **pathv, int flags) 622 { 623 struct man_node *manpage = NULL; 624 struct man_node *currp = NULL; 625 struct man_node *lastp = NULL; 626 char **p; 627 char **q; 628 char *mand = NULL; 629 char *mandir = MANDIR; 630 int s; 631 struct dupnode *didup = NULL; 632 struct stat sb; 633 634 s = sizeof (struct man_node); 635 for (p = pathv; *p; ) { 636 637 if (flags & BMP_ISPATH) { 638 if ((mand = path_to_manpath(*p)) == NULL) { 639 goto next; 640 } 641 free(*p); 642 *p = mand; 643 } 644 q = split(*p, ','); 645 if (stat(q[0], &sb) != 0 || (sb.st_mode & S_IFDIR) == 0) { 646 freev(q); 647 goto next; 648 } 649 650 if (access(q[0], R_OK|X_OK) != 0) { 651 if (catmando) { 652 (void) fprintf(stderr, 653 gettext("%s is not accessible.\n"), 654 q[0]); 655 (void) fflush(stderr); 656 } 657 } else { 658 659 /* 660 * Some element exists. Do not append MANDIR as a 661 * fallback. 662 */ 663 flags &= ~BMP_FALLBACK_MANDIR; 664 665 if ((currp = (struct man_node *)calloc(1, s)) == NULL) { 666 malloc_error(); 667 } 668 669 currp->frompath = (flags & BMP_ISPATH); 670 671 if (manpage == NULL) { 672 lastp = manpage = currp; 673 } 674 675 getpath(currp, p); 676 getsect(currp, p); 677 678 /* 679 * If there are no new elements in this path, 680 * do not add it to the manpage list 681 */ 682 if (dupcheck(currp, &didup) != 0) { 683 freev(currp->secv); 684 free(currp); 685 } else { 686 currp->next = NULL; 687 if (currp != manpage) { 688 lastp->next = currp; 689 } 690 lastp = currp; 691 } 692 } 693 freev(q); 694 next: 695 /* 696 * Special handling of appending MANDIR. 697 * After all pathv elements have been processed, append MANDIR 698 * if needed. 699 */ 700 if (p == &mandir) { 701 break; 702 } 703 p++; 704 if (*p != NULL) { 705 continue; 706 } 707 if (flags & (BMP_APPEND_MANDIR|BMP_FALLBACK_MANDIR)) { 708 p = &mandir; 709 flags &= ~BMP_ISPATH; 710 } 711 } 712 713 free_dupnode(didup); 714 715 return (manpage); 716 } 717 718 /* 719 * Stores the mandir path into the manp structure. 720 */ 721 722 static void 723 getpath(struct man_node *manp, char **pv) 724 { 725 char *s; 726 int i = 0; 727 728 s = *pv; 729 730 while (*s != NULL && *s != ',') 731 i++, s++; 732 733 manp->path = (char *)malloc(i+1); 734 if (manp->path == NULL) 735 malloc_error(); 736 (void) strncpy(manp->path, *pv, i); 737 *(manp->path + i) = '\0'; 738 } 739 740 /* 741 * Stores the mandir's corresponding sections (submandir 742 * directories) into the manp structure. 743 */ 744 745 static void 746 getsect(struct man_node *manp, char **pv) 747 { 748 char *sections; 749 char **sectp; 750 751 if (sargs) { 752 manp->secv = split(mansec, ','); 753 754 for (sectp = manp->secv; *sectp; sectp++) 755 lower(*sectp); 756 } else if ((sections = strchr(*pv, ',')) != NULL) { 757 if (debug) { 758 if (manp->frompath != 0) { 759 /* 760 * TRANSLATION_NOTE - message for man -d or catman -p 761 * ex. /usr/share/man: derived from PATH, MANSECTS=,1b 762 */ 763 (void) printf(gettext( 764 "%s: derived from PATH, MANSECTS=%s\n"), 765 manp->path, sections); 766 } else { 767 /* 768 * TRANSLATION_NOTE - message for man -d or catman -p 769 * ex. /usr/share/man: from -M option, MANSECTS=,1,2,3c 770 */ 771 (void) fprintf(stdout, gettext( 772 "%s: from -M option, MANSECTS=%s\n"), 773 manp->path, sections); 774 } 775 } 776 manp->secv = split(++sections, ','); 777 for (sectp = manp->secv; *sectp; sectp++) 778 lower(*sectp); 779 780 if (*manp->secv == NULL) 781 get_all_sect(manp); 782 } else if ((sections = check_config(*pv)) != NULL) { 783 manp->defsrch = 1; 784 /* 785 * TRANSLATION_NOTE - message for man -d or catman -p 786 * ex. /usr/share/man: from man.cf, MANSECTS=1,1m,1c 787 */ 788 if (debug) 789 (void) fprintf(stdout, gettext( 790 "%s: from %s, MANSECTS=%s\n"), 791 manp->path, CONFIG, sections); 792 manp->secv = split(sections, ','); 793 794 for (sectp = manp->secv; *sectp; sectp++) 795 lower(*sectp); 796 797 if (*manp->secv == NULL) 798 get_all_sect(manp); 799 } else { 800 manp->defsrch = 1; 801 /* 802 * TRANSLATION_NOTE - message for man -d or catman -p 803 * if man.cf has not been found or sections has not been specified 804 * man/catman searches the sections lexicographically. 805 */ 806 if (debug) 807 (void) fprintf(stdout, gettext( 808 "%s: search the sections lexicographically\n"), 809 manp->path); 810 manp->secv = NULL; 811 get_all_sect(manp); 812 } 813 } 814 815 /* 816 * Get suffices of all sub-mandir directories in a mandir. 817 */ 818 819 static void 820 get_all_sect(struct man_node *manp) 821 { 822 DIR *dp; 823 char **dirv; 824 char **dv; 825 char **p; 826 char *prev = NULL; 827 char *tmp = NULL; 828 int plen; 829 int maxentries = MAXTOKENS; 830 int entries = 0; 831 832 if ((dp = opendir(manp->path)) == 0) 833 return; 834 835 /* 836 * sortdir() allocates memory for dirv and dirv[]. 837 */ 838 sortdir(dp, &dirv); 839 840 (void) closedir(dp); 841 842 if (manp->secv == NULL) { 843 /* 844 * allocates memory for manp->secv only if it's NULL 845 */ 846 manp->secv = (char **)malloc(maxentries * sizeof (char *)); 847 if (manp->secv == NULL) 848 malloc_error(); 849 } 850 851 for (dv = dirv, p = manp->secv; *dv; dv++) { 852 plen = PLEN; 853 if (match(*dv, SGMLDIR, PLEN+1)) 854 ++plen; 855 856 if (strcmp(*dv, CONFIG) == 0) { 857 /* release memory allocated by sortdir */ 858 free(*dv); 859 continue; 860 } 861 862 if (tmp != NULL) 863 free(tmp); 864 tmp = strdup(*dv + plen); 865 if (tmp == NULL) 866 malloc_error(); 867 (void) sprintf(tmp, "%s", *dv + plen); 868 869 if (prev != NULL) { 870 if (strcmp(prev, tmp) == 0) { 871 /* release memory allocated by sortdir */ 872 free(*dv); 873 continue; 874 } 875 } 876 877 if (prev != NULL) 878 free(prev); 879 prev = strdup(*dv + plen); 880 if (prev == NULL) 881 malloc_error(); 882 (void) sprintf(prev, "%s", *dv + plen); 883 /* 884 * copy the string in (*dv + plen) to *p 885 */ 886 *p = strdup(*dv + plen); 887 if (*p == NULL) 888 malloc_error(); 889 p++; 890 entries++; 891 if (entries == maxentries) { 892 maxentries += MAXTOKENS; 893 manp->secv = (char **)realloc(manp->secv, 894 sizeof (char *) * maxentries); 895 if (manp->secv == NULL) 896 malloc_error(); 897 p = manp->secv + entries; 898 } 899 /* release memory allocated by sortdir */ 900 free(*dv); 901 } 902 *p = 0; 903 /* release memory allocated by sortdir */ 904 free(dirv); 905 } 906 907 /* 908 * Format man pages (build cat pages); if no 909 * sections are specified, build all of them. 910 * When building cat pages: 911 * catman() tries to build cat pages for locale specific 912 * man dirs first. Then, catman() tries to build cat pages 913 * for the default man dir (for C locale like /usr/share/man) 914 * regardless of the locale. 915 * When building windex file: 916 * catman() tries to build windex file for locale specific 917 * man dirs first. Then, catman() tries to build windex file 918 * for the default man dir (for C locale like /usr/share/man) 919 * regardless of the locale. 920 */ 921 922 static void 923 catman(struct man_node *manp, char **argv, int argc) 924 { 925 char cmdbuf[BUFSIZ]; 926 char **dv; 927 int changed; 928 struct man_node *p; 929 int ndirs = 0; 930 char *ldir; 931 int i; 932 struct dupnode *dnp = NULL; 933 char **realsecv; 934 /* 935 * May be overwritten in dupcheck() so must be kept out of .rodata. 936 */ 937 char fakename[] = " catman "; 938 char *fakesecv[2]; 939 940 fakesecv[0] = fakename; 941 fakesecv[1] = NULL; 942 943 for (p = manp; p != NULL; p = p->next) { 944 /* 945 * prevent catman from doing very heavy lifting multiple 946 * times on some directory 947 */ 948 realsecv = p->secv; 949 p->secv = fakesecv; 950 if (dupcheck(p, &dnp) != 0) { 951 p->secv = realsecv; 952 continue; 953 } 954 955 /* 956 * TRANSLATION_NOTE - message for catman -p 957 * ex. mandir path = /usr/share/man 958 */ 959 if (debug) 960 (void) fprintf(stdout, gettext( 961 "\nmandir path = %s\n"), p->path); 962 ndirs = 0; 963 964 /* 965 * Build cat pages 966 * addlocale() allocates memory and returns it 967 */ 968 ldir = addlocale(p->path); 969 if (!whatonly) { 970 if (*localedir != '\0') { 971 if (defaultmandir) 972 defaultmandir = 0; 973 /* getdirs allocate memory for dv */ 974 ndirs = getdirs(ldir, &dv, 1); 975 if (ndirs != 0) { 976 changed = argc ? 977 makecat(ldir, argv, argc) : 978 makecat(ldir, dv, ndirs); 979 /* release memory by getdirs */ 980 for (i = 0; i < ndirs; i++) { 981 free(dv[i]); 982 } 983 free(dv); 984 } 985 } 986 987 /* default man dir is always processed */ 988 defaultmandir = 1; 989 ndirs = getdirs(p->path, &dv, 1); 990 changed = argc ? 991 makecat(p->path, argv, argc) : 992 makecat(p->path, dv, ndirs); 993 /* release memory allocated by getdirs */ 994 for (i = 0; i < ndirs; i++) { 995 free(dv[i]); 996 } 997 free(dv); 998 } 999 /* 1000 * Build whatis database 1001 * print error message if locale is set and man dir not found 1002 * won't build it at all if -c option is on 1003 */ 1004 if (!compargs && (whatonly || (!nowhatis && changed))) { 1005 if (*localedir != '\0') { 1006 /* just count the number of ndirs */ 1007 if ((ndirs = getdirs(ldir, NULL, 0)) != 0) { 1008 (void) sprintf(cmdbuf, 1009 "/usr/bin/sh %s %s", 1010 MAKEWHATIS, ldir); 1011 (void) sys(cmdbuf); 1012 } 1013 } 1014 /* whatis database of the default man dir */ 1015 /* will be always built in C locale. */ 1016 (void) sprintf(cmdbuf, 1017 "/usr/bin/sh %s %s", 1018 MAKEWHATIS, p->path); 1019 (void) sys(cmdbuf); 1020 } 1021 /* release memory allocated by addlocale() */ 1022 free(ldir); 1023 } 1024 free_dupnode(dnp); 1025 } 1026 1027 /* 1028 * Build cat pages for given sections 1029 */ 1030 1031 static int 1032 makecat(char *path, char **dv, int ndirs) 1033 { 1034 DIR *dp, *sdp; 1035 struct dirent *d; 1036 struct stat sbuf; 1037 char mandir[MAXPATHLEN+1]; 1038 char smandir[MAXPATHLEN+1]; 1039 char catdir[MAXPATHLEN+1]; 1040 char *dirp, *sdirp; 1041 int i, fmt; 1042 int manflag, smanflag; 1043 1044 for (i = fmt = 0; i < ndirs; i++) { 1045 (void) snprintf(mandir, MAXPATHLEN, "%s/%s%s", 1046 path, MANDIRNAME, dv[i]); 1047 (void) snprintf(smandir, MAXPATHLEN, "%s/%s%s", 1048 path, SGMLDIR, dv[i]); 1049 (void) snprintf(catdir, MAXPATHLEN, "%s/%s%s", 1050 path, subdirs[1], dv[i]); 1051 dirp = strrchr(mandir, '/') + 1; 1052 sdirp = strrchr(smandir, '/') + 1; 1053 1054 manflag = smanflag = 0; 1055 1056 if ((dp = opendir(mandir)) != NULL) 1057 manflag = 1; 1058 1059 if (!no_sroff && (sdp = opendir(smandir)) != NULL) 1060 smanflag = 1; 1061 1062 if (dp == 0 && sdp == 0) { 1063 if (strcmp(mandir, CONFIG) == 0) 1064 perror(mandir); 1065 continue; 1066 } 1067 /* 1068 * TRANSLATION_NOTE - message for catman -p 1069 * ex. Building cat pages for mandir = /usr/share/man/ja 1070 */ 1071 if (debug) 1072 (void) fprintf(stdout, gettext( 1073 "Building cat pages for mandir = %s\n"), path); 1074 1075 if (!compargs && stat(catdir, &sbuf) < 0) { 1076 (void) umask(02); 1077 /* 1078 * TRANSLATION_NOTE - message for catman -p 1079 * ex. mkdir /usr/share/man/ja/cat3c 1080 */ 1081 if (debug) 1082 (void) fprintf(stdout, gettext("mkdir %s\n"), 1083 catdir); 1084 else { 1085 if (mkdir(catdir, 0755) < 0) { 1086 perror(catdir); 1087 continue; 1088 } 1089 (void) chmod(catdir, 0755); 1090 } 1091 } 1092 1093 /* 1094 * if it is -c option of catman, if there is no 1095 * coresponding man dir for sman files to go to, 1096 * make the man dir 1097 */ 1098 1099 if (compargs && !manflag) { 1100 if (mkdir(mandir, 0755) < 0) { 1101 perror(mandir); 1102 continue; 1103 } 1104 (void) chmod(mandir, 0755); 1105 } 1106 1107 if (smanflag) { 1108 while ((d = readdir(sdp))) { 1109 if (eq(".", d->d_name) || eq("..", d->d_name)) 1110 continue; 1111 1112 if (format(path, sdirp, (char *)0, d->d_name) 1113 > 0) 1114 fmt++; 1115 } 1116 } 1117 1118 if (manflag && !compargs) { 1119 while ((d = readdir(dp))) { 1120 if (eq(".", d->d_name) || eq("..", d->d_name)) 1121 continue; 1122 1123 if (format(path, dirp, (char *)0, d->d_name) 1124 > 0) 1125 fmt++; 1126 } 1127 } 1128 1129 if (manflag) 1130 (void) closedir(dp); 1131 1132 if (smanflag) 1133 (void) closedir(sdp); 1134 1135 } 1136 return (fmt); 1137 } 1138 1139 1140 /* 1141 * Get all "man" and "sman" dirs under a given manpath 1142 * and return the number found 1143 * If -c option is on, only count sman dirs 1144 */ 1145 1146 static int 1147 getdirs(char *path, char ***dirv, short flag) 1148 { 1149 DIR *dp; 1150 struct dirent *d; 1151 int n = 0; 1152 int plen, sgml_flag, man_flag; 1153 int i = 0; 1154 int maxentries = MAXDIRS; 1155 char **dv; 1156 1157 if ((dp = opendir(path)) == 0) { 1158 if (debug) { 1159 if (*localedir != '\0') 1160 (void) printf(gettext("\ 1161 locale is %s, search in %s\n"), localedir, path); 1162 perror(path); 1163 } 1164 return (0); 1165 } 1166 1167 if (flag) { 1168 /* allocate memory for dirv */ 1169 *dirv = (char **)malloc(sizeof (char *) * 1170 maxentries); 1171 if (*dirv == NULL) 1172 malloc_error(); 1173 dv = *dirv; 1174 } 1175 while ((d = readdir(dp))) { 1176 plen = PLEN; 1177 man_flag = sgml_flag = 0; 1178 if (match(d->d_name, SGMLDIR, PLEN+1)) { 1179 plen = PLEN + 1; 1180 sgml_flag = 1; 1181 i++; 1182 } 1183 1184 if (match(subdirs[0], d->d_name, PLEN)) 1185 man_flag = 1; 1186 1187 if (compargs && sgml_flag) { 1188 if (flag) { 1189 *dv = strdup(d->d_name+plen); 1190 if (*dv == NULL) 1191 malloc_error(); 1192 dv++; 1193 n = i; 1194 } 1195 } else if (!compargs && (sgml_flag || man_flag)) { 1196 if (flag) { 1197 *dv = strdup(d->d_name+plen); 1198 if (*dv == NULL) 1199 malloc_error(); 1200 dv++; 1201 } 1202 n++; 1203 } 1204 if (flag) { 1205 if ((dv - *dirv) == maxentries) { 1206 int entries = maxentries; 1207 maxentries += MAXTOKENS; 1208 *dirv = (char **)realloc(*dirv, 1209 sizeof (char *) * maxentries); 1210 if (*dirv == NULL) 1211 malloc_error(); 1212 dv = *dirv + entries; 1213 } 1214 } 1215 } 1216 1217 (void) closedir(dp); 1218 return (n); 1219 } 1220 1221 1222 /* 1223 * Find matching whatis or apropos entries 1224 * whatapro() tries to handle the windex file of the locale specific 1225 * man dirs first, then tries to handle the windex file of the default 1226 * man dir (of C locale like /usr/share/man). 1227 */ 1228 1229 static void 1230 whatapro(struct man_node *manp, char *word, int apropos) 1231 { 1232 char whatpath[MAXPATHLEN+1]; 1233 char *p; 1234 struct man_node *b; 1235 int ndirs = 0; 1236 char *ldir; 1237 1238 1239 /* 1240 * TRANSLATION_NOTE - message for man -d 1241 * %s takes a parameter to -k option. 1242 */ 1243 DPRINTF(gettext("word = %s \n"), word); 1244 1245 /* 1246 * get base part of name 1247 */ 1248 if (!apropos) { 1249 if ((p = strrchr(word, '/')) == NULL) 1250 p = word; 1251 else 1252 p++; 1253 } else { 1254 p = word; 1255 } 1256 1257 for (b = manp; b != NULL; b = b->next) { 1258 1259 if (*localedir != '\0') { 1260 /* addlocale() allocates memory and returns it */ 1261 ldir = addlocale(b->path); 1262 if (defaultmandir) 1263 defaultmandir = 0; 1264 ndirs = getdirs(ldir, NULL, 0); 1265 if (ndirs != 0) { 1266 (void) sprintf(whatpath, "%s/%s", ldir, WHATIS); 1267 /* 1268 * TRANSLATION_NOTE - message for man -d 1269 * ex. mandir path = /usr/share/man/ja 1270 */ 1271 DPRINTF(gettext("\nmandir path = %s\n"), ldir); 1272 lookup_windex(whatpath, p, b->secv); 1273 } 1274 /* release memory allocated by addlocale() */ 1275 free(ldir); 1276 } 1277 1278 defaultmandir = 1; 1279 (void) sprintf(whatpath, "%s/%s", b->path, WHATIS); 1280 /* 1281 * TRANSLATION_NOTE - message for man -d 1282 * ex. mandir path = /usr/share/man 1283 */ 1284 DPRINTF(gettext("\nmandir path = %s\n"), b->path); 1285 1286 lookup_windex(whatpath, p, b->secv); 1287 } 1288 } 1289 1290 1291 static void 1292 lookup_windex(char *whatpath, char *word, char **secv) 1293 { 1294 FILE *fp; 1295 char *matches[MAXPAGES]; 1296 char **pp; 1297 wchar_t wbuf[BUFSIZ]; 1298 wchar_t *word_wchar = NULL; 1299 wchar_t *ws; 1300 size_t word_len, ret; 1301 1302 if ((fp = fopen(whatpath, "r")) == NULL) { 1303 perror(whatpath); 1304 return; 1305 } 1306 1307 if (apropos) { 1308 word_len = strlen(word) + 1; 1309 if ((word_wchar = (wchar_t *)malloc(sizeof (wchar_t) * 1310 word_len)) == NULL) { 1311 malloc_error(); 1312 } 1313 ret = mbstowcs(word_wchar, (const char *)word, word_len); 1314 if (ret == (size_t)-1) { 1315 (void) fprintf(stderr, gettext( 1316 "Invalid character in keyword\n")); 1317 exit(1); 1318 } 1319 while (fgetws(wbuf, BUFSIZ, fp) != NULL) 1320 for (ws = wbuf; *ws; ws++) 1321 if (icmp(word_wchar, ws) == 0) { 1322 (void) printf("%ws", wbuf); 1323 break; 1324 } 1325 } else { 1326 if (bfsearch(fp, matches, word, secv)) 1327 for (pp = matches; *pp; pp++) { 1328 (void) printf("%s", *pp); 1329 /* 1330 * release memory allocated by 1331 * strdup() in bfsearch() 1332 */ 1333 free(*pp); 1334 } 1335 } 1336 (void) fclose(fp); 1337 if (word_wchar) 1338 free(word_wchar); 1339 1340 } 1341 1342 1343 /* 1344 * case-insensitive compare unless upper case is used 1345 * ie) "mount" matches mount, Mount, MOUNT 1346 * "Mount" matches Mount, MOUNT 1347 * "MOUNT" matches MOUNT only 1348 * If matched return 0. Otherwise, return 1. 1349 */ 1350 1351 static int 1352 icmp(wchar_t *ws, wchar_t *wt) 1353 { 1354 for (; (*ws == 0) || 1355 (*ws == (iswupper(*ws) ? *wt: towlower(*wt))); 1356 ws++, wt++) 1357 if (*ws == 0) 1358 return (0); 1359 1360 return (1); 1361 } 1362 1363 1364 /* 1365 * Invoke PAGER with all matching man pages 1366 */ 1367 1368 static void 1369 more(char **pages, int plain) 1370 { 1371 char cmdbuf[BUFSIZ]; 1372 char **vp; 1373 1374 /* 1375 * Dont bother. 1376 */ 1377 if (list || (*pages == 0)) 1378 return; 1379 1380 if (plain && troffit) { 1381 cleanup(pages); 1382 return; 1383 } 1384 (void) sprintf(cmdbuf, "%s", troffit ? troffcat : 1385 plain ? CAT : pager); 1386 1387 /* 1388 * Build arg list 1389 */ 1390 for (vp = pages; vp < endp; vp++) { 1391 (void) strcat(cmdbuf, " "); 1392 (void) strcat(cmdbuf, *vp); 1393 } 1394 (void) sys(cmdbuf); 1395 cleanup(pages); 1396 } 1397 1398 1399 /* 1400 * Get rid of dregs. 1401 */ 1402 1403 static void 1404 cleanup(char **pages) 1405 { 1406 char **vp; 1407 1408 for (vp = pages; vp < endp; vp++) { 1409 if (match(TEMPLATE, *vp, TMPLEN)) 1410 (void) unlink(*vp); 1411 free(*vp); 1412 } 1413 1414 endp = pages; /* reset */ 1415 } 1416 1417 1418 /* 1419 * Clean things up after receiving a signal. 1420 */ 1421 1422 /*ARGSUSED*/ 1423 static void 1424 bye(int sig) 1425 { 1426 cleanup(pages); 1427 exit(1); 1428 /*NOTREACHED*/ 1429 } 1430 1431 1432 /* 1433 * Split a string by specified separator. 1434 * ignore empty components/adjacent separators. 1435 * returns vector to all tokens 1436 */ 1437 1438 static char ** 1439 split(char *s1, char sep) 1440 { 1441 char **tokv, **vp; 1442 char *mp, *tp; 1443 int maxentries = MAXTOKENS; 1444 int entries = 0; 1445 1446 tokv = vp = (char **)malloc(maxentries * sizeof (char *)); 1447 if (tokv == NULL) 1448 malloc_error(); 1449 mp = s1; 1450 for (; mp && *mp; mp = tp) { 1451 tp = strchr(mp, sep); 1452 if (mp == tp) { /* empty component */ 1453 tp++; /* ignore */ 1454 continue; 1455 } 1456 if (tp) { 1457 /* a component found */ 1458 size_t len; 1459 1460 len = tp - mp; 1461 *vp = (char *)malloc(sizeof (char) * len + 1); 1462 if (*vp == NULL) 1463 malloc_error(); 1464 (void) strncpy(*vp, mp, len); 1465 *(*vp + len) = '\0'; 1466 tp++; 1467 vp++; 1468 } else { 1469 /* the last component */ 1470 *vp = strdup(mp); 1471 if (*vp == NULL) 1472 malloc_error(); 1473 vp++; 1474 } 1475 entries++; 1476 if (entries == maxentries) { 1477 maxentries += MAXTOKENS; 1478 tokv = (char **)realloc(tokv, 1479 maxentries * sizeof (char *)); 1480 if (tokv == NULL) 1481 malloc_error(); 1482 vp = tokv + entries; 1483 } 1484 } 1485 *vp = 0; 1486 return (tokv); 1487 } 1488 1489 /* 1490 * Free a vector allocated by split(); 1491 */ 1492 static void 1493 freev(char **v) 1494 { 1495 int i; 1496 if (v != NULL) { 1497 for (i = 0; v[i] != NULL; i++) { 1498 free(v[i]); 1499 } 1500 free(v); 1501 } 1502 } 1503 1504 /* 1505 * Convert paths to full paths if necessary 1506 * 1507 */ 1508 1509 static void 1510 fullpaths(struct man_node **manp_head) 1511 { 1512 char *cwd = NULL; 1513 char *p; 1514 char cwd_gotten = 0; 1515 struct man_node *manp = *manp_head; 1516 struct man_node *b; 1517 struct man_node *prev = NULL; 1518 1519 for (b = manp; b != NULL; b = b->next) { 1520 if (*(b->path) == '/') { 1521 prev = b; 1522 continue; 1523 } 1524 1525 /* try to get cwd if haven't already */ 1526 if (!cwd_gotten) { 1527 cwd = getcwd(NULL, MAXPATHLEN+1); 1528 cwd_gotten = 1; 1529 } 1530 1531 if (cwd) { 1532 /* case: relative manpath with cwd: make absolute */ 1533 if ((p = malloc(strlen(b->path)+strlen(cwd)+2)) == 1534 NULL) { 1535 malloc_error(); 1536 } 1537 (void) sprintf(p, "%s/%s", cwd, b->path); 1538 /* 1539 * resetting b->path 1540 */ 1541 free(b->path); 1542 b->path = p; 1543 } else { 1544 /* case: relative manpath but no cwd: omit path entry */ 1545 if (prev) 1546 prev->next = b->next; 1547 else 1548 *manp_head = b->next; 1549 1550 free_manp(b); 1551 } 1552 } 1553 /* 1554 * release memory allocated by getcwd() 1555 */ 1556 free(cwd); 1557 } 1558 1559 /* 1560 * Free a man_node structure and its contents 1561 */ 1562 1563 static void 1564 free_manp(struct man_node *manp) 1565 { 1566 char **p; 1567 1568 free(manp->path); 1569 p = manp->secv; 1570 while ((p != NULL) && (*p != NULL)) { 1571 free(*p); 1572 p++; 1573 } 1574 free(manp->secv); 1575 free(manp); 1576 } 1577 1578 1579 /* 1580 * Map (in place) to lower case 1581 */ 1582 1583 static void 1584 lower(char *s) 1585 { 1586 if (s == 0) 1587 return; 1588 while (*s) { 1589 if (isupper(*s)) 1590 *s = tolower(*s); 1591 s++; 1592 } 1593 } 1594 1595 1596 /* 1597 * compare for sort() 1598 * sort first by section-spec, then by prefix {sman, man, cat, fmt} 1599 * note: prefix is reverse sorted so that "sman" and "man" always 1600 * comes before {cat, fmt} 1601 */ 1602 1603 static int 1604 cmp(const void *arg1, const void *arg2) 1605 { 1606 int n; 1607 char **p1 = (char **)arg1; 1608 char **p2 = (char **)arg2; 1609 1610 1611 /* by section; sman always before man dirs */ 1612 if ((n = strcmp(*p1 + PLEN + (**p1 == 's' ? 1 : 0), 1613 *p2 + PLEN + (**p2 == 's' ? 1 : 0)))) 1614 return (n); 1615 1616 /* by prefix reversed */ 1617 return (strncmp(*p2, *p1, PLEN)); 1618 } 1619 1620 1621 /* 1622 * Find a man page ... 1623 * Loop through each path specified, 1624 * first try the lookup method (whatis database), 1625 * and if it doesn't exist, do the hard way. 1626 */ 1627 1628 static int 1629 manual(struct man_node *manp, char *name) 1630 { 1631 struct man_node *p; 1632 struct man_node *local; 1633 int ndirs = 0; 1634 char *ldir; 1635 char *ldirs[2]; 1636 char *fullname = name; 1637 char *slash; 1638 1639 if ((slash = strrchr(name, '/')) != NULL) { 1640 name = slash + 1; 1641 } 1642 1643 /* 1644 * for each path in MANPATH 1645 */ 1646 found = 0; 1647 1648 for (p = manp; p != NULL; p = p->next) { 1649 /* 1650 * TRANSLATION_NOTE - message for man -d 1651 * ex. mandir path = /usr/share/man 1652 */ 1653 DPRINTF(gettext("\nmandir path = %s\n"), p->path); 1654 1655 if (*localedir != '\0') { 1656 /* addlocale() allocates memory and returns it */ 1657 ldir = addlocale(p->path); 1658 if (defaultmandir) 1659 defaultmandir = 0; 1660 /* 1661 * TRANSLATION_NOTE - message for man -d 1662 * ex. localedir = ja, ldir = /usr/share/man/ja 1663 */ 1664 if (debug) 1665 (void) printf(gettext( 1666 "localedir = %s, ldir = %s\n"), 1667 localedir, ldir); 1668 ndirs = getdirs(ldir, NULL, 0); 1669 if (ndirs != 0) { 1670 ldirs[0] = ldir; 1671 ldirs[1] = NULL; 1672 local = build_manpath(ldirs, 0); 1673 if (force || 1674 windex(local->secv, ldir, name) < 0) 1675 mandir(local->secv, ldir, name); 1676 free_manp(local); 1677 } 1678 /* release memory allocated by addlocale() */ 1679 free(ldir); 1680 } 1681 1682 defaultmandir = 1; 1683 /* 1684 * locale mandir not valid, man page in locale 1685 * mandir not found, or -a option present 1686 */ 1687 if (ndirs == 0 || !found || all) { 1688 if (force || windex(p->secv, p->path, name) < 0) 1689 mandir(p->secv, p->path, name); 1690 } 1691 1692 if (found && !all) 1693 break; 1694 } 1695 1696 if (found) { 1697 more(pages, nomore); 1698 } else { 1699 if (sargs) { 1700 (void) fprintf(stderr, gettext("No entry for %s in " 1701 "section(s) %s of the manual.\n"), 1702 fullname, mansec); 1703 } else { 1704 (void) fprintf(stderr, gettext( 1705 "No manual entry for %s.\n"), fullname, mansec); 1706 } 1707 1708 if (sman_no_man_no_sroff) 1709 (void) fprintf(stderr, gettext("(An SGML manpage was " 1710 "found for '%s' but it cannot be displayed.)\n"), 1711 fullname, mansec); 1712 } 1713 sman_no_man_no_sroff = 0; 1714 return (!found); 1715 } 1716 1717 1718 /* 1719 * For a specified manual directory, 1720 * read, store, & sort section subdirs, 1721 * for each section specified 1722 * find and search matching subdirs 1723 */ 1724 1725 static void 1726 mandir(char **secv, char *path, char *name) 1727 { 1728 DIR *dp; 1729 char **dirv; 1730 char **dv, **pdv; 1731 int len, dslen, plen = PLEN; 1732 1733 if ((dp = opendir(path)) == 0) { 1734 /* 1735 * TRANSLATION_NOTE - message for man -d or catman -p 1736 * opendir(%s) returned 0 1737 */ 1738 if (debug) 1739 (void) fprintf(stdout, gettext( 1740 " opendir on %s failed\n"), path); 1741 return; 1742 } 1743 1744 /* 1745 * TRANSLATION_NOTE - message for man -d or catman -p 1746 * ex. mandir path = /usr/share/man/ja 1747 */ 1748 if (debug) 1749 (void) printf(gettext("mandir path = %s\n"), path); 1750 1751 /* 1752 * sordir() allocates memory for dirv and dirv[]. 1753 */ 1754 sortdir(dp, &dirv); 1755 /* 1756 * Search in the order specified by MANSECTS 1757 */ 1758 for (; *secv; secv++) { 1759 /* 1760 * TRANSLATION_NOTE - message for man -d or catman -p 1761 * ex. section = 3c 1762 */ 1763 DPRINTF(gettext(" section = %s\n"), *secv); 1764 len = strlen(*secv); 1765 for (dv = dirv; *dv; dv++) { 1766 plen = PLEN; 1767 if (*dv[0] == 's') 1768 plen++; 1769 dslen = strlen(*dv+plen); 1770 if (dslen > len) 1771 len = dslen; 1772 if (**secv == '\\') { 1773 if (!eq(*secv + 1, *dv+plen)) 1774 continue; 1775 } else if (strncasecmp(*secv, *dv+plen, len) != 0) { 1776 /* check to see if directory name changed */ 1777 if (!all && 1778 (newsection = map_section(*secv, path)) 1779 == NULL) { 1780 continue; 1781 } 1782 if (newsection == NULL) 1783 newsection = ""; 1784 if (!match(newsection, *dv+plen, len)) { 1785 continue; 1786 } 1787 } 1788 1789 if (searchdir(path, *dv, name) == 0) 1790 continue; 1791 1792 if (!all) { 1793 /* release memory allocated by sortdir() */ 1794 pdv = dirv; 1795 while (*pdv) { 1796 free(*pdv); 1797 pdv++; 1798 } 1799 (void) closedir(dp); 1800 /* release memory allocated by sortdir() */ 1801 free(dirv); 1802 return; 1803 } 1804 /* 1805 * if we found a match in the man dir skip 1806 * the corresponding cat dir if it exists 1807 */ 1808 if (all && **dv == 'm' && *(dv+1) && 1809 eq(*(dv+1)+plen, *dv+plen)) 1810 dv++; 1811 } 1812 } 1813 /* release memory allocated by sortdir() */ 1814 pdv = dirv; 1815 while (*pdv) { 1816 free(*pdv); 1817 pdv++; 1818 } 1819 free(dirv); 1820 (void) closedir(dp); 1821 } 1822 1823 /* 1824 * Sort directories. 1825 */ 1826 1827 static void 1828 sortdir(DIR *dp, char ***dirv) 1829 { 1830 struct dirent *d; 1831 char **dv; 1832 int maxentries = MAXDIRS; 1833 int entries = 0; 1834 1835 *dirv = (char **)malloc(sizeof (char *) * maxentries); 1836 dv = *dirv; 1837 while ((d = readdir(dp))) { /* store dirs */ 1838 if (eq(d->d_name, ".") || eq(d->d_name, "..")) /* ignore */ 1839 continue; 1840 1841 /* check if it matches sman, man, cat format */ 1842 if (match(d->d_name, SGMLDIR, PLEN+1) || 1843 match(d->d_name, subdirs[0], PLEN) || 1844 match(d->d_name, subdirs[1], PLEN)) { 1845 *dv = malloc(strlen(d->d_name) + 1); 1846 if (*dv == NULL) 1847 malloc_error(); 1848 (void) strcpy(*dv, d->d_name); 1849 dv++; 1850 entries++; 1851 if (entries == maxentries) { 1852 maxentries += MAXDIRS; 1853 *dirv = (char **)realloc(*dirv, 1854 sizeof (char *) * maxentries); 1855 if (*dirv == NULL) 1856 malloc_error(); 1857 dv = *dirv + entries; 1858 } 1859 } 1860 } 1861 *dv = 0; 1862 1863 qsort((void *)*dirv, dv - *dirv, sizeof (char *), cmp); 1864 1865 } 1866 1867 1868 /* 1869 * Search a section subdirectory for a 1870 * given man page, return 1 for success 1871 */ 1872 1873 static int 1874 searchdir(char *path, char *dir, char *name) 1875 { 1876 DIR *sdp; 1877 struct dirent *sd; 1878 char sectpath[MAXPATHLEN+1]; 1879 char file[MAXNAMLEN+1]; 1880 char dname[MAXPATHLEN+1]; 1881 char *last; 1882 int nlen; 1883 1884 /* 1885 * TRANSLATION_NOTE - message for man -d or catman -p 1886 * ex. scanning = man3c 1887 */ 1888 DPRINTF(gettext(" scanning = %s\n"), dir); 1889 (void) sprintf(sectpath, "%s/%s", path, dir); 1890 (void) snprintf(file, MAXPATHLEN, "%s.", name); 1891 1892 if ((sdp = opendir(sectpath)) == 0) { 1893 if (errno != ENOTDIR) /* ignore matching cruft */ 1894 perror(sectpath); 1895 return (0); 1896 } 1897 while ((sd = readdir(sdp))) { 1898 last = strrchr(sd->d_name, '.'); 1899 nlen = last - sd->d_name; 1900 (void) sprintf(dname, "%.*s.", nlen, sd->d_name); 1901 if (eq(dname, file) || eq(sd->d_name, name)) { 1902 if (no_sroff && *dir == 's') { 1903 sman_no_man_no_sroff = 1; 1904 return (0); 1905 } 1906 (void) format(path, dir, name, sd->d_name); 1907 (void) closedir(sdp); 1908 return (1); 1909 } 1910 } 1911 (void) closedir(sdp); 1912 return (0); 1913 } 1914 1915 /* 1916 * Check the hash table of old directory names to see if there is a 1917 * new directory name. 1918 * Returns new directory name if a match; after checking to be sure 1919 * directory exists. 1920 * Otherwise returns NULL 1921 */ 1922 1923 static char * 1924 map_section(char *section, char *path) 1925 { 1926 int i; 1927 int len; 1928 char fullpath[MAXPATHLEN]; 1929 1930 if (list) /* -l option fall through */ 1931 return (NULL); 1932 1933 for (i = 0; i <= ((sizeof (map)/sizeof (map[0]) - 1)); i++) { 1934 if (strlen(section) > strlen(map[i].new_name)) { 1935 len = strlen(section); 1936 } else { 1937 len = strlen(map[i].new_name); 1938 } 1939 if (match(section, map[i].old_name, len)) { 1940 (void) sprintf(fullpath, 1941 "%s/sman%s", path, map[i].new_name); 1942 if (!access(fullpath, R_OK | X_OK)) { 1943 return (map[i].new_name); 1944 } else { 1945 return (NULL); 1946 } 1947 } 1948 } 1949 1950 return (NULL); 1951 } 1952 1953 1954 /* 1955 * Use windex database for quick lookup of man pages 1956 * instead of mandir() (brute force search) 1957 */ 1958 1959 static int 1960 windex(char **secv, char *path, char *name) 1961 { 1962 FILE *fp; 1963 struct stat sbuf; 1964 struct suffix *sp; 1965 struct suffix psecs[MAXPAGES+1]; 1966 char whatfile[MAXPATHLEN+1]; 1967 char page[MAXPATHLEN+1]; 1968 char *matches[MAXPAGES]; 1969 char *file, *dir; 1970 char **sv, **vp; 1971 int len, dslen, exist, i; 1972 int found_in_windex = 0; 1973 char *tmp[] = {0, 0, 0, 0}; 1974 1975 1976 (void) sprintf(whatfile, "%s/%s", path, WHATIS); 1977 if ((fp = fopen(whatfile, "r")) == NULL) { 1978 if (errno == ENOENT) 1979 return (-1); 1980 return (0); 1981 } 1982 1983 /* 1984 * TRANSLATION_NOTE - message for man -d or catman -p 1985 * ex. search in = /usr/share/man/ja/windex file 1986 */ 1987 if (debug) 1988 (void) fprintf(stdout, gettext( 1989 " search in = %s file\n"), whatfile); 1990 1991 if (bfsearch(fp, matches, name, NULL) == 0) { 1992 (void) fclose(fp); 1993 return (-1); /* force search in mandir */ 1994 } 1995 1996 (void) fclose(fp); 1997 1998 /* 1999 * Save and split sections 2000 * section() allocates memory for sp->ds 2001 */ 2002 for (sp = psecs, vp = matches; *vp; vp++, sp++) { 2003 if ((sp - psecs) < MAXPAGES) { 2004 section(sp, *vp); 2005 } else { 2006 if (debug) 2007 (void) fprintf(stderr, gettext( 2008 "too many sections in %s windex entry\n"), 2009 name); 2010 2011 /* Setting sp->ds to NULL signifies end-of-data. */ 2012 sp->ds = 0; 2013 goto finish; 2014 } 2015 } 2016 2017 sp->ds = 0; 2018 2019 /* 2020 * Search in the order specified 2021 * by MANSECTS 2022 */ 2023 for (; *secv; secv++) { 2024 len = strlen(*secv); 2025 2026 /* 2027 * TRANSLATION_NOTE - message for man -d or catman -p 2028 * ex. search an entry to match printf.3c 2029 */ 2030 if (debug) 2031 (void) fprintf(stdout, gettext( 2032 " search an entry to match %s.%s\n"), name, *secv); 2033 /* 2034 * For every whatis entry that 2035 * was matched 2036 */ 2037 for (sp = psecs; sp->ds; sp++) { 2038 dslen = strlen(sp->ds); 2039 if (dslen > len) 2040 len = dslen; 2041 if (**secv == '\\') { 2042 if (!eq(*secv + 1, sp->ds)) 2043 continue; 2044 } else if (!match(*secv, sp->ds, len)) { 2045 /* check to see if directory name changed */ 2046 if (!all && 2047 (newsection = map_section(*secv, path)) 2048 == NULL) { 2049 continue; 2050 } 2051 if (newsection == NULL) 2052 newsection = ""; 2053 if (!match(newsection, sp->ds, len)) { 2054 continue; 2055 } 2056 } 2057 /* 2058 * here to form "sman", "man", "cat"|"fmt" in 2059 * order 2060 */ 2061 if (!no_sroff) { 2062 tmp[0] = SGMLDIR; 2063 for (i = 1; i < 4; i++) 2064 tmp[i] = subdirs[i-1]; 2065 } else { 2066 for (i = 0; i < 3; i++) 2067 tmp[i] = subdirs[i]; 2068 } 2069 2070 for (sv = tmp; *sv; sv++) { 2071 (void) sprintf(page, 2072 "%s/%s%s/%s%s%s", path, *sv, 2073 sp->ds, name, *sp->fs ? "." : "", 2074 sp->fs); 2075 exist = (stat(page, &sbuf) == 0); 2076 if (exist) 2077 break; 2078 } 2079 if (!exist) { 2080 (void) fprintf(stderr, gettext( 2081 "%s entry incorrect: %s(%s) not found.\n"), 2082 WHATIS, name, sp->ds); 2083 continue; 2084 } 2085 2086 file = strrchr(page, '/'), *file = 0; 2087 dir = strrchr(page, '/'); 2088 2089 /* 2090 * By now we have a match 2091 */ 2092 found_in_windex = 1; 2093 (void) format(path, ++dir, name, ++file); 2094 2095 if (!all) 2096 goto finish; 2097 } 2098 } 2099 finish: 2100 /* 2101 * release memory allocated by section() 2102 */ 2103 sp = psecs; 2104 while (sp->ds) { 2105 free(sp->ds); 2106 sp->ds = NULL; 2107 sp++; 2108 } 2109 2110 /* 2111 * If we didn't find a match, return failure as if we didn't find 2112 * the windex at all. Why? Well, if you create a windex, then upgrade 2113 * to a later release that contains new man pages, and forget to 2114 * recreate the windex (since we don't do that automatically), you 2115 * won't see any new man pages since they aren't in the windex. 2116 * Pretending we didn't see a windex at all if there are no matches 2117 * forces a search of the underlying directory. After all, the 2118 * goal of the windex is to enable searches (man -k) and speed things 2119 * up, not to _prevent_ you from seeing new man pages, so this seems 2120 * ok. The only problem is when there are multiple entries (different 2121 * sections), and some are in and some are out. Say you do 'man ls', 2122 * and ls(1) isn't in the windex, but ls(1B) is. In that case, we 2123 * will find a match in ls(1B), and you'll see that man page. 2124 * That doesn't seem bad since if you specify the section the search 2125 * will be restricted too. So in the example above, if you do 2126 * 'man -s 1 ls' you'll get ls(1). 2127 */ 2128 if (found_in_windex) 2129 return (0); 2130 else 2131 return (-1); 2132 } 2133 2134 2135 /* 2136 * Return pointers to the section-spec 2137 * and file-suffix of a whatis entry 2138 */ 2139 2140 static void 2141 section(struct suffix *sp, char *s) 2142 { 2143 char *lp, *p; 2144 2145 lp = strchr(s, '('); 2146 p = strchr(s, ')'); 2147 2148 if (++lp == 0 || p == 0 || lp == p) { 2149 (void) fprintf(stderr, 2150 gettext("mangled windex entry:\n\t%s\n"), s); 2151 return; 2152 } 2153 *p = 0; 2154 2155 /* 2156 * copy the string pointed to by lp 2157 */ 2158 lp = strdup(lp); 2159 if (lp == NULL) 2160 malloc_error(); 2161 /* 2162 * release memory in s 2163 * s has been allocated memory in bfsearch() 2164 */ 2165 free(s); 2166 2167 lower(lp); 2168 2169 /* 2170 * split section-specifier if file-name 2171 * suffix differs from section-suffix 2172 */ 2173 sp->ds = lp; 2174 if ((p = strchr(lp, '/'))) { 2175 *p++ = 0; 2176 sp->fs = p; 2177 } else 2178 sp->fs = lp; 2179 } 2180 2181 2182 /* 2183 * Binary file search to find matching man 2184 * pages in whatis database. 2185 */ 2186 2187 static int 2188 bfsearch(FILE *fp, char **matchv, char *key, char **secv) 2189 { 2190 char entry[BUFSIZ]; 2191 char **vp; 2192 long top, bot, mid; 2193 int c; 2194 2195 vp = matchv; 2196 bot = 0; 2197 (void) fseek(fp, 0L, 2); 2198 top = ftell(fp); 2199 for (;;) { 2200 mid = (top+bot)/2; 2201 (void) fseek(fp, mid, 0); 2202 do { 2203 c = getc(fp); 2204 mid++; 2205 } while (c != EOF && c != '\n'); 2206 if (fgets(entry, sizeof (entry), fp) == NULL) 2207 break; 2208 switch (compare(key, entry, secv)) { 2209 case -2: 2210 case -1: 2211 case 0: 2212 if (top <= mid) 2213 break; 2214 top = mid; 2215 continue; 2216 case 1: 2217 case 2: 2218 bot = mid; 2219 continue; 2220 } 2221 break; 2222 } 2223 (void) fseek(fp, bot, 0); 2224 while (ftell(fp) < top) { 2225 if (fgets(entry, sizeof (entry), fp) == NULL) { 2226 *matchv = 0; 2227 return (matchv - vp); 2228 } 2229 switch (compare(key, entry, secv)) { 2230 case -2: 2231 *matchv = 0; 2232 return (matchv - vp); 2233 case -1: 2234 case 0: 2235 *matchv = strdup(entry); 2236 if (*matchv == NULL) 2237 malloc_error(); 2238 else 2239 matchv++; 2240 break; 2241 case 1: 2242 case 2: 2243 continue; 2244 } 2245 break; 2246 } 2247 while (fgets(entry, sizeof (entry), fp)) { 2248 switch (compare(key, entry, secv)) { 2249 case -1: 2250 case 0: 2251 *matchv = strdup(entry); 2252 if (*matchv == NULL) 2253 malloc_error(); 2254 else 2255 matchv++; 2256 continue; 2257 } 2258 break; 2259 } 2260 *matchv = 0; 2261 return (matchv - vp); 2262 } 2263 2264 static int 2265 compare(char *key, char *entry, char **secv) 2266 { 2267 char *entbuf; 2268 char *s; 2269 int comp, mlen; 2270 int mbcurmax = MB_CUR_MAX; 2271 char *secp = NULL; 2272 int rv; 2273 int eblen; 2274 2275 entbuf = strdup(entry); 2276 if (entbuf == NULL) { 2277 malloc_error(); 2278 } 2279 eblen = strlen(entbuf); 2280 2281 s = entbuf; 2282 while (*s) { 2283 if (*s == '\t' || *s == ' ') { 2284 *s = '\0'; 2285 break; 2286 } 2287 mlen = mblen(s, mbcurmax); 2288 if (mlen == -1) { 2289 (void) fprintf(stderr, gettext( 2290 "Invalid character in windex file.\n")); 2291 exit(1); 2292 } 2293 s += mlen; 2294 } 2295 /* 2296 * Find the section within parantheses 2297 */ 2298 if (secv != NULL && (s - entbuf) < eblen) { 2299 if ((secp = strchr(s + 1, ')')) != NULL) { 2300 *secp = '\0'; 2301 if ((secp = strchr(s + 1, '(')) != NULL) { 2302 secp++; 2303 } 2304 } 2305 } 2306 2307 comp = strcmp(key, entbuf); 2308 if (comp == 0) { 2309 if (secp == NULL) { 2310 rv = 0; 2311 } else { 2312 while (*secv != NULL) { 2313 if ((strcmp(*secv, secp)) == 0) { 2314 rv = 0; 2315 break; 2316 } 2317 secv++; 2318 } 2319 } 2320 } else if (comp < 0) { 2321 rv = -2; 2322 } else { 2323 rv = 2; 2324 } 2325 free(entbuf); 2326 return (rv); 2327 } 2328 2329 2330 /* 2331 * Format a man page and follow .so references 2332 * if necessary. 2333 */ 2334 2335 static int 2336 format(char *path, char *dir, char *name, char *pg) 2337 { 2338 char manpname[MAXPATHLEN+1], catpname[MAXPATHLEN+1]; 2339 char manpname_sgml[MAXPATHLEN+1], smantmpname[MAXPATHLEN+1]; 2340 char soed[MAXPATHLEN+1], soref[MAXPATHLEN+1]; 2341 char manbuf[BUFSIZ], cmdbuf[BUFSIZ], tmpbuf[BUFSIZ]; 2342 char tmpdir[MAXPATHLEN+1]; 2343 int socount, updatedcat, regencat; 2344 struct stat mansb, catsb, smansb; 2345 char *tmpname; 2346 int catonly = 0; 2347 struct stat statb; 2348 int plen = PLEN; 2349 FILE *md; 2350 int tempfd; 2351 ssize_t count; 2352 int temp, sgml_flag = 0, check_flag = 0; 2353 char prntbuf[BUFSIZ + 1]; 2354 char *ptr; 2355 char *new_m; 2356 char *tmpsubdir; 2357 2358 found++; 2359 2360 if (*dir != 'm' && *dir != 's') 2361 catonly++; 2362 2363 2364 if (*dir == 's') { 2365 tmpsubdir = SGMLDIR; 2366 ++plen; 2367 (void) sprintf(manpname_sgml, "%s/man%s/%s", 2368 path, dir+plen, pg); 2369 } else 2370 tmpsubdir = MANDIRNAME; 2371 2372 if (list) { 2373 (void) printf(gettext("%s (%s)\t-M %s\n"), 2374 name, dir+plen, path); 2375 return (-1); 2376 } 2377 2378 (void) sprintf(manpname, "%s/%s%s/%s", path, tmpsubdir, dir+plen, pg); 2379 (void) sprintf(catpname, "%s/%s%s/%s", path, subdirs[1], dir+plen, pg); 2380 2381 (void) sprintf(smantmpname, "%s/%s%s/%s", path, SGMLDIR, dir+plen, pg); 2382 2383 /* 2384 * TRANSLATION_NOTE - message for man -d or catman -p 2385 * ex. unformatted = /usr/share/man/ja/man3s/printf.3s 2386 */ 2387 DPRINTF(gettext( 2388 " unformatted = %s\n"), catonly ? "" : manpname); 2389 /* 2390 * TRANSLATION_NOTE - message for man -d or catman -p 2391 * ex. formatted = /usr/share/man/ja/cat3s/printf.3s 2392 */ 2393 DPRINTF(gettext( 2394 " formatted = %s\n"), catpname); 2395 2396 /* 2397 * Take care of indirect references to other man pages; 2398 * i.e., resolve files containing only ".so manx/file.x". 2399 * We follow .so chains, replacing title with the .so'ed 2400 * file at each stage, and keeping track of how many times 2401 * we've done so, so that we can avoid looping. 2402 */ 2403 *soed = 0; 2404 socount = 0; 2405 for (;;) { 2406 FILE *md; 2407 char *cp; 2408 char *s; 2409 char *new_s; 2410 2411 if (catonly) 2412 break; 2413 /* 2414 * Grab manpname's first line, stashing it in manbuf. 2415 */ 2416 2417 2418 if ((md = fopen(manpname, "r")) == NULL) { 2419 if (*soed && errno == ENOENT) { 2420 (void) fprintf(stderr, 2421 gettext("Can't find referent of " 2422 ".so in %s\n"), soed); 2423 (void) fflush(stderr); 2424 return (-1); 2425 } 2426 perror(manpname); 2427 return (-1); 2428 } 2429 2430 /* 2431 * If this is a directory, just ignore it. 2432 */ 2433 if (fstat(fileno(md), &statb) == NULL) { 2434 if (S_ISDIR(statb.st_mode)) { 2435 if (debug) { 2436 (void) fprintf(stderr, 2437 "\tignoring directory %s\n", 2438 manpname); 2439 (void) fflush(stderr); 2440 } 2441 (void) fclose(md); 2442 return (-1); 2443 } 2444 } 2445 2446 if (fgets(manbuf, BUFSIZ-1, md) == NULL) { 2447 (void) fclose(md); 2448 (void) fprintf(stderr, gettext("%s: null file\n"), 2449 manpname); 2450 (void) fflush(stderr); 2451 return (-1); 2452 } 2453 (void) fclose(md); 2454 2455 if (strncmp(manbuf, DOT_SO, sizeof (DOT_SO) - 1)) 2456 break; 2457 so_again: if (++socount > SOLIMIT) { 2458 (void) fprintf(stderr, gettext(".so chain too long\n")); 2459 (void) fflush(stderr); 2460 return (-1); 2461 } 2462 s = manbuf + sizeof (DOT_SO) - 1; 2463 if ((check_flag == 1) && ((new_s = strrchr(s, '/')) != NULL)) { 2464 new_s++; 2465 (void) sprintf(s, "%s%s/%s", 2466 tmpsubdir, dir+plen, new_s); 2467 } 2468 2469 cp = strrchr(s, '\n'); 2470 if (cp) 2471 *cp = '\0'; 2472 /* 2473 * Compensate for sloppy typists by stripping 2474 * trailing white space. 2475 */ 2476 cp = s + strlen(s); 2477 while (--cp >= s && (*cp == ' ' || *cp == '\t')) 2478 *cp = '\0'; 2479 2480 /* 2481 * Go off and find the next link in the chain. 2482 */ 2483 (void) strcpy(soed, manpname); 2484 (void) strcpy(soref, s); 2485 (void) sprintf(manpname, "%s/%s", path, s); 2486 /* 2487 * TRANSLATION_NOTE - message for man -d or catman -p 2488 * ex. .so ref = man3c/string.3c 2489 */ 2490 DPRINTF(gettext(".so ref = %s\n"), s); 2491 } 2492 2493 /* 2494 * Make symlinks if so'ed and cattin' 2495 */ 2496 if (socount && catmando) { 2497 (void) sprintf(cmdbuf, "cd %s; rm -f %s; ln -s ../%s%s %s", 2498 path, catpname, subdirs[1], soref+plen, catpname); 2499 (void) sys(cmdbuf); 2500 return (1); 2501 } 2502 2503 /* 2504 * Obtain the cat page that corresponds to the man page. 2505 * If it already exists, is up to date, and if we haven't 2506 * been told not to use it, use it as it stands. 2507 */ 2508 regencat = updatedcat = 0; 2509 if (compargs || (!catonly && stat(manpname, &mansb) >= 0 && 2510 (stat(catpname, &catsb) < 0 || catsb.st_mtime < mansb.st_mtime)) || 2511 (access(catpname, R_OK) != 0)) { 2512 /* 2513 * Construct a shell command line for formatting manpname. 2514 * The resulting file goes initially into /tmp. If possible, 2515 * it will later be moved to catpname. 2516 */ 2517 2518 int pipestage = 0; 2519 int needcol = 0; 2520 char *cbp = cmdbuf; 2521 2522 regencat = updatedcat = 1; 2523 2524 if (!catmando && !debug && !check_flag) { 2525 (void) fprintf(stderr, gettext( 2526 "Reformatting page. Please Wait...")); 2527 if (sargs && (newsection != NULL) && 2528 (*newsection != '\0')) { 2529 (void) fprintf(stderr, gettext( 2530 "\nThe directory name has been changed " 2531 "to %s\n"), newsection); 2532 } 2533 (void) fflush(stderr); 2534 } 2535 2536 /* 2537 * in catman command, if the file exists in sman dir already, 2538 * don't need to convert the file in man dir to cat dir 2539 */ 2540 2541 if (!no_sroff && catmando && 2542 match(tmpsubdir, MANDIRNAME, PLEN) && 2543 stat(smantmpname, &smansb) >= 0) 2544 return (1); 2545 2546 /* 2547 * cd to path so that relative .so commands will work 2548 * correctly 2549 */ 2550 (void) sprintf(cbp, "cd %s; ", path); 2551 cbp += strlen(cbp); 2552 2553 2554 /* 2555 * check to see whether it is a sgml file 2556 * assume sgml symbol(>!DOCTYPE) can be found in the first 2557 * BUFSIZ bytes 2558 */ 2559 2560 if ((temp = open(manpname, 0)) == -1) { 2561 perror(manpname); 2562 return (-1); 2563 } 2564 2565 if ((count = read(temp, prntbuf, BUFSIZ)) <= 0) { 2566 perror(manpname); 2567 return (-1); 2568 } 2569 2570 prntbuf[count] = '\0'; /* null terminate */ 2571 ptr = prntbuf; 2572 if (sgmlcheck((const char *)ptr) == 1) { 2573 sgml_flag = 1; 2574 if (defaultmandir && *localedir) { 2575 (void) sprintf(cbp, "LC_MESSAGES=C %s %s ", 2576 SROFF_CMD, manpname); 2577 } else { 2578 (void) sprintf(cbp, "%s %s ", 2579 SROFF_CMD, manpname); 2580 } 2581 cbp += strlen(cbp); 2582 } else if (*dir == 's') { 2583 (void) close(temp); 2584 return (-1); 2585 } 2586 (void) close(temp); 2587 2588 /* 2589 * Check for special formatting requirements by examining 2590 * manpname's first line preprocessor specifications. 2591 */ 2592 2593 if (strncmp(manbuf, PREPROC_SPEC, 2594 sizeof (PREPROC_SPEC) - 1) == 0) { 2595 char *ptp; 2596 2597 ptp = manbuf + sizeof (PREPROC_SPEC) - 1; 2598 while (*ptp && *ptp != '\n') { 2599 const struct preprocessor *pp; 2600 2601 /* 2602 * Check for a preprocessor we know about. 2603 */ 2604 for (pp = preprocessors; pp->p_tag; pp++) { 2605 if (pp->p_tag == *ptp) 2606 break; 2607 } 2608 if (pp->p_tag == 0) { 2609 (void) fprintf(stderr, 2610 gettext("unknown preprocessor " 2611 "specifier %c\n"), *ptp); 2612 (void) fflush(stderr); 2613 return (-1); 2614 } 2615 2616 /* 2617 * Add it to the pipeline. 2618 */ 2619 (void) sprintf(cbp, "%s %s |", 2620 troffit ? pp->p_troff : pp->p_nroff, 2621 pipestage++ == 0 ? manpname : 2622 pp->p_stdin_char); 2623 cbp += strlen(cbp); 2624 2625 /* 2626 * Special treatment: if tbl is among the 2627 * preprocessors and we'll process with 2628 * nroff, we have to pass things through 2629 * col at the end of the pipeline. 2630 */ 2631 if (pp->p_tag == 't' && !troffit) 2632 needcol++; 2633 2634 ptp++; 2635 } 2636 } 2637 2638 /* 2639 * if catman, use the cat page name 2640 * otherwise, dup template and create another 2641 * (needed for multiple pages) 2642 */ 2643 if (catmando) 2644 tmpname = catpname; 2645 else { 2646 tmpname = strdup(TEMPLATE); 2647 if (tmpname == NULL) 2648 malloc_error(); 2649 (void) close(mkstemp(tmpname)); 2650 } 2651 2652 if (! Tflag) { 2653 if (*localedir != '\0') { 2654 (void) sprintf(macros, "%s/%s", path, MACROF); 2655 /* 2656 * TRANSLATION_NOTE - message for man -d or catman -p 2657 * ex. locale macros = /usr/share/man/ja/tmac.an 2658 */ 2659 if (debug) 2660 (void) printf(gettext( 2661 "\nlocale macros = %s "), 2662 macros); 2663 if (stat(macros, &statb) < 0) 2664 (void) strcpy(macros, TMAC_AN); 2665 /* 2666 * TRANSLATION_NOTE - message for man -d or catman -p 2667 * ex. macros = /usr/share/man/ja/tman.an 2668 */ 2669 if (debug) 2670 (void) printf(gettext( 2671 "\nmacros = %s\n"), 2672 macros); 2673 } 2674 } 2675 2676 tmpdir[0] = '\0'; 2677 if (sgml_flag == 1) { 2678 if (check_flag == 0) { 2679 strcpy(tmpdir, "/tmp/sman_XXXXXX"); 2680 if ((tempfd = mkstemp(tmpdir)) == -1) { 2681 (void) fprintf(stderr, gettext( 2682 "%s: null file\n"), tmpdir); 2683 (void) fflush(stderr); 2684 return (-1); 2685 } 2686 2687 if (debug) 2688 close(tempfd); 2689 2690 (void) sprintf(tmpbuf, "%s > %s", 2691 cmdbuf, tmpdir); 2692 if (sys(tmpbuf)) { 2693 /* 2694 * TRANSLATION_NOTE - message for man -d or catman -p 2695 * Error message if sys(%s) failed 2696 */ 2697 (void) fprintf(stderr, gettext( 2698 "sys(%s) fail!\n"), tmpbuf); 2699 (void) fprintf(stderr, 2700 gettext(" aborted (sorry)\n")); 2701 (void) fflush(stderr); 2702 /* release memory for tmpname */ 2703 if (!catmando) { 2704 (void) unlink(tmpdir); 2705 (void) unlink(tmpname); 2706 free(tmpname); 2707 } 2708 return (-1); 2709 } else if (debug == 0) { 2710 if ((md = fdopen(tempfd, "r")) 2711 == NULL) { 2712 (void) fprintf(stderr, gettext( 2713 "%s: null file\n"), tmpdir); 2714 (void) fflush(stderr); 2715 close(tempfd); 2716 /* release memory for tmpname */ 2717 if (!catmando) 2718 free(tmpname); 2719 return (-1); 2720 } 2721 2722 /* if the file is empty, */ 2723 /* it's a fragment, do nothing */ 2724 if (fgets(manbuf, BUFSIZ-1, md) 2725 == NULL) { 2726 (void) fclose(md); 2727 /* release memory for tmpname */ 2728 if (!catmando) 2729 free(tmpname); 2730 return (1); 2731 } 2732 (void) fclose(md); 2733 2734 if (strncmp(manbuf, DOT_SO, 2735 sizeof (DOT_SO) - 1) == 0) { 2736 if (!compargs) { 2737 check_flag = 1; 2738 (void) unlink(tmpdir); 2739 (void) unlink(tmpname); 2740 /* release memory for tmpname */ 2741 if (!catmando) 2742 free(tmpname); 2743 goto so_again; 2744 } else { 2745 (void) unlink(tmpdir); 2746 strcpy(tmpdir, 2747 "/tmp/sman_XXXXXX"); 2748 tempfd = mkstemp(tmpdir); 2749 if ((tempfd == -1) || 2750 (md = fdopen(tempfd, "w")) 2751 == NULL) { 2752 (void) fprintf(stderr, 2753 gettext( 2754 "%s: null file\n"), 2755 tmpdir); 2756 (void) fflush(stderr); 2757 if (tempfd != -1) 2758 close(tempfd); 2759 /* release memory for tmpname */ 2760 if (!catmando) 2761 free(tmpname); 2762 return (-1); 2763 } 2764 if ((new_m = strrchr(manbuf, '/')) != NULL) { 2765 (void) fprintf(md, ".so man%s%s\n", dir+plen, new_m); 2766 } else { 2767 /* 2768 * TRANSLATION_NOTE - message for catman -c 2769 * Error message if unable to get file name 2770 */ 2771 (void) fprintf(stderr, 2772 gettext("file not found\n")); 2773 (void) fflush(stderr); 2774 return (-1); 2775 } 2776 (void) fclose(md); 2777 } 2778 } 2779 } 2780 if (catmando && compargs) 2781 (void) sprintf(cmdbuf, "cat %s > %s", 2782 tmpdir, manpname_sgml); 2783 else 2784 (void) sprintf(cmdbuf, " cat %s | tbl | eqn | %s %s - %s > %s", 2785 tmpdir, troffit ? troffcmd : "nroff -u0 -Tlp", 2786 macros, troffit ? "" : " | col -x", tmpname); 2787 } else 2788 if (catmando && compargs) 2789 (void) sprintf(cbp, " > %s", 2790 manpname_sgml); 2791 else 2792 (void) sprintf(cbp, " | tbl | eqn | %s %s - %s > %s", 2793 troffit ? troffcmd : "nroff -u0 -Tlp", 2794 macros, troffit ? "" : " | col -x", tmpname); 2795 2796 } else 2797 (void) sprintf(cbp, "%s %s %s%s > %s", 2798 troffit ? troffcmd : "nroff -u0 -Tlp", 2799 macros, pipestage == 0 ? manpname : "-", 2800 troffit ? "" : " | col -x", tmpname); 2801 2802 /* Reformat the page. */ 2803 if (sys(cmdbuf)) { 2804 /* 2805 * TRANSLATION_NOTE - message for man -d or catman -p 2806 * Error message if sys(%s) failed 2807 */ 2808 (void) fprintf(stderr, gettext( 2809 "sys(%s) fail!\n"), cmdbuf); 2810 (void) fprintf(stderr, gettext(" aborted (sorry)\n")); 2811 (void) fflush(stderr); 2812 (void) unlink(tmpname); 2813 /* release memory for tmpname */ 2814 if (!catmando) 2815 free(tmpname); 2816 return (-1); 2817 } 2818 2819 if (tmpdir[0] != '\0') 2820 (void) unlink(tmpdir); 2821 2822 if (catmando) 2823 return (1); 2824 2825 /* 2826 * Attempt to move the cat page to its proper home. 2827 */ 2828 (void) sprintf(cmdbuf, 2829 "trap '' 1 15; /usr/bin/mv -f %s %s 2> /dev/null", 2830 tmpname, 2831 catpname); 2832 if (sys(cmdbuf)) 2833 updatedcat = 0; 2834 else if (debug == 0) 2835 (void) chmod(catpname, 0644); 2836 2837 if (debug) { 2838 /* release memory for tmpname */ 2839 if (!catmando) 2840 free(tmpname); 2841 (void) unlink(tmpname); 2842 return (1); 2843 } 2844 2845 (void) fprintf(stderr, gettext(" done\n")); 2846 (void) fflush(stderr); 2847 } 2848 2849 /* 2850 * Save file name (dup if necessary) 2851 * to view later 2852 * fix for 1123802 - don't save names if we are invoked as catman 2853 */ 2854 if (!catmando) { 2855 char **tmpp; 2856 int dup; 2857 char *newpage; 2858 2859 if (regencat && !updatedcat) 2860 newpage = tmpname; 2861 else { 2862 newpage = strdup(catpname); 2863 if (newpage == NULL) 2864 malloc_error(); 2865 } 2866 /* make sure we don't add a dup */ 2867 dup = 0; 2868 for (tmpp = pages; tmpp < endp; tmpp++) { 2869 if (strcmp(*tmpp, newpage) == 0) { 2870 dup = 1; 2871 break; 2872 } 2873 } 2874 if (!dup) 2875 *endp++ = newpage; 2876 if (endp >= &pages[MAXPAGES]) { 2877 fprintf(stderr, 2878 gettext("Internal pages array overflow!\n")); 2879 exit(1); 2880 } 2881 } 2882 2883 return (regencat); 2884 } 2885 2886 /* 2887 * Add <localedir> to the path. 2888 */ 2889 2890 static char * 2891 addlocale(char *path) 2892 { 2893 2894 char *tmp; 2895 2896 tmp = malloc(strlen(path) + strlen(localedir) + 2); 2897 if (tmp == NULL) 2898 malloc_error(); 2899 (void) sprintf(tmp, "%s/%s", path, localedir); 2900 return (tmp); 2901 2902 } 2903 2904 /* 2905 * From the configuration file "man.cf", get the order of suffices of 2906 * sub-mandirs to be used in the search path for a given mandir. 2907 */ 2908 2909 static char * 2910 check_config(char *path) 2911 { 2912 FILE *fp; 2913 static char submandir[BUFSIZ]; 2914 char *sect; 2915 char fname[MAXPATHLEN]; 2916 2917 (void) sprintf(fname, "%s/%s", path, CONFIG); 2918 2919 if ((fp = fopen(fname, "r")) == NULL) 2920 return (NULL); 2921 else { 2922 if (get_manconfig(fp, submandir) == -1) { 2923 (void) fclose(fp); 2924 return (NULL); 2925 } 2926 2927 (void) fclose(fp); 2928 2929 sect = strchr(submandir, '='); 2930 if (sect != NULL) 2931 return (++sect); 2932 else 2933 return (NULL); 2934 } 2935 } 2936 2937 /* 2938 * This routine is for getting the MANSECTS entry from man.cf. 2939 * It sets submandir to the line in man.cf that contains 2940 * MANSECTS=sections[,sections]... 2941 */ 2942 2943 static int 2944 get_manconfig(FILE *fp, char *submandir) 2945 { 2946 char *s, *t, *rc; 2947 char buf[BUFSIZ]; 2948 2949 while ((rc = fgets(buf, sizeof (buf), fp)) != NULL) { 2950 2951 /* 2952 * skip leading blanks 2953 */ 2954 for (t = buf; *t != '\0'; t++) { 2955 if (!isspace(*t)) 2956 break; 2957 } 2958 /* 2959 * skip line that starts with '#' or empty line 2960 */ 2961 if (*t == '#' || *t == '\0') 2962 continue; 2963 2964 if (strstr(buf, "MANSECTS") != NULL) 2965 break; 2966 } 2967 2968 /* 2969 * the man.cf file doesn't have a MANSECTS entry 2970 */ 2971 if (rc == NULL) 2972 return (-1); 2973 2974 s = strchr(buf, '\n'); 2975 *s = '\0'; /* replace '\n' with '\0' */ 2976 2977 (void) strcpy(submandir, buf); 2978 return (0); 2979 } 2980 2981 static void 2982 malloc_error(void) 2983 { 2984 (void) fprintf(stderr, gettext( 2985 "Memory allocation failed.\n")); 2986 exit(1); 2987 } 2988 2989 static int 2990 sgmlcheck(const char *s1) 2991 { 2992 const char *s2 = SGML_SYMBOL; 2993 int len; 2994 2995 while (*s1) { 2996 /* 2997 * Assume the first character of SGML_SYMBOL(*s2) is '<'. 2998 * Therefore, not necessary to do toupper(*s1) here. 2999 */ 3000 if (*s1 == *s2) { 3001 /* 3002 * *s1 is '<'. Check the following substring matches 3003 * with "!DOCTYPE". 3004 */ 3005 s1++; 3006 if (strncasecmp(s1, s2 + 1, SGML_SYMBOL_LEN - 1) 3007 == 0) { 3008 /* 3009 * SGML_SYMBOL found 3010 */ 3011 return (1); 3012 } 3013 continue; 3014 } else if (isascii(*s1)) { 3015 /* 3016 * *s1 is an ASCII char 3017 * Skip one character 3018 */ 3019 s1++; 3020 continue; 3021 } else { 3022 /* 3023 * *s1 is a non-ASCII char or 3024 * the first byte of the multibyte char. 3025 * Skip one character 3026 */ 3027 len = mblen(s1, MB_CUR_MAX); 3028 if (len == -1) 3029 len = 1; 3030 s1 += len; 3031 continue; 3032 } 3033 } 3034 /* 3035 * SGML_SYMBOL not found 3036 */ 3037 return (0); 3038 } 3039 3040 /* 3041 * Initializes the bintoman array with appropriate device and inode info 3042 */ 3043 3044 static void 3045 init_bintoman(void) 3046 { 3047 int i; 3048 struct stat sb; 3049 3050 for (i = 0; bintoman[i].bindir != NULL; i++) { 3051 if (stat(bintoman[i].bindir, &sb) == 0) { 3052 bintoman[i].dev = sb.st_dev; 3053 bintoman[i].ino = sb.st_ino; 3054 } else { 3055 bintoman[i].dev = NODEV; 3056 } 3057 } 3058 } 3059 3060 /* 3061 * If a duplicate is found, return 1 3062 * If a duplicate is not found, add it to the dupnode list and return 0 3063 */ 3064 static int 3065 dupcheck(struct man_node *mnp, struct dupnode **dnp) 3066 { 3067 struct dupnode *curdnp; 3068 struct secnode *cursnp; 3069 struct stat sb; 3070 int i; 3071 int rv = 1; 3072 int dupfound; 3073 3074 /* 3075 * If the path doesn't exist, treat it as a duplicate 3076 */ 3077 if (stat(mnp->path, &sb) != 0) { 3078 return (1); 3079 } 3080 3081 /* 3082 * If no sections were found in the man dir, treat it as duplicate 3083 */ 3084 if (mnp->secv == NULL) { 3085 return (1); 3086 } 3087 3088 /* 3089 * Find the dupnode structure for the previous time this directory 3090 * was looked at. Device and inode numbers are compared so that 3091 * directories that are reached via different paths (e.g. /usr/man vs. 3092 * /usr/share/man) are treated as equivalent. 3093 */ 3094 for (curdnp = *dnp; curdnp != NULL; curdnp = curdnp->next) { 3095 if (curdnp->dev == sb.st_dev && curdnp->ino == sb.st_ino) { 3096 break; 3097 } 3098 } 3099 3100 /* 3101 * First time this directory has been seen. Add a new node to the 3102 * head of the list. Since all entries are guaranteed to be unique 3103 * copy all sections to new node. 3104 */ 3105 if (curdnp == NULL) { 3106 if ((curdnp = calloc(1, sizeof (struct dupnode))) == NULL) { 3107 malloc_error(); 3108 } 3109 for (i = 0; mnp->secv[i] != NULL; i++) { 3110 if ((cursnp = calloc(1, sizeof (struct secnode))) 3111 == NULL) { 3112 malloc_error(); 3113 } 3114 cursnp->next = curdnp->secl; 3115 curdnp->secl = cursnp; 3116 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) { 3117 malloc_error(); 3118 } 3119 } 3120 curdnp->dev = sb.st_dev; 3121 curdnp->ino = sb.st_ino; 3122 curdnp->next = *dnp; 3123 *dnp = curdnp; 3124 return (0); 3125 } 3126 3127 /* 3128 * Traverse the section vector in the man_node and the section list 3129 * in dupnode cache to eliminate all duplicates from man_node 3130 */ 3131 for (i = 0; mnp->secv[i] != NULL; i++) { 3132 dupfound = 0; 3133 for (cursnp = curdnp->secl; cursnp != NULL; 3134 cursnp = cursnp->next) { 3135 if (strcmp(mnp->secv[i], cursnp->secp) == 0) { 3136 dupfound = 1; 3137 break; 3138 } 3139 } 3140 if (dupfound) { 3141 mnp->secv[i][0] = '\0'; 3142 continue; 3143 } 3144 3145 3146 /* 3147 * Update curdnp and set return value to indicate that this 3148 * was not all duplicates. 3149 */ 3150 if ((cursnp = calloc(1, sizeof (struct secnode))) == NULL) { 3151 malloc_error(); 3152 } 3153 cursnp->next = curdnp->secl; 3154 curdnp->secl = cursnp; 3155 if ((cursnp->secp = strdup(mnp->secv[i])) == NULL) { 3156 malloc_error(); 3157 } 3158 rv = 0; 3159 } 3160 3161 return (rv); 3162 } 3163 3164 /* 3165 * Given a bin directory, return the corresponding man directory. 3166 * Return string must be free()d by the caller. 3167 * 3168 * NULL will be returned if no matching man directory can be found. 3169 */ 3170 3171 static char * 3172 path_to_manpath(char *bindir) 3173 { 3174 char *mand, *p; 3175 int i; 3176 struct stat sb; 3177 3178 /* 3179 * First look for known translations for specific bin paths 3180 */ 3181 if (stat(bindir, &sb) != 0) { 3182 return (NULL); 3183 } 3184 for (i = 0; bintoman[i].bindir != NULL; i++) { 3185 if (sb.st_dev == bintoman[i].dev && 3186 sb.st_ino == bintoman[i].ino) { 3187 if ((mand = strdup(bintoman[i].mandir)) == NULL) { 3188 malloc_error(); 3189 } 3190 if ((p = strchr(mand, ',')) != NULL) { 3191 *p = '\0'; 3192 } 3193 if (stat(mand, &sb) != 0) { 3194 free(mand); 3195 return (NULL); 3196 } 3197 if (p != NULL) { 3198 *p = ','; 3199 } 3200 return (mand); 3201 } 3202 } 3203 3204 /* 3205 * No specific translation found. Try `dirname $bindir`/man 3206 * and `dirname $bindir`/share/man 3207 */ 3208 if ((mand = malloc(PATH_MAX)) == NULL) { 3209 malloc_error(); 3210 } 3211 3212 if (strlcpy(mand, bindir, PATH_MAX) >= PATH_MAX) { 3213 free(mand); 3214 return (NULL); 3215 } 3216 3217 /* 3218 * Advance to end of buffer, strip trailing /'s then remove last 3219 * directory component. 3220 */ 3221 for (p = mand; *p != '\0'; p++) 3222 ; 3223 for (; p > mand && *p == '/'; p--) 3224 ; 3225 for (; p > mand && *p != '/'; p--) 3226 ; 3227 if (p == mand && *p == '.') { 3228 if (realpath("..", mand) == NULL) { 3229 free(mand); 3230 return (NULL); 3231 } 3232 for (; *p != '\0'; p++) 3233 ; 3234 } else { 3235 *p = '\0'; 3236 } 3237 3238 if (strlcat(mand, "/man", PATH_MAX) >= PATH_MAX) { 3239 free(mand); 3240 return (NULL); 3241 } 3242 3243 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) { 3244 return (mand); 3245 } 3246 3247 /* 3248 * Strip the /man off and try /share/man 3249 */ 3250 *p = '\0'; 3251 if (strlcat(mand, "/share/man", PATH_MAX) >= PATH_MAX) { 3252 free(mand); 3253 return (NULL); 3254 } 3255 if ((stat(mand, &sb) == 0) && S_ISDIR(sb.st_mode)) { 3256 return (mand); 3257 } 3258 3259 /* 3260 * No man or share/man directory found 3261 */ 3262 free(mand); 3263 return (NULL); 3264 } 3265 3266 /* 3267 * Free a linked list of dupnode structs 3268 */ 3269 void 3270 free_dupnode(struct dupnode *dnp) { 3271 struct dupnode *dnp2; 3272 struct secnode *snp; 3273 3274 while (dnp != NULL) { 3275 dnp2 = dnp; 3276 dnp = dnp->next; 3277 while (dnp2->secl != NULL) { 3278 snp = dnp2->secl; 3279 dnp2->secl = dnp2->secl->next; 3280 free(snp->secp); 3281 free(snp); 3282 } 3283 free(dnp2); 3284 } 3285 } 3286 3287 /* 3288 * prints manp linked list to stdout. 3289 * 3290 * If namep is NULL, output can be used for setting MANPATH. 3291 * 3292 * If namep is not NULL output is two columns. First column is the string 3293 * pointed to by namep. Second column is a MANPATH-compatible representation 3294 * of manp linked list. 3295 */ 3296 void 3297 print_manpath(struct man_node *manp, char *namep) 3298 { 3299 char colon[2]; 3300 char **secp; 3301 3302 if (namep != NULL) { 3303 (void) printf("%s ", namep); 3304 } 3305 3306 colon[0] = '\0'; 3307 colon[1] = '\0'; 3308 3309 for (; manp != NULL; manp = manp->next) { 3310 (void) printf("%s%s", colon, manp->path); 3311 colon[0] = ':'; 3312 3313 /* 3314 * If man.cf or a directory scan was used to create section 3315 * list, do not print section list again. If the output of 3316 * man -p is used to set MANPATH, subsequent runs of man 3317 * will re-read man.cf and/or scan man directories as 3318 * required. 3319 */ 3320 if (manp->defsrch != 0) { 3321 continue; 3322 } 3323 3324 for (secp = manp->secv; *secp != NULL; secp++) { 3325 /* 3326 * Section deduplication may have eliminated some 3327 * sections from the vector. Avoid displaying this 3328 * detail which would appear as ",," in output 3329 */ 3330 if ((*secp)[0] != '\0') { 3331 (void) printf(",%s", *secp); 3332 } 3333 } 3334 } 3335 (void) printf("\n"); 3336 }