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